diff --git a/frontend-js/src/main/js/GuiConnector.js b/frontend-js/src/main/js/GuiConnector.js index 5596e0d97ba7a114b7c2ab3178e54d71eb10e3b9..87070fd26cebaef9099722450ab23ae2554bffc8 100644 --- a/frontend-js/src/main/js/GuiConnector.js +++ b/frontend-js/src/main/js/GuiConnector.js @@ -49,6 +49,12 @@ GuiConnector.getCustomMap = function() { }; GuiConnector.init = function() { + // bootstrap tab initialization + $("ul.nav-tabs a").click(function(e) { + e.preventDefault(); + $(this).tab('show'); + }); + // find GuiConnector.getParams document.location.search.replace(/\??(?:([^=]+)=([^&]*)&?)/g, function() { function decode(s) { @@ -67,11 +73,6 @@ GuiConnector.init = function() { GuiConnector.leftPanelTabNavi = new TabNavi("tabView", { top : "17px" }); - GuiConnector.searchTabNavi = new TabNavi("tabView:mainForm:dTable", { - hideRemaining : false, - tabSize : 1, - top : "5px" - }); GuiConnector.drugTabNavi = new TabNavi("tabView:drugForm:drugResults", { hideRemaining : false, tabSize : 1, diff --git a/frontend-js/src/main/js/ObjectWithListeners.js b/frontend-js/src/main/js/ObjectWithListeners.js index 2b326294519fec3be0c04cb3889aab30b3c77d18..11c7eaf4bce4cda627f874042a2e2507a615bce6 100644 --- a/frontend-js/src/main/js/ObjectWithListeners.js +++ b/frontend-js/src/main/js/ObjectWithListeners.js @@ -143,15 +143,17 @@ ObjectWithListeners.prototype.callListeners = function(type) { throw new Error("Unknown listener type: " + type); } var listenerList = this._validListeners[type]; + var promises = []; if (listenerList.length > 0) { for ( var i in listenerList) { var e = { type : type, object : this, }; - listenerList[i](e); + promises.push(listenerList[i](e)); } } + return Promise.all(promises); }; /** diff --git a/frontend-js/src/main/js/ServerConnector.js b/frontend-js/src/main/js/ServerConnector.js index 1de30ae567a450ccb17a107e40b9a214e3950b58..8b1dc799ef11312a5bf6dff0947efdd916d9dcfe 100644 --- a/frontend-js/src/main/js/ServerConnector.js +++ b/frontend-js/src/main/js/ServerConnector.js @@ -906,7 +906,7 @@ ServerConnector.getReactions = function(reactionIds, projectId, columns) { ServerConnector.getAliases = function(aliasIds, projectId, columns) { var self = this; return new Promise(function(resolve, reject) { - self.getProjectId(projectId).then(function(result) { + return self.getProjectId(projectId).then(function(result) { projectId = result; return self.getToken(); }).then(function(token) { @@ -918,9 +918,7 @@ ServerConnector.getAliases = function(aliasIds, projectId, columns) { result.push(new Alias(array[i])); } resolve(result); - }).catch(function(exception){ - reject(exception); - }); + }).catch(reject); }); }; diff --git a/frontend-js/src/main/js/gui/SearchPanel.js b/frontend-js/src/main/js/gui/SearchPanel.js new file mode 100644 index 0000000000000000000000000000000000000000..f46d2ff97c50130feb31c8e0834c40fc940e6edf --- /dev/null +++ b/frontend-js/src/main/js/gui/SearchPanel.js @@ -0,0 +1,355 @@ +"use strict"; + +var Promise = require("bluebird"); + +var Alias = require('../map/data/Alias'); +var Reaction = require('../map/data/Reaction'); +var logger = require('../logger'); +var Functions = require('../Functions'); + +function SearchPanel(params) { + var self = this; + + this.setElement(params.element); + this.setMap(params.customMap); + + var searchDb = self.getMap().getOverlayByName('search'); + if (searchDb === undefined) { + throw new Error("Cannot find search db overlay"); + } + searchDb.addListener("onSearch", function() { + return self.refreshSearchResults(); + }); + + this._tabIdCount = 0; +} + +SearchPanel.prototype.setMap = function(map) { + this._map = map; +}; + +SearchPanel.prototype.getMap = function() { + return this._map; +}; + +SearchPanel.prototype.setElement = function(element) { + if (element === undefined) { + throw new Error("DOM Element must be defined"); + } + this._element = element; + + if (this.getNavElement() === undefined) { + throw new Error("No nav-tabs div found in the search panel element"); + } + if (this.getContentElement() === undefined) { + throw new Error("No tab-content div found in the search panel element"); + } +}; + +SearchPanel.prototype.getSearchQueryElement = function() { + var children = this.getElement().children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.getAttribute("name") === "searchQuery") { + return child; + } + } +}; + +SearchPanel.prototype.getSearchResultsElement = function() { + var children = this.getElement().children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.getAttribute("name") === "searchResults") { + return child; + } + } +}; + +SearchPanel.prototype.getNavElement = function() { + var searchResultsElement = this.getSearchResultsElement(); + + if (searchResultsElement) { + var children = searchResultsElement.children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.className.indexOf("nav-tabs") !== -1) { + return child; + } + } + } +}; + +SearchPanel.prototype.getContentElement = function() { + var searchResultsElement = this.getSearchResultsElement(); + + if (searchResultsElement) { + var children = searchResultsElement.children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.className.indexOf("tab-content") !== -1) { + return child; + } + } + } +}; + +SearchPanel.prototype.getElement = function() { + return this._element; +}; + +SearchPanel.prototype.clearResults = function() { + var navElement = this.getNavElement(); + while (navElement.firstChild) { + navElement.removeChild(navElement.firstChild); + } + + var contentElement = this.getContentElement(); + while (contentElement.firstChild) { + contentElement.removeChild(contentElement.firstChild); + } +}; + +SearchPanel.prototype.refreshSearchResults = function() { + var self = this; + self.clearResults(); + var searchDb = self.getMap().getOverlayByName('search'); + var queries = searchDb.getQueries(); + + var promises = []; + for (var i = 0; i < queries.length; i++) { + promises.push(searchDb.getElementsByQuery(queries[i])); + } + return Promise.all(promises).then(function(results) { + for (var i = 0; i < queries.length; i++) { + self.addResultTab(queries[i], results[i]); + } + }); +}; + +SearchPanel.prototype.addResultTab = function(query, elements) { + var tabId = "searchTab_" + this._tabIdCount; + this._tabIdCount++; + + var navElement = this.getNavElement(); + var contentElement = this.getContentElement(); + var navClass = ''; + var contentClass = 'tab-pane'; + if (navElement.children.length === 0) { + navClass = "active"; + contentClass = "tab-pane active"; + } + + var navLi = document.createElement("li"); + navLi.className = navClass; + var navLink = document.createElement("a"); + navLink.href = "#" + tabId; + navLi.appendChild(navLink); + if (query.name !== undefined) { + navLink.innerHTML = query.name; + } + navElement.appendChild(navLi); + + var contentDiv = document.createElement("div"); + contentDiv.className = contentClass; + contentDiv.id = tabId; + + contentElement.appendChild(contentDiv); + + var tableDiv = document.createElement("table"); + tableDiv.className = "result-table"; + contentDiv.appendChild(tableDiv); + + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + if (element instanceof Alias) { + tableDiv.appendChild(this.createAliasElement(element)); + } else if (element instanceof Reaction) { + tableDiv.appendChild(this.createReactionElement(element)); + } else { + throw new Error("Unknown element type: " + element.constructor.name); + } + } +}; + +SearchPanel.prototype.createReactionElement = function(reaction) { + var self = this; + var result = document.createElement("tr"); + var td = document.createElement("td"); + result.appendChild(td); + var div = document.createElement("div"); + td.appendChild(div); + + div.appendChild(createLabel("Reaction: " + reaction.getReactionId())); + var lineBreak = document.createElement("hr"); + div.appendChild(lineBreak); + + if (reaction.getModelId() != self.getMap().getId()) { + div.appendChild(createSubMapLink(reaction)); + } + div.appendChild(createNewLine(3)); + + div.appendChild(createParamLine("Symbol: ", reaction.getSymbol())); + div.appendChild(createParamLine("Abbreviation: ", reaction.getAbbreviation())); + div.appendChild(createParamLine("Formula: ", reaction.getFormula())); + div.appendChild(createParamLine("Mechanical Confidence Score: ", reaction.getMechanicalConfidenceScore())); + div.appendChild(createParamLine("Lower Bound: ", reaction.getLowerBound())); + div.appendChild(createParamLine("Upper Bound: ", reaction.getUpperBound())); + div.appendChild(createParamLine("Gene Protein Reaction: ", reaction.getGeneProteinReaction())); + div.appendChild(createParamLine("Subsystem: ", reaction.getSubsystem())); + div.appendChild(createArrayParamLine("Synonyms: ", reaction.getSynonyms())); + div.appendChild(createParamLine("Description: ", reaction.getDescription())); + div.appendChild(createReactantsLine(reaction.getReactants())); + div.appendChild(createProductsLine(reaction.getProducts())); + div.appendChild(createModifiersLine(reaction.getModifiers())); + div.appendChild(createCandidates("Candidates: ", reaction.getOther('dataMining'))); + div.appendChild(createAnnotations("Annotations: ", reaction.getReferences())); + + return result; +}; + +function createLabel(value) { + var result = document.createElement("span"); + result.innerHTML = value; + result.className = "searchDescriptionLabel"; + return result; +}; + +function createLabelText(value) { + var result = document.createElement("span"); + result.innerHTML = value; + return result; +}; + +function createSeparator() { + var result = document.createElement("hr"); + return result; +}; + +function createNewLine(count) { + var result = document.createElement("p"); + result.style.width = "10px"; + if (count > 0) { + result.style.width = (count * 10) + "px"; + } + return result; +}; + +function createParamLine(label, value) { + var result = document.createElement("div"); + if (value !== undefined) { + result.appendChild(createLabel(label)); + result.appendChild(createLabelText(value)); + result.appendChild(createNewLine()); + } + return result; +}; + +function createArrayParamLine(label, value) { + var result = document.createElement("div"); + if (value !== undefined && value.length > 0) { + result.appendChild(createLabel(label)); + result.appendChild(createLabelText(value.join(","))); + result.appendChild(createNewLine()); + } + return result; +}; + +function createReactantsLine(label, value) { + var result = document.createElement("div"); + if (value !== undefined && value.length > 0) { + for (var i = 0; i < value.length; i++) { + result.appendChild(createParamLine("Reactant: ", value[i])); + } + } + return result; +}; + +function createProductsLine(label, value) { + var result = document.createElement("div"); + if (value !== undefined && value.length > 0) { + for (var i = 0; i < value.length; i++) { + result.appendChild(createParamLine("Product: ", value[i])); + } + } + return result; +}; + +function createModifiersLine(label, value) { + var result = document.createElement("div"); + if (value !== undefined && value.length > 0) { + for (var i = 0; i < value.length; i++) { + result.appendChild(createParamLine("Modifier: ", value[i])); + } + } + return result; +}; + +function createPostTranslationalModifications(label, value) { + var result = document.createElement("div"); + if (value !== undefined) { + throw new Error("Not implemented"); + } + return result; +}; + +function createCandidates(label, value) { + var result = document.createElement("div"); + if (value !== undefined) { + throw new Error("Not implemented"); + } + return result; +}; + +function createChebiTree(label, value) { + var result = document.createElement("div"); + if (value !== undefined) { + throw new Error("Not implemented"); + } + return result; +}; + +function createAnnotations(label, value) { + var result = document.createElement("div"); + if (value !== undefined && value.length > 0) { + throw new Error("Not implemented"); + } + return result; +}; + +SearchPanel.prototype.createAliasElement = function(alias) { + var self = this; + + var result = document.createElement("tr"); + var td = document.createElement("td"); + result.appendChild(td); + var div = document.createElement("div"); + td.appendChild(div); + + div.appendChild(createParamLine(alias.getType() + ": ", alias.getName())); + + if (alias.getModelId() != self.getMap().getId()) { + div.appendChild(createSubMapLink(alias)); + } + div.appendChild(createNewLine(3)); + + div.appendChild(createParamLine("Full name: ", alias.getFullName())); + div.appendChild(createParamLine("Symbol: ", alias.getSymbol())); + div.appendChild(createParamLine("Abbreviation: ", alias.getAbbreviation())); + div.appendChild(createParamLine("Formula: ", alias.getFormula())); + div.appendChild(createArrayParamLine("Former symbols: ", alias.getFormerSymbols())); + div.appendChild(createPostTranslationalModifications("Posttranslational modifications: ", alias + .getOther('posttranslationalModifications'))); + div.appendChild(createParamLine("Charge: ", alias.getCharge())); + div.appendChild(createParamLine("Synonyms: ", alias.getSynonyms())); + div.appendChild(createLabelText(alias.getDescription())); + div.appendChild(createCandidates("Candidates: ", alias.getOther('dataMining'))); + div.appendChild(createChebiTree("Chebi ontology: ", alias.getOther('chebiTree'))); + div.appendChild(createAnnotations("Annotations: ", alias.getReferences())); + + div.appendChild(createSeparator()); + + return result; +}; + +module.exports = SearchPanel; diff --git a/frontend-js/src/main/js/map/data/Alias.js b/frontend-js/src/main/js/map/data/Alias.js index 7b426ecc9bb3a51b205af31c8b770dd76dcff05c..595f683ddd23866ede0a3433aea2c6e2c453c81e 100644 --- a/frontend-js/src/main/js/map/data/Alias.js +++ b/frontend-js/src/main/js/map/data/Alias.js @@ -42,17 +42,18 @@ Alias.prototype.update = function(javaObject) { if (javaObject.name === undefined) { return; } - this.description = javaObject.notes; - this.type = javaObject.type; - this.symbol = javaObject.symbol; - this.fullName = javaObject.fullName; - this.abbreviation = javaObject.abbreviation; - this.formula = javaObject.formula; - this.name = javaObject.name; - this.synonyms = javaObject.synonyms; - this.formerSymbols = javaObject.formerSymbols; - this.references = javaObject.references; - this.other = javaObject.other; + this.setDescription(javaObject.notes); + this.setType(javaObject.type); + this.setCharge(javaObject.charge); + this.setSymbol(javaObject.symbol); + this.setFullName(javaObject.fullName); + this.setAbbreviation(javaObject.abbreviation); + this.setFormula(javaObject.formula); + this.setName(javaObject.name); + this.setSynonyms(javaObject.synonyms); + this.setFormerSymbols(javaObject.formerSymbols); + this.setReferences(javaObject.references); + this.setOther(javaObject.other); this.setIsComplete(true); }; @@ -69,6 +70,48 @@ Alias.prototype.setId = function(id) { this.id = id; }; +Alias.prototype.getFormula = function() { + return this.formula; +}; + +Alias.prototype.setFormula = function(formula) { + this.formula = formula; +}; + +Alias.prototype.getDescription = function() { + return this.description; +}; + +Alias.prototype.setDescription = function(description) { + this.description = description; +}; + +Alias.prototype.getCharge = function() { + return this.charge; +}; + +Alias.prototype.setCharge = function(charge) { + this.charge = charge; +}; + +Alias.prototype.getFormerSymbols = function() { + return this.formerSymbols; +}; + +Alias.prototype.setFormerSymbols = function(formerSymbols) { + this.formerSymbols = formerSymbols; +}; + +Alias.prototype.getOther = function(type) { + if (this.other !== undefined) { + return this.other[type]; + } +}; + +Alias.prototype.setOther = function(other) { + this.other = other; +}; + /** * Returns model identifier where {@link Alias} is located. * @@ -110,10 +153,50 @@ Alias.prototype.getName = function() { return this.name; }; +Alias.prototype.setName = function(name) { + this.name = name; +}; + +Alias.prototype.getSynonyms = function() { + return this.synonyms; +}; + +Alias.prototype.setSynonyms = function(synonyms) { + this.synonyms = synonyms; +}; + +Alias.prototype.getReferences = function() { + return this.references; +}; + +Alias.prototype.setReferences = function(references) { + this.references = references; +}; + Alias.prototype.getFullName = function() { return this.fullName; }; +Alias.prototype.setFullName = function(fullName) { + this.fullName = fullName; +}; + +Alias.prototype.getSymbol = function() { + return this.symbol; +}; + +Alias.prototype.setSymbol = function(symbol) { + this.symbol = symbol; +}; + +Alias.prototype.getAbbreviation = function() { + return this.abbreviation; +}; + +Alias.prototype.setAbbreviation = function(abbreviation) { + this.abbreviation = abbreviation; +}; + Alias.prototype.setType = function(type) { this.type = type; }; diff --git a/frontend-js/src/main/js/map/data/MapModel.js b/frontend-js/src/main/js/map/data/MapModel.js index c7370e29731556cb820e9eabfe38dca853eef454..5e1b1862397fd24fcc39d0dd4bdab93022547656 100644 --- a/frontend-js/src/main/js/map/data/MapModel.js +++ b/frontend-js/src/main/js/map/data/MapModel.js @@ -176,12 +176,12 @@ MapModel.prototype.getReactionById = function(id, complete) { MapModel.prototype.getCompleteReactionById = function(id) { var self = this; - return new Promise(function(resolve) { + return new Promise(function(resolve, reject) { if (self._reactions[id] instanceof Reaction && self._reactions[id].isComplete()) { resolve(self._reactions[id]); } else { var result; - ServerConnector.getReactions([id]).then(function(reactions){ + return ServerConnector.getReactions([id]).then(function(reactions){ if (self._reactions[id] === undefined) { self._reactions[id] = reactions[0]; } else { @@ -231,7 +231,7 @@ MapModel.prototype.getCompleteReactionById = function(id) { } } resolve(result); - }); + }).catch(reject); } }); }; @@ -601,4 +601,14 @@ MapModel.prototype._getLayouts = function() { return result; }; +MapModel.prototype.getByIdentifiedElement = function(ie, complete) { + if (ie.getType()==="ALIAS") { + return this.getAliasById(ie.getId(), complete); + } else if (ie.getType()==="REACTION") { + return this.getReactionById(ie.getId()); + } else { + throw new Error("Unknown type: "+ie.getType(), complete); + } +}; + module.exports = MapModel; diff --git a/frontend-js/src/main/js/map/data/Reaction.js b/frontend-js/src/main/js/map/data/Reaction.js index 395353c8195aec8a1a3d3b27ae97b9d89dc6a2c3..66a318fe0a32ebf25bf61d115148e81e16daf4ad 100644 --- a/frontend-js/src/main/js/map/data/Reaction.js +++ b/frontend-js/src/main/js/map/data/Reaction.js @@ -94,7 +94,19 @@ Reaction.prototype.update = function(javaObject) { return; } this.setReactionId(javaObject.reactionId); - + this.setSymbol(javaObject.symbol); + this.setAbbreviation(javaObject.abbreviation); + this.setFormula(javaObject.formula); + this.setMechanicalConfidenceScore(javaObject.mechanicalConfidenceScore); + this.setLowerBound(javaObject.lowerBound); + this.setUpperBound(javaObject.upperBound); + this.setGeneProteinReaction(javaObject.geneProteinReaction); + this.setSubsystem(javaObject.subsystem); + this.setSynonyms(javaObject.synonyms); + this.setDescription(javaObject.notes); + this.setOther(javaObject.other); + this.setReferences(javaObject.references); + if (javaObject.reactants !== "") { this.setReactants(javaObject.reactants.split(",")); } else { @@ -128,6 +140,86 @@ Reaction.prototype.setReactionId = function(reactionId) { this._reactionId = reactionId; }; +Reaction.prototype.getSymbol = function() { + return this._symbol; +}; + +Reaction.prototype.setSymbol = function(symbol) { + this._symbol = symbol; +}; + +Reaction.prototype.getAbbreviation= function() { + return this._abbreviation; +}; + +Reaction.prototype.setAbbreviation= function(abbreviation) { + this._abbreviation = abbreviation; +}; + +Reaction.prototype.getFormula= function() { + return this._formula; +}; + +Reaction.prototype.setFormula = function(formula) { + this._formula = formula; +}; + +Reaction.prototype.getMechanicalConfidenceScore= function() { + return this._mechanicalConfidenceScore; +}; + +Reaction.prototype.setMechanicalConfidenceScore = function(mechanicalConfidenceScore) { + this._mechanicalConfidenceScore = mechanicalConfidenceScore; +}; + +Reaction.prototype.getLowerBound= function() { + return this._lowerBound; +}; + +Reaction.prototype.setLowerBound = function(lowerBound) { + this._lowerBound = lowerBound; +}; + +Reaction.prototype.getUpperBound= function() { + return this._upperBound; +}; + +Reaction.prototype.setUpperBound = function(upperBound) { + this._upperBound = upperBound; +}; + +Reaction.prototype.setGeneProteinReaction = function(geneProteinReaction) { + this._geneProteinReaction = geneProteinReaction; +}; + +Reaction.prototype.getGeneProteinReaction= function() { + return this._geneProteinReaction; +}; + +Reaction.prototype.setSubsystem = function(subsystem) { + this._subsystem = subsystem; +}; + +Reaction.prototype.getSubsystem= function() { + return this._subsystem; +}; + +Reaction.prototype.setSynonyms = function(synonyms) { + this._synonyms = synonyms; +}; + +Reaction.prototype.getSynonyms= function() { + return this._synonyms; +}; + +Reaction.prototype.setDescription = function(description) { + this._description = description; +}; + +Reaction.prototype.getDescription= function() { + return this._description; +}; + Reaction.prototype.getReactants = function() { return this._reactants; }; @@ -160,4 +252,22 @@ Reaction.prototype.getModifiers = function() { return this._modifiers; }; +Reaction.prototype.getOther = function(type) { + if (this._other !== undefined) { + return this._other[type]; + } +}; + +Reaction.prototype.setOther = function(other) { + this._other = other; +}; + +Reaction.prototype.getReferences = function() { + return this.references; +}; + +Reaction.prototype.setReferences = function(references) { + this.references = references; +}; + module.exports = Reaction; diff --git a/frontend-js/src/main/js/map/overlay/OverlayCollection.js b/frontend-js/src/main/js/map/overlay/OverlayCollection.js index 0679d6731848bf35bceb7d69a7edc2864d4ccb99..91961f1123913f9cc7d27a75da4019b4d12123de 100644 --- a/frontend-js/src/main/js/map/overlay/OverlayCollection.js +++ b/frontend-js/src/main/js/map/overlay/OverlayCollection.js @@ -2,7 +2,9 @@ var logger = require('../../logger'); + var IdentifiedElement = require('../data/IdentifiedElement'); +var ObjectWithListeners = require('../../ObjectWithListeners'); /** * This class is responsible for collecting and updating markers found by @@ -19,6 +21,8 @@ function OverlayCollection(params) { // map, name, allowSearchById, allowGeneralSearch var self = this; + ObjectWithListeners.call(this); + if (params.map === undefined) { throw new Error("map param must be defined"); } @@ -38,6 +42,9 @@ function OverlayCollection(params) { this.getMap().registerSource(self); } +OverlayCollection.prototype = Object.create(ObjectWithListeners.prototype); +OverlayCollection.prototype.constructor = OverlayCollection; + /** * Returns true if overlay allows to get general data for element. */ diff --git a/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js b/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js index be7537e82691c7fe266dba0fd12823abe2526df3..d74644d48b3cc3e0c3296c65e936b856b97a149c 100644 --- a/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js +++ b/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js @@ -15,6 +15,7 @@ function SearchDbOverlay(params) { this.setIconType("marker"); this.setIconStart(0); this._elementsByQuery = []; + this.registerListenerType('onSearch'); } SearchDbOverlay.prototype = Object.create(OverlayCollection.prototype); @@ -33,6 +34,17 @@ function encodeQuery(type, model, arg){ } } +SearchDbOverlay.prototype.getElementsByQuery = function(query) { + var self = this; + var elements = this._elementsByQuery[query]; + var promises = []; + for (var i=0;i<elements.length;i++) { + var model = self.getMap().getSubmodelById(elements[0].getModelId()).getModel(); + promises.push(model.getByIdentifiedElement(elements[i], true)); + } + return Promise.all(promises); +}; + SearchDbOverlay.prototype.searchByCoordinates = function(model, coordinates) { var self = this; return new Promise(function(resolve, reject) { @@ -40,7 +52,9 @@ SearchDbOverlay.prototype.searchByCoordinates = function(model, coordinates) { self.setQueries([query]); if (self._elementsByQuery[query] !== undefined) { - resolve(self._elementsByQuery[query]); + return self.callListeners('onSearch').then(function(){ + resolve(self._elementsByQuery[query]); + }); } else { return ServerConnector.getClosestElementsByCoordinates({ modelId:model.getId(), coordinates: coordinates, count: 1 @@ -54,11 +68,14 @@ SearchDbOverlay.prototype.searchByCoordinates = function(model, coordinates) { for (i=0;i<reactionElements.length;i++) { self._elementsByQuery[query].push(new IdentifiedElement(reactionElements[i])); } - resolve(self._elementsByQuery[query]); - }).catch(reject); + }); } else { - resolve(self._elementsByQuery[query]); + return null; } + }).then(function(){ + return self.callListeners('onSearch'); + }).then(function(){ + resolve(self._elementsByQuery[query]); }).catch(reject); } }); diff --git a/frontend-js/src/main/js/minerva.js b/frontend-js/src/main/js/minerva.js index daf8c324d7ead1b1125344f751a08c11249dc0e0..e8b3f09083fca84b4227ed433d464cc517096183 100644 --- a/frontend-js/src/main/js/minerva.js +++ b/frontend-js/src/main/js/minerva.js @@ -6,6 +6,7 @@ var CommentDbOverlay = require('./map/overlay/CommentDbOverlay'); var CustomMap = require('./map/CustomMap'); var OverlayCollection = require('./map/overlay/OverlayCollection'); var SearchDbOverlay = require('./map/overlay/SearchDbOverlay'); +var SearchPanel = require('./gui/SearchPanel'); var OriginalGuiConnector = require('./GuiConnector'); var OriginalServerConnector = require('./ServerConnector'); @@ -78,6 +79,12 @@ function create(params) { collection = new CommentDbOverlay(collectionParams); } else if (collectionParams.name === "search") { collection = new SearchDbOverlay(collectionParams); + + new SearchPanel({ + element : document.getElementById("searchTab"), + customMap : result + }); + } else { collection = new OverlayCollection(collectionParams); } diff --git a/frontend-js/src/test/js/gui/SearchPanel-test.js b/frontend-js/src/test/js/gui/SearchPanel-test.js new file mode 100644 index 0000000000000000000000000000000000000000..01299134c92c6dd43793cd241c689fcb802ecc52 --- /dev/null +++ b/frontend-js/src/test/js/gui/SearchPanel-test.js @@ -0,0 +1,52 @@ +"use strict"; + +var Helper = require('../helper'); + +require("../mocha-config.js"); + +var SearchPanel = require('../../../main/js/gui/SearchPanel'); + +var chai = require('chai'); +var assert = chai.assert; +var logger = require('../logger'); + +describe('SearchPanel', function() { + + + var helper; + before(function() { + helper = new Helper(); + }); + + it('contructor', function() { + var div = helper.createSearchTab(); + + var map = helper.createCustomMap(); + var searchDbOverlay = helper.createSearchDbOverlay(map); + + new SearchPanel({ + element : div, + customMap : map + }); + assert.equal(logger.getWarnings().length, 0); + }); + + it('on searchResults changed', function() { + var div = helper.createSearchTab(); + var map = helper.createCustomMap(); + map.getModel().setId(15781); + var searchDbOverlay = helper.createSearchDbOverlay(map); + + new SearchPanel({ + element : div, + customMap : map + }); + + return searchDbOverlay.searchByCoordinates(map.getModel(), new google.maps.Point(26547.33, 39419.29)).then( + function(results) { + assert.equal(logger.getWarnings().length, 0); + assert.ok(div.innerHTML.indexOf("Reaction") >= 0); + }); + }); + +}); diff --git a/frontend-js/src/test/js/helper.js b/frontend-js/src/test/js/helper.js index b7001fdb649f1efc8c68648f3bc41556c887e091..d5ff0f4ca182520a168347d18cd543cc7a7b7670 100644 --- a/frontend-js/src/test/js/helper.js +++ b/frontend-js/src/test/js/helper.js @@ -22,6 +22,31 @@ function Helper() { this.idCounter = 1000000; } +Helper.prototype.createSearchTab = function() { + var result = document.createElement("div"); + result.id = "searchTab"; + var searchQueryDiv = document.createElement("div"); + searchQueryDiv.setAttribute("name", "searchQuery"); + result.appendChild(searchQueryDiv); + + var searchResultsDiv = document.createElement("div"); + searchResultsDiv.setAttribute("name", "searchResults"); + result.appendChild(searchResultsDiv); + + var navDiv = document.createElement("ul"); + navDiv.className = "nav nav-tabs"; + navDiv.innerHTML = '<li class="active"><a href="#set1"/></li>'; + + var contentDiv = document.createElement("div"); + contentDiv.className = "tab-content"; + contentDiv.innerHTML = '<div class="tab-pane fade active in" id="set1"/>'; + + searchResultsDiv.appendChild(navDiv); + searchResultsDiv.appendChild(contentDiv); + + return result; +}; + Helper.prototype.createCommentDbOverlay = function(map) { var result = new CommentDbOverlay({ map : map, @@ -39,8 +64,6 @@ Helper.prototype.createSearchDbOverlay = function(map) { return result; }; - - Helper.prototype.createDrugDbOverlay = function(map) { var result = this.createDbOverlay(map); result.setName('drug'); diff --git a/frontend-js/src/test/js/minerva-test.js b/frontend-js/src/test/js/minerva-test.js index 7dc361355f18700a932a4c0c87d6b5b210d94436..9a9d865224b98f3d7c0b7a663167687bb97c1b45 100644 --- a/frontend-js/src/test/js/minerva-test.js +++ b/frontend-js/src/test/js/minerva-test.js @@ -17,6 +17,15 @@ describe('minerva global', function() { helper = new Helper(); }); + beforeEach(function() { + global.searchTab = helper.createSearchTab(); + document.body.appendChild(global.searchTab); + }); + + afterEach(function() { + document.body.removeChild(global.searchTab); + }); + it('create', function() { var options = helper.createOptions(); @@ -29,9 +38,11 @@ describe('minerva global', function() { it("contructor with GET zoom param", function() { var options = helper.createCustomMapOptions(); GuiConnector.getParams["zoom"] = "5"; - return minerva.create(options).then(function() { - assert.equal(ServerConnector.getSessionData(options.getProject()).getZoomLevel(options.getProject().getModel()), 5); - }); + return minerva.create(options).then( + function() { + assert.equal(ServerConnector.getSessionData(options.getProject()).getZoomLevel( + options.getProject().getModel()), 5); + }); }); it("contructor with GET coord param", function() { @@ -46,7 +57,6 @@ describe('minerva global', function() { }); }); - it('create with layout', function() { var project = helper.createProject(); @@ -82,7 +92,6 @@ describe('minerva global', function() { var project = helper.createProject(); var map = helper.createGoogleMap(); - var layout = helper.createLayout(); layout.setInputDataAvailable(true); // disable upload of the data from server @@ -126,7 +135,7 @@ describe('minerva global', function() { assert.ok(result); }); }); - + it('create with search overlay', function() { var project = helper.createProject(); project.getModel().setId(15781); diff --git a/web/src/main/webapp/WEB-INF/components/map/map.xhtml b/web/src/main/webapp/WEB-INF/components/map/map.xhtml index 5e386d8e3db0fb6432ab462fb28c38f15b1f131b..c5923d23abedf32f9ede94e41e5fab66b70c13c9 100644 --- a/web/src/main/webapp/WEB-INF/components/map/map.xhtml +++ b/web/src/main/webapp/WEB-INF/components/map/map.xhtml @@ -62,10 +62,6 @@ <h:inputHidden id="systemMaxColor" value="#{configurationMB.maxColor}"/> </h:form> - <h:form id="searchForm"> - <p:remoteCommand name="_searchByCoord" actionListener="#{searchMB.mapClicked}" update=":tabView:mainForm:dTable :tabView:mainForm:searchText"/> - </h:form> - <h:form id="accessLightAliasForm"> <p:remoteCommand name="_retreiveLightAliases" actionListener="#{mapMB.retreiveLightAliases}" /> </h:form> diff --git a/web/src/main/webapp/WEB-INF/components/map/missingConnectionDialog.xhtml b/web/src/main/webapp/WEB-INF/components/map/missingConnectionDialog.xhtml index e07331ffac9846c89d56111fa733983202f2bbe9..051413b2b9f3a17b6f2af621c4f5e50360b60049 100644 --- a/web/src/main/webapp/WEB-INF/components/map/missingConnectionDialog.xhtml +++ b/web/src/main/webapp/WEB-INF/components/map/missingConnectionDialog.xhtml @@ -11,7 +11,7 @@ <h:panelGrid columns="1" cellpadding="5"> <p:inputTextarea id="removeContent" label="content" /> <f:facet name="footer"> - <p:commandButton id="removeConnectionButton" value="Remove" actionListener="#{searchMB.removeConnection}" onsuccess="dlg4.hide()" update=":tabView:mainForm:dTable"/> + <p:commandButton id="removeConnectionButton" value="Remove" actionListener="#{searchMB.removeConnection}" onsuccess="dlg4.hide()"/> <p:commandButton id="cancelRemoveButton" value="Cancel" onclick="dlg4.hide()" /> </f:facet> </h:panelGrid> @@ -21,7 +21,7 @@ <h:panelGrid columns="1" cellpadding="5"> <p:inputTextarea id="removeCommentContent" label="content" /> <f:facet name="footer"> - <p:commandButton id="removeCommentButton" value="Remove" actionListener="#{feedbackMB.removeComment}" onsuccess="dlg5.hide()" update=":tabView:mainForm:dTable"/> + <p:commandButton id="removeCommentButton" value="Remove" actionListener="#{feedbackMB.removeComment}" onsuccess="dlg5.hide()"/> <p:commandButton id="cancelRemoveCommentButton" value="Cancel" onclick="dlg5.hide()" /> </f:facet> </h:panelGrid> diff --git a/web/src/main/webapp/WEB-INF/components/map/searchPanel.xhtml b/web/src/main/webapp/WEB-INF/components/map/searchPanel.xhtml index 7c0373385a87e05158cbb3a2062e932d6fc90906..6260990e38ebb114d5a873a520234a80b2744712 100644 --- a/web/src/main/webapp/WEB-INF/components/map/searchPanel.xhtml +++ b/web/src/main/webapp/WEB-INF/components/map/searchPanel.xhtml @@ -6,11 +6,49 @@ xmlns:cc="http://java.sun.com/jsf/composite/pfcomp" xmlns:p="http://primefaces.org/ui"> +<h:outputStylesheet library="css" name="global.css" /> <h:outputStylesheet library="css" name="search.css" /> - -<h:form id="mainForm" class="searchPanel" > + +<div id="searchTab"> + <div name="searchQuery" class="searchPanel"> + <table cellpadding="4" style="width:100%"> + <tbody> + <tr> + <td>SEARCH:</td> + </tr> + <tr> + <td> + <input id="searchTextInput" class="input-field"/> + </td> + <td> + <a id="searchButton" href="#"> + <img src="resources/images/icons/search.png"/> + </a> + </td> + </tr> + <tr> + <td> + <input id="perfectMatchInput" type="checkbox"/> + <span>PERFECT MATCH</span> + </td> + </tr> + </tbody> + </table> + </div> + + <div name="searchResults" class="tabbable boxed parentTabs"> + <ul class="nav nav-tabs"> + <li class="active"><a href="#set1"/></li> + </ul> + <div class="tab-content"> + <div class="tab-pane fade active in" id="set1"> + </div> + </div> + </div> +</div> -<!-- Search text box --> +<!-- + <h:panelGrid columns="2" cellpadding="4" style="width:100%"> <h:outputText value="SEARCH: "/> <cc:helpButton helpText="search tab allows to search for particular elements or interactions in the map<p>perfect match tick box active: only terms with an exact match to the query will be returned<p>separate multiple search by semicolon" style="float:right;margin-top:-26px;margin-right:-20px;"/> @@ -29,7 +67,7 @@ <h:outputText value="PERFECT MATCH" style="font-size:13px; vertical-align:top; line-height:24px; padding-left:8px;"/> -<!-- results left panel --> +<! - - results left panel - - > <div class="searchResultsDivClass"> <p:tabView id="dTable" styleClass="searchResultsDivClass2" value="#{searchMB.results}" var="result" > <p:tab > @@ -42,14 +80,14 @@ <p:column > <cc:aliasSearchElement element="#{element}" elementRendered="#{(element['class'].simpleName == 'FullAliasView')}"/> <cc:reactionSearchElement element="#{element}" elementRendered="#{(element['class'].simpleName == 'FullReactionView')}"/> -<!-- <div style="float:right;"> +<! - - <div style="float:right;"> <p:commandLink actionListener="#{feedbackMB.updateCommentList}" oncomplete="commentDialog.show();" id="commentResultButton" ajax="true" update=":feedbackForm:feedbackDialog"> <h:graphicImage library="images" name="icons/comment.png" id="commentResultIcon" styleClass="imageButton"/> <f:param name="submodelId" value="#{searchRow.modelId}"/> <f:param name="latCoord" value="#{searchRow.latlng.lat}"/> <f:param name="lngCoord" value="#{searchRow.latlng.lng}"/> </p:commandLink> - </div> --> + </div> - - > </p:column> </p:dataTable> </p:scrollPanel> @@ -57,11 +95,7 @@ </p:tabView> </div> </h:form> -<h:form id="_searchConnector"> - <p:remoteCommand name="_refreshSearchOverlayCollection" actionListener="#{searchMB.refreshOverlayCollection}"/> - <p:remoteCommand name="_registerSearchOverlayCollection" actionListener="#{searchMB.registerOverlayCollection}"/> - <p:remoteCommand name="_clearSearchOverlayCollection" actionListener="#{searchMB.clear}" update=":tabView:mainForm:dTable :tabView:mainForm:searchText"/> -</h:form> +--> </html> diff --git a/web/src/main/webapp/index.xhtml b/web/src/main/webapp/index.xhtml index 7fe48e61ff52797e584c043a40a4a0d8cf8d7993..6431b0ebb5875907e84a860890c0bb099104d159 100644 --- a/web/src/main/webapp/index.xhtml +++ b/web/src/main/webapp/index.xhtml @@ -17,6 +17,9 @@ <!-- Google Maps API version 3.20 --> <script src="https://maps.google.com/maps/api/js?libraries=drawing&v=3.22" type="text/javascript"/> + + <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js" type="text/javascript"/> + <link rel="shortcut icon" href="./resources/images/favicon.png" type="image/png" /> @@ -63,6 +66,7 @@ function initMap(){ </h:head> <h:body onload="initMap();" > <h:outputStylesheet library="css" name="style.css"/> +<h:outputStylesheet library="css" name="global.css" /> <h:outputStylesheet library="css" name="pileup.css"/> <h:outputStylesheet library="css" name="bootstrap.min.css"/> diff --git a/web/src/main/webapp/resources/css/global.css b/web/src/main/webapp/resources/css/global.css new file mode 100644 index 0000000000000000000000000000000000000000..00c008f0697f6fa47befc8fc418f9d42d60f4175 --- /dev/null +++ b/web/src/main/webapp/resources/css/global.css @@ -0,0 +1,30 @@ +.input-field { + background-color: #21BDF1; + color: #ffffff; + width: 210px; + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + font-size: 14px; + font-weight: 900; + padding: 8px; + font-family: Lato; + margin: 0; + border: none +} + +.chkbox { + width: 16px; + height: 16px; + display: inline-block; + -moz-border-radius: 0px; + -webkit-border-radius: 0px; + border-radius: 0px; +} + +.result-table { + width: 100%; + border-spacing: 2px; + -webkit-border-horizontal-spacing: 2px; + -webkit-border-vertical-spacing: 2px; +} \ No newline at end of file