From 6dba4a2aa1951ad3594c98ed24e2f15c35e567d9 Mon Sep 17 00:00:00 2001 From: Piotr Gawron <piotr.gawron@uni.lu> Date: Wed, 4 Jan 2017 13:47:13 +0100 Subject: [PATCH] comments functionality works via API --- frontend-js/src/main/js/ServerConnector.js | 99 +++++- frontend-js/src/main/js/gui/CommentDialog.js | 289 ++++++++++++++++++ .../src/main/js/map/AbstractCustomMap.js | 7 +- frontend-js/src/main/js/map/CustomMap.js | 33 ++ frontend-js/src/main/js/map/data/Alias.js | 4 + frontend-js/src/main/js/map/data/MapModel.js | 94 +++++- frontend-js/src/main/js/map/data/Reaction.js | 65 ++++ frontend-js/src/test/js/GuiConnector-mock.js | 5 + .../src/test/js/ServerConnector-mock.js | 31 +- .../src/test/js/ServerConnector-test.js | 8 +- .../src/test/js/gui/CommentDialog-test.js | 31 ++ frontend-js/src/test/js/map/CustomMap-test.js | 32 ++ frontend-js/src/test/js/mocha-config.js | 5 + ...alse&projectId=sample&token=MOCK_TOKEN_ID& | 1 + ...=102&projectId=sample&token=MOCK_TOKEN_ID& | 1 + ...9168&projectId=sample&token=MOCK_TOKEN_ID& | 1 + ...9173&projectId=sample&token=MOCK_TOKEN_ID& | 1 + ...9173&projectId=sample&token=MOCK_TOKEN_ID& | 1 + ...3511&projectId=sample&token=MOCK_TOKEN_ID& | 1 + .../java/lcsb/mapviewer/bean/ExportBean.java | 3 +- .../components/map/feedbackDialog.xhtml | 54 +--- 21 files changed, 691 insertions(+), 75 deletions(-) create mode 100644 frontend-js/src/main/js/gui/CommentDialog.js create mode 100644 frontend-js/src/test/js/gui/CommentDialog-test.js create mode 100644 frontend-js/testFiles/apiCalls/comment/addComment/content=&coordinates=2,12&elementId=&elementType=POINT&email=&modelId=102&name=&pinned=false&projectId=sample&token=MOCK_TOKEN_ID& create mode 100644 frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=2,12&modelId=102&projectId=sample&token=MOCK_TOKEN_ID& create mode 100644 frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329168&projectId=sample&token=MOCK_TOKEN_ID& create mode 100644 frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329173&projectId=sample&token=MOCK_TOKEN_ID& create mode 100644 frontend-js/testFiles/apiCalls/project/getElements/columns=id,bounds,modelId&id=329168,329173&projectId=sample&token=MOCK_TOKEN_ID& create mode 100644 frontend-js/testFiles/apiCalls/project/getReactions/columns=&id=153511&projectId=sample&token=MOCK_TOKEN_ID& diff --git a/frontend-js/src/main/js/ServerConnector.js b/frontend-js/src/main/js/ServerConnector.js index 30be4d5fa9..1ef52399d4 100644 --- a/frontend-js/src/main/js/ServerConnector.js +++ b/frontend-js/src/main/js/ServerConnector.js @@ -690,6 +690,21 @@ ServerConnector.readFile = function(url) { }); }; +ServerConnector.sendPostRequest = function(url, params) { + return new Promise(function(resolve, reject) { + request.post({url:url, form:params}, function(error, response, body) { + if (error) { + reject(error); + + } else if (response.statusCode !== 200) { + reject(response); + } else { + resolve(body); + } + }); + }); +}; + ServerConnector.getToken = function() { var self = this; return new Promise(function(resolve) { @@ -738,8 +753,11 @@ ServerConnector.getApiUrl = function(paramObj) { var type = paramObj.type; var method = paramObj.method; var params = this.createGetParams(paramObj.params); - - var result = this.getApiBaseUrl() + "/"+type+"/"+method+"?"+params; + + var result = this.getApiBaseUrl() + "/"+type+"/"+method; + if (params!=="") { + result+="?"+params; + } return result; }; @@ -754,6 +772,12 @@ ServerConnector.getProjectUrl = function(projectId, token) { }); }; +ServerConnector.addCommentUrl = function() { + return this.getApiUrl({type:"comment", + method: "addComment", + }); +}; + ServerConnector.getOverlaysUrl = function(projectId, token) { return this.getApiUrl({type:"overlay", method: "getOverlayList", @@ -807,9 +831,12 @@ ServerConnector.getOverlayElementsUrl = function(overlayId, projectId, token) { ServerConnector.idsToString = function (ids) { var result = ""; if (ids!==undefined) { + ids.sort(function(a, b){return a-b}); for (var i = 0; i < ids.length; i++) { if (result !== "") { - result = result + "," + ids[i]; + if (ids[i-1]!=ids[i]) { + result = result + "," + ids[i]; + } // we ignore duplicates } else { result = ids[i]; } @@ -818,6 +845,10 @@ ServerConnector.idsToString = function (ids) { return result; }; +ServerConnector.pointToString = function (point) { + return point.x+","+point.y; +}; + ServerConnector.columnsToString = function (columns) { if (columns === undefined) { return ""; @@ -865,6 +896,21 @@ ServerConnector.getConfigurationUrl = function(token) { }); return result; }; +ServerConnector.getClosestElementsByCoordinatesUrl = function(params) { + var coordinates = this.pointToString(params.coordinates); + var projectId = params.projectId; + var modelId = params.modelId; + var token = params.token; + + return this.getApiUrl({type:"project", + method:"getClosestElementsByCoordinates", + params: { + projectId: projectId, + coordinates: coordinates, + modelId: modelId, + token: token}, + }); +}; ServerConnector.getConfigurationParam = function(paramId) { var self = this; @@ -1078,4 +1124,51 @@ ServerConnector.getSessionData = function() { } return this._sessionData; }; + +ServerConnector.getClosestElementsByCoordinates = function(params) { + var self = this; + var projectId; + return new Promise(function(resolve, reject) { + return self.getProjectId(params.projectId).then(function(result) { + projectId = result; + return self.getToken(); + }).then(function(token) { + return self.readFile(self.getClosestElementsByCoordinatesUrl({projectId:projectId, token:token, modelId:params.modelId, coordinates: params.coordinates})); + }).then(function(content) { + var array=JSON.parse(content); + var result = []; + for (var i = 0; i < array.length; i++) { + result.push(new IdentifiedElement(array[i])); + } + resolve(result); + }).catch(function(exception){ + reject(exception); + }); + }); +}; + +ServerConnector.addComment = function(params) { + var self = this; + var projectId; + return new Promise(function(resolve, reject) { + return self.getProjectId(params.projectId).then(function(result) { + params.projectId = result; + return self.getToken(); + }).then(function(token) { + params.token = token; + params.coordinates =self.pointToString(params.coordinates); + return self.sendPostRequest(self.addCommentUrl(),params); + }).then(function(content) { + var response=JSON.parse(content); + if (response.status==="OK") { + resolve(); + } else { + reject(response); + } + }).catch(function(exception){ + reject(exception); + }); + }); +}; + module.exports = ServerConnector; diff --git a/frontend-js/src/main/js/gui/CommentDialog.js b/frontend-js/src/main/js/gui/CommentDialog.js new file mode 100644 index 0000000000..cdb2f59e89 --- /dev/null +++ b/frontend-js/src/main/js/gui/CommentDialog.js @@ -0,0 +1,289 @@ +"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 CommentDialog(element, customMap) { + var self = this; + + this.setElement(element); + this.setMap(customMap); + var table = document.createElement('table'); + + var typeLabel = document.createElement('label'); + typeLabel.innerHTML = "Type"; + var typeOptions = document.createElement("select"); + this.setTypeOptions(typeOptions); + + table.appendChild(createRow([ typeLabel, typeOptions ])); + + var detailDiv = document.createElement('div'); + + table.appendChild(createRow([ document.createElement('div'), detailDiv ])); + + var pinnedLabel = document.createElement('label'); + pinnedLabel.innerHTML = "Pinned"; + var pinnedCheckbox = document.createElement('input'); + pinnedCheckbox.type = "checkbox"; + + table.appendChild(createRow([ pinnedLabel, pinnedCheckbox ])); + this.setPinnedCheckbox(pinnedCheckbox); + + var nameLabel = document.createElement('label'); + nameLabel.innerHTML = "Name:<br/>(Visible to moderators only)"; + var nameInput = document.createElement('input'); + nameInput.type = "text"; + + table.appendChild(createRow([ nameLabel, nameInput ])); + this.setNameInput(nameInput); + nameInput.onchange = function(el) { + logger.debug(nameInput.value); + } + + var emailLabel = document.createElement('label'); + emailLabel.innerHTML = "Email:<br/>(Visible to moderators only)"; + var emailInput = document.createElement('input'); + emailInput.type = "text"; + + table.appendChild(createRow([ emailLabel, emailInput ])); + this.setEmailInput(emailInput); + + var contentLabel = document.createElement('label'); + contentLabel.innerHTML = "Content:"; + var contentInput = document.createElement('textarea'); + contentInput.cols = "80"; + contentInput.rows = "3"; + + table.appendChild(createRow([ contentLabel, contentInput ])); + this.setContentInput(contentInput); + + var sendButton = document.createElement('button'); + sendButton.innerHTML = "Send"; + sendButton.onclick = function() { + self.addComment().then(function() { + if (self.close !== undefined) { + self.close(); + } else { + logger.warn("Cannot close dialog"); + } + }); + } + + table.appendChild(createRow([ sendButton ])); + + element.appendChild(table); + + typeOptions.onchange = function() { + var option = self.getSelectedType(); + var text = ""; + if (option instanceof Alias) { + if (option.getFullName() !== undefined) { + text = option.getFullName(); + } + } else if (option instanceof Reaction) { + text = "Reactants: "; + var reactants = option.getReactants(); + for (var i = 0; i < reactants.length; i++) { + text += reactants[i].getName() + ","; + } + text += "<br/>"; + text += "Modifiers: "; + var modifiers = option.getModifiers(); + for (var i = 0; i < modifiers.length; i++) { + text += modifiers[i].getName() + ","; + } + text += "<br/>"; + text += "Products: "; + var products = option.getProducts(); + for (var i = 0; i < products.length; i++) { + text += products[i].getName() + ","; + } + text += "<br/>"; + } + detailDiv.innerHTML = text; + }; + +} + +CommentDialog.GENERAL = "<General>"; + +function createRow(elements) { + var row = document.createElement('tr'); + for (var i = 0; i < elements.length; i++) { + var container = document.createElement('td'); + container.appendChild(elements[i]); + row.appendChild(container); + } + return row; +}; + +CommentDialog.prototype.setMap = function(map) { + this._map = map; +} + +CommentDialog.prototype.getMap = function() { + return this._map; +} + +CommentDialog.prototype.setElement = function(element) { + this._element = element; +} + +CommentDialog.prototype.getElement = function() { + return this._element; +} + +CommentDialog.prototype.open = function(types) { + var self = this; + self.setTypes([ CommentDialog.GENERAL ]); + + var promises = [ CommentDialog.GENERAL ]; + for (var i = 0; i < types.length; i++) { + var ie = types[i]; + if (ie.getType() === "ALIAS") { + promises.push(self.getMap().getSubmodelById(ie.getModelId()).getModel().getAliasById(ie.getId(), true)); + } else if (ie.getType() === "REACTION") { + promises.push(self.getMap().getSubmodelById(ie.getModelId()).getModel().getReactionById(ie.getId(), true)); + } else { + throw new Error("Unknown element type: " + ie.getType()); + } + } + return Promise.all(promises).then(function(elements) { + self.setTypes(elements); + }); +}; +CommentDialog.prototype.setTypes = function(types) { + var typeOptions = this.getTypeOptions(); + while (typeOptions.firstChild) { + typeOptions.removeChild(typeOptions.firstChild); + } + + for (var i = 0; i < types.length; i++) { + var option = document.createElement("option"); + option.value = i; + var element = types[i]; + var text = element; + if (element instanceof Alias) { + text = element.getType() + ": " + element.getName(); + } else if (element instanceof Reaction) { + text = "Reaction: " + element.getReactionId(); + } + option.text = text; + typeOptions.appendChild(option); + } + typeOptions.value = 0; + + this._types = types; +} + +CommentDialog.prototype.getTypes = function() { + return this._types; +}; + +CommentDialog.prototype.getSelectedType = function() { + return this._types[this.getTypeOptions().value]; +}; + +CommentDialog.prototype.setSelectedType = function(value) { + if (Functions.isInt(value)) { + this.getTypeOptions().value = value; + this.getTypeOptions().onchange(); + } else { + throw new Error("Unknown value type: " + value) + } +}; + +CommentDialog.prototype.getTypeOptions = function() { + return this._typeOptions; +}; +CommentDialog.prototype.setTypeOptions = function(typeOptions) { + this._typeOptions = typeOptions; +}; +CommentDialog.prototype.setContentInput = function(contentInput) { + this._contentInput = contentInput; +}; +CommentDialog.prototype.getContentInput = function() { + return this._contentInput; +}; +CommentDialog.prototype.setNameInput = function(nameInput) { + this._nameInput = nameInput; +}; +CommentDialog.prototype.getNameInput = function() { + return this._nameInput; +}; +CommentDialog.prototype.setEmailInput = function(emailInput) { + this._emailInput = emailInput; +}; +CommentDialog.prototype.getEmailInput = function() { + return this._emailInput; +}; +CommentDialog.prototype.setPinnedCheckbox = function(pinnedCheckbox) { + this._pinnedCheckbox = pinnedCheckbox; +}; + +CommentDialog.prototype.getPinnedCheckbox = function() { + return this._pinnedCheckbox; +}; + +CommentDialog.prototype.getTypes = function(types) { + return this._types; +}; + +CommentDialog.prototype.getName = function() { + return this.getNameInput().value; +}; + +CommentDialog.prototype.getEmail = function() { + return this.getEmailInput().value; +}; + +CommentDialog.prototype.getContent = function() { + return this.getContentInput().value; +}; + +CommentDialog.prototype.isPinned = function() { + return this.getPinnedCheckbox().checked; +}; +CommentDialog.prototype.getSelectedTypeId = function() { + var selected = this.getSelectedType(); + if (selected instanceof Alias) { + return selected.getId(); + } else if (selected instanceof Reaction) { + return selected.getId(); + } else { + return ""; + } +}; + +CommentDialog.prototype.getSelectedTypeClass = function() { + var selected = this.getSelectedType(); + if (selected instanceof Alias) { + return "ALIAS"; + } else if (selected instanceof Reaction) { + return "REACTION"; + } else { + return "POINT"; + } +}; + +CommentDialog.prototype.addComment = function() { + var self = this; + var name = self.getName(); + return ServerConnector.addComment({ + modelId : self.getMap().getActiveSubmapId(), + coordinates : self.getMap().getActiveSubmapClickCoordinates(), + name : name, + email : self.getEmail(), + content : self.getContent(), + pinned : self.isPinned(), + elementId : self.getSelectedTypeId(), + elementType : self.getSelectedTypeClass() + + }); +}; + +module.exports = CommentDialog; diff --git a/frontend-js/src/main/js/map/AbstractCustomMap.js b/frontend-js/src/main/js/map/AbstractCustomMap.js index a2016e0ffe..34ed259182 100644 --- a/frontend-js/src/main/js/map/AbstractCustomMap.js +++ b/frontend-js/src/main/js/map/AbstractCustomMap.js @@ -317,13 +317,15 @@ AbstractCustomMap.prototype.registerMapClickEvents = function() { }); // select last clicked map - google.maps.event.addListener(this.getGoogleMap(), 'click', function() { + google.maps.event.addListener(this.getGoogleMap(), 'click', function(mouseEvent) { customMap.setActiveSubmapId(self.getId()); + customMap.setActiveSubmapClickCoordinates(self.fromLatLngToPoint(mouseEvent.latLng)); }); // select last clicked map - google.maps.event.addListener(this.getGoogleMap(), 'rightclick', function() { + google.maps.event.addListener(this.getGoogleMap(), 'rightclick', function(mouseEvent) { customMap.setActiveSubmapId(self.getId()); + customMap.setActiveSubmapClickCoordinates(self.fromLatLngToPoint(mouseEvent.latLng)); }); // prepare for image export @@ -356,7 +358,6 @@ AbstractCustomMap.prototype.registerMapClickEvents = function() { // context menu event google.maps.event.addListener(this.getGoogleMap(), 'rightclick', function(mouseEvent) { - ServerConnector.requestUpdateCommentList(self.getId(), mouseEvent.latLng); GuiConnector.showRightClickMenu(GuiConnector.xPos, GuiConnector.yPos); }); }; diff --git a/frontend-js/src/main/js/map/CustomMap.js b/frontend-js/src/main/js/map/CustomMap.js index d0ab5c7f9e..48d7ef1662 100644 --- a/frontend-js/src/main/js/map/CustomMap.js +++ b/frontend-js/src/main/js/map/CustomMap.js @@ -7,6 +7,7 @@ var functions = require('../Functions'); var AbstractCustomMap = require('./AbstractCustomMap'); var AliasMarker = require('./marker/AliasMarker'); +var CommentDialog = require('../gui/CommentDialog'); var ControlType = require('./ControlType'); var CustomMapOptions = require('./CustomMapOptions'); var IdentifiedElement = require('./data/IdentifiedElement'); @@ -83,7 +84,11 @@ function CustomMap(options) { // list of reference genomes this._referenceGenome = []; + var commentDialog = new CommentDialog(document.getElementById("feedbackContent"), this); + this.setCommentDialog(commentDialog); + + ServerConnector.actualizeSessionData(); } @@ -1660,6 +1665,16 @@ CustomMap.prototype.setActiveSubmapId = function(submapId) { this._activeSubmapId = submapId; }; +CustomMap.prototype.setActiveSubmapClickCoordinates = function(coordinates) { + if (!(coordinates instanceof google.maps.Point)) { + throw new Error("Coordinates must be provided as google.maps.Point object, but found: "+coordinates ); + } + this._activeSubmapCoordinates = coordinates; +}; +CustomMap.prototype.getActiveSubmapClickCoordinates = function() { + return this._activeSubmapCoordinates; +}; + CustomMap.prototype.updateAliasesForLayout = function(layoutId, jsonAliases) { logger.debug("Updating aliases for layout: " + layoutId); @@ -1739,4 +1754,22 @@ CustomMap.prototype.getControl = function(type) { return this._controls[type]; }; +CustomMap.prototype.setCommentDialog = function(commentDialog) { + this._commentDialog = commentDialog; + commentDialog.close = function(){ + jsfCommentDialog.hide(); + } +}; + +CustomMap.prototype.getCommentDialog = function() { + return this._commentDialog; +}; + +CustomMap.prototype.openCommentDialog = function() { + var self = this; + return ServerConnector.getClosestElementsByCoordinates({modelId:this.getActiveSubmapId(), coordinates:this.getActiveSubmapClickCoordinates()}).then(function(elements){ + return self.getCommentDialog().open(elements); + }); +}; + module.exports = CustomMap; diff --git a/frontend-js/src/main/js/map/data/Alias.js b/frontend-js/src/main/js/map/data/Alias.js index 9ddea3e571..7b426ecc9b 100644 --- a/frontend-js/src/main/js/map/data/Alias.js +++ b/frontend-js/src/main/js/map/data/Alias.js @@ -110,6 +110,10 @@ Alias.prototype.getName = function() { return this.name; }; +Alias.prototype.getFullName = function() { + return this.fullName; +}; + 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 411450ae13..c2fcb21d92 100644 --- a/frontend-js/src/main/js/map/data/MapModel.js +++ b/frontend-js/src/main/js/map/data/MapModel.js @@ -136,17 +136,18 @@ MapModel.prototype.getAliasById = function(id, complete) { MapModel.prototype.getCompleteAliasById = function(id) { var self = this; return new Promise(function(resolve, reject) { - if (self._aliases[id].isComplete()) { + if (self._aliases[id]!==undefined && self._aliases[id].isComplete()) { resolve(self._aliases[id]); + } else { + ServerConnector.getAliases([id]).then(function(aliases){ + if (self._aliases[id] === undefined) { + self._aliases[id] = aliases[0]; + } else { + self._aliases[id] .update(aliases[0]); + } + resolve(self._aliases[id]); + }, reject); } - ServerConnector.getAliases([id]).then(function(aliases){ - if (self._aliases[id] === undefined) { - self._aliases[id] = aliases[0]; - } else { - self._aliases[id] .update(aliases[0]); - } - resolve(self._aliases[id]); - }, reject); }); }; @@ -157,8 +158,11 @@ MapModel.prototype.getCompleteAliasById = function(id) { * identifier of the {@link Reaction} * @returns {@link Reaction} by identifier */ -MapModel.prototype.getReactionById = function(id) { +MapModel.prototype.getReactionById = function(id, complete) { var self = this; + if (complete) { + return this.getCompleteReactionById(id); + } return new Promise(function(resolve, reject) { if (self._reactions[id] !== undefined) { resolve(self._reactions[id]); @@ -170,6 +174,69 @@ MapModel.prototype.getReactionById = function(id) { }); }; +MapModel.prototype.getCompleteReactionById = function(id) { + var self = this; + return new Promise(function(resolve, reject) { + if (self._reactions[id]!==undefined && self._reactions[id].isComplete()) { + resolve(self._reactions[id]); + } else { + var result; + ServerConnector.getReactions([id]).then(function(reactions){ + if (self._reactions[id] === undefined) { + self._reactions[id] = reactions[0]; + } else { + self._reactions[id] .update(reactions[0]); + } + var ids =[]; + var i; + result =self._reactions[id]; + for (i=0;i<result.getReactants().length;i++) { + if (!(result.getReactants()[i] instanceof Alias)) { + if (self._aliases[result.getReactants()[i]]===undefined || !self._aliases[result.getReactants()[i]].isComplete()) { + ids.push(result.getReactants()[i]); + } + } + } + for (i=0;i<result.getProducts().length;i++) { + if (!(result.getProducts()[i] instanceof Alias)) { + if (self._aliases[result.getProducts()[i]]===undefined || !self._aliases[result.getProducts()[i]].isComplete()) { + ids.push(result.getProducts()[i]); + } + } + } + for (i=0;i<result.getModifiers().length;i++) { + if (!(result.getModifiers()[i] instanceof Alias)) { + if (self._aliases[result.getModifiers()[i]]===undefined || !self._aliases[result.getModifiers()[i]].isComplete()) { + ids.push(result.getModifiers()[i]); + } + } + } + return self.getMissingElements({aliasIds:ids, complete : true}); + }).then(function(){ + var i; + result =self._reactions[id]; + for (i=0;i<result.getReactants().length;i++) { + if (!(result.getReactants()[i] instanceof Alias)) { + result.getReactants()[i] = self._aliases[result.getReactants()[i]]; + } + } + for (i=0;i<result.getProducts().length;i++) { + if (!(result.getProducts()[i] instanceof Alias)) { + result.getProducts()[i] = self._aliases[result.getProducts()[i]]; + } + } + for (i=0;i<result.getModifiers().length;i++) { + if (!(result.getModifiers()[i] instanceof Alias)) { + result.getModifiers()[i] = self._aliases[result.getModifiers()[i]]; + } + } + resolve(result); + }); + } + }); +}; + + MapModel.prototype.getMissingElements = function(elements) { var self = this; @@ -221,7 +288,12 @@ MapModel.prototype.getMissingElements = function(elements) { var aliasPromise = null; if (aliasIds.length>0){ - aliasPromise = ServerConnector.getLightAliases(aliasIds); + if (elements.complete){ + aliasPromise = ServerConnector.getAliases(aliasIds); + } else { + aliasPromise = ServerConnector.getLightAliases(aliasIds); + + } } diff --git a/frontend-js/src/main/js/map/data/Reaction.js b/frontend-js/src/main/js/map/data/Reaction.js index ec7c176696..8b6aae5848 100644 --- a/frontend-js/src/main/js/map/data/Reaction.js +++ b/frontend-js/src/main/js/map/data/Reaction.js @@ -39,6 +39,8 @@ function Reaction(javaObject) { } this.setCenter(javaObject.centerPoint); this.setModelId(javaObject.modelId); + this.setCompletness(false); + this.update(javaObject); } } @@ -87,4 +89,67 @@ Reaction.prototype.setModelId = function(modelId) { this._modelId = modelId; }; +Reaction.prototype.update = function(javaObject) { + if (javaObject.reactionId === undefined) { + return; + } + this.setReactionId(javaObject.reactionId); + + if (javaObject.reactants !== "") { + this.setReactants(javaObject.reactants.split(",")); + } else { + this.setReactants([]); + } + if (javaObject.products !== "") { + this.setProducts(javaObject.products.split(",")); + } else { + this.setProducts([]); + } + if (javaObject.modifiers !== "") { + this.setModifiers(javaObject.modifiers.split(",")); + } else { + this.setModifiers([]); + } +}; + +Reaction.prototype.getCompletness = function() { + return this._completness; +}; + +Reaction.prototype.setCompletness = function(completness) { + this._completness = completness; +}; + +Reaction.prototype.getReactionId = function() { + return this._reactionId; +}; + +Reaction.prototype.setReactionId = function(reactionId) { + this._reactionId = reactionId; +}; + +Reaction.prototype.getReactants = function() { + return this._reactants; +}; + +Reaction.prototype.setReactants = function(reactants) { + this._reactants = reactants; +}; + +Reaction.prototype.setProducts = function(products) { + this._products = products; +}; + +Reaction.prototype.getProducts = function() { + return this._products; +}; + +Reaction.prototype.setModifiers = function(modifiers) { + this._modifiers = modifiers; +}; + +Reaction.prototype.getModifiers = function() { + return this._modifiers; +}; + module.exports = Reaction; diff --git a/frontend-js/src/test/js/GuiConnector-mock.js b/frontend-js/src/test/js/GuiConnector-mock.js index d098d2aabd..a0eec6f133 100644 --- a/frontend-js/src/test/js/GuiConnector-mock.js +++ b/frontend-js/src/test/js/GuiConnector-mock.js @@ -141,4 +141,9 @@ GuiConnectorMock.getCustomMap = function() { return this._customMap; }; +GuiConnectorMock.alert = function(message) { + logger.error(message); + throw new Error(message); +}; + module.exports = GuiConnectorMock; diff --git a/frontend-js/src/test/js/ServerConnector-mock.js b/frontend-js/src/test/js/ServerConnector-mock.js index b9151e2300..d1e35939b0 100644 --- a/frontend-js/src/test/js/ServerConnector-mock.js +++ b/frontend-js/src/test/js/ServerConnector-mock.js @@ -160,10 +160,6 @@ ServerConnectorMock.sendOverlayDetailDataRequest = function(overlayName, identif ServerConnectorMock.callListeners("onSendOverlayDetailDataRequest", overlayName, identifiedElement); }; -ServerConnectorMock.requestUpdateCommentList = function(modelId, latLngCoordinates) { - ServerConnectorMock.callListeners("onRequestUpdateCommentList", [ modelId, latLngCoordinates ]); -}; - ServerConnectorMock.searchByCoord = function(modelId, latLngCoordinates) { ServerConnectorMock.callListeners("onSearchByCoord", [ modelId, latLngCoordinates ]); }; @@ -216,6 +212,33 @@ ServerConnectorMock.readFile = function(url) { }); }; +ServerConnectorMock.sendPostRequest = function(url, params) { + var self = this; + return new Promise(function(resolve, reject) { + if (url.indexOf("http") === 0) { + request.post({url:url, form:params}, function(error, response, body) { + if (error) { + reject(error); + + } else if (response.statusCode !== 200) { + reject(response); + } else { + resolve(body); + } + }); + } else { + var mockUrl = url+"/"+self.createGetParams(params); + fs.readFile(mockUrl, 'utf8', function(err, content) { + if (err) { + reject(err); + } else { + resolve(content); + } + }); + } + }); +}; + ServerConnectorMock.getApiBaseUrl = function() { return "./testFiles/apiCalls/"; }; diff --git a/frontend-js/src/test/js/ServerConnector-test.js b/frontend-js/src/test/js/ServerConnector-test.js index bac17fa99f..c2c9f5a3e7 100644 --- a/frontend-js/src/test/js/ServerConnector-test.js +++ b/frontend-js/src/test/js/ServerConnector-test.js @@ -50,7 +50,7 @@ describe('ServerConnector', function() { assert.equal(reaction.getModelId(), 15781); }); }); - + it('getOverlayElements', function() { return ServerConnector.getOverlayElements(101).then(function(result) { assert.equal(result.length, 1); @@ -62,4 +62,10 @@ describe('ServerConnector', function() { }); }); + it('idsToString', function() { + var ids = [ 3, 2, 9, 1, 6, 8, 3, 2, 9, 1, 7, 3 ]; + var str = ServerConnector.idsToString(ids); + assert.equal(str, "1,2,3,6,7,8,9") + }); + }); diff --git a/frontend-js/src/test/js/gui/CommentDialog-test.js b/frontend-js/src/test/js/gui/CommentDialog-test.js new file mode 100644 index 0000000000..d94f3234ae --- /dev/null +++ b/frontend-js/src/test/js/gui/CommentDialog-test.js @@ -0,0 +1,31 @@ +"use strict"; + +var Helper = require('../helper'); + +require("../mocha-config.js"); + +var CommentDialog = require('../../../main/js/gui/CommentDialog'); + +var chai = require('chai'); +var assert = chai.assert; +var logger = require('../logger'); + +describe('CommentDialog', function() { + + var helper; + before(function() { + helper = new Helper(); + }); + + it('contructor', function() { + var map = helper.createCustomMap(); + + var dialog = new CommentDialog(testDiv, map); + logger.debug(testDiv.innerHTML); + }); + + it('getName', function() { + var dialog = new CommentDialog(testDiv); + assert.ok(dialog.getName()!==undefined); + }); +}); diff --git a/frontend-js/src/test/js/map/CustomMap-test.js b/frontend-js/src/test/js/map/CustomMap-test.js index 4e6040d712..d0fe6f60b1 100644 --- a/frontend-js/src/test/js/map/CustomMap-test.js +++ b/frontend-js/src/test/js/map/CustomMap-test.js @@ -626,4 +626,36 @@ describe('CustomMap', function() { }); }); + it("openCommentDialog", function() { + var map = helper.createCustomMap(); + map.getModel().setId(102); + map.setActiveSubmapId(102); + map.setActiveSubmapClickCoordinates(new google.maps.Point(2,12)); + return map.openCommentDialog().then(function(){ + var types = map.getCommentDialog().getTypes(); + assert.equal(types.length, 3); + var selected = map.getCommentDialog().getSelectedType(); + assert.ok(selected === "<General>"); + + map.getCommentDialog().setSelectedType(1); + selected = map.getCommentDialog().getSelectedType(); + assert.notOk(selected === "<General>"); + + map.getCommentDialog().setSelectedType(2); + selected = map.getCommentDialog().getSelectedType(); + assert.notOk(selected === "<General>"); + }); + }); + + it("addComment", function() { + var map = helper.createCustomMap(); + map.getModel().setId(102); + map.setActiveSubmapId(102); + map.setActiveSubmapClickCoordinates(new google.maps.Point(2,12)); + return map.openCommentDialog().then(function(){ + return map.getCommentDialog().addComment(); + }); + }); + + }); diff --git a/frontend-js/src/test/js/mocha-config.js b/frontend-js/src/test/js/mocha-config.js index f8a84ea24b..0ef06b7c38 100644 --- a/frontend-js/src/test/js/mocha-config.js +++ b/frontend-js/src/test/js/mocha-config.js @@ -33,11 +33,16 @@ beforeEach(function() { global.testDiv.id = "test"; document.body.appendChild(testDiv); + global.dialogDiv = document.createElement("div"); + global.dialogDiv.id = "feedbackContent"; + document.body.appendChild(global.dialogDiv); + ServerConnector.init(); ServerConnector.setToken("MOCK_TOKEN_ID"); }); after(function() { document.body.removeChild(global.testDiv); + document.body.removeChild(global.dialogDiv); delete global.testDiv; }); diff --git a/frontend-js/testFiles/apiCalls/comment/addComment/content=&coordinates=2,12&elementId=&elementType=POINT&email=&modelId=102&name=&pinned=false&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/comment/addComment/content=&coordinates=2,12&elementId=&elementType=POINT&email=&modelId=102&name=&pinned=false&projectId=sample&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..48aa9beb26 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/comment/addComment/content=&coordinates=2,12&elementId=&elementType=POINT&email=&modelId=102&name=&pinned=false&projectId=sample&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +{"status":"OK"} \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=2,12&modelId=102&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=2,12&modelId=102&projectId=sample&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..5b65788a70 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=2,12&modelId=102&projectId=sample&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"modelId":102,"id":329173,"type":"ALIAS"},{"modelId":102,"id":153511,"type":"REACTION"}] \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329168&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329168&projectId=sample&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..f85b05bb4b --- /dev/null +++ b/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329168&projectId=sample&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"formerSymbols":[],"references":[],"modelId":15781,"synonyms":[],"description":"","type":"RNA","name":"s5","bounds":{"x":0.0,"y":118.5,"width":90.0,"height":25.0},"id":329168}] \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329173&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329173&projectId=sample&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..1dd70f6d37 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329173&projectId=sample&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"formerSymbols":[],"references":[],"modelId":102,"synonyms":[],"description":"description of S1\r\nthird line","type":"Protein","name":"s1","bounds":{"x":12.0,"y":6.0,"width":80.0,"height":40.0},"id":329173}] \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/project/getElements/columns=id,bounds,modelId&id=329168,329173&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getElements/columns=id,bounds,modelId&id=329168,329173&projectId=sample&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..40ddfd8017 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/project/getElements/columns=id,bounds,modelId&id=329168,329173&projectId=sample&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"modelId":102,"bounds":{"x":12.0,"y":6.0,"width":80.0,"height":40.0},"id":329173},{"modelId":102,"bounds":{"x":0.0,"y":118.5,"width":90.0,"height":25.0},"id":329168}] \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/project/getReactions/columns=&id=153511&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getReactions/columns=&id=153511&projectId=sample&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..1aa69bc2e9 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/project/getReactions/columns=&id=153511&projectId=sample&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"modelId":102,"reactants":"329168","reactionId":"re2","id":153511,"type":"State transition","lines":[{"start":{"x":45.833333333333336,"y":118.49999999999999},"end":{"x":47.983923957904906,"y":86.24114063142645},"type":"START"},{"start":{"x":48.516076042095094,"y":78.25885936857357},"end":{"x":50.666666666666664,"y":46.0},"type":"END"}],"modifiers":"","centerPoint":{"x":48.25,"y":82.25},"products":"329173"}] \ No newline at end of file diff --git a/web/src/main/java/lcsb/mapviewer/bean/ExportBean.java b/web/src/main/java/lcsb/mapviewer/bean/ExportBean.java index a516eed1bd..5cae8b6263 100644 --- a/web/src/main/java/lcsb/mapviewer/bean/ExportBean.java +++ b/web/src/main/java/lcsb/mapviewer/bean/ExportBean.java @@ -986,8 +986,7 @@ public class ExportBean extends AbstractManagedBean { MenuModel result = new DefaultMenuModel(); DefaultMenuItem addCommentItem = new DefaultMenuItem("Add comment"); - addCommentItem.setOncomplete("commentDialog.show();"); - addCommentItem.setUpdate(":feedbackForm:feedbackDialog"); + addCommentItem.setOncomplete("jsfCommentDialog.show();customMap.openCommentDialog();"); addCommentItem.setStyleClass("addCommentContext"); addCommentItem.setCommand("#{exportMB.nop()}"); addCommentItem.setAjax(true); diff --git a/web/src/main/webapp/WEB-INF/components/map/feedbackDialog.xhtml b/web/src/main/webapp/WEB-INF/components/map/feedbackDialog.xhtml index 7985b50d88..ce04a24bc9 100644 --- a/web/src/main/webapp/WEB-INF/components/map/feedbackDialog.xhtml +++ b/web/src/main/webapp/WEB-INF/components/map/feedbackDialog.xhtml @@ -5,56 +5,8 @@ xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:p="http://primefaces.org/ui"> - <h:form id = "feedbackForm"> - <p:dialog id="feedbackDialog" header="Comment" widgetVar="commentDialog" modal="true" onShow="updateFeedbackForm();"> - <!-- This is a workaround, because update in menuitem it had some issues--> - <p:remoteCommand name="updateFeedbackForm" update="feedbackList,feedbackDescription1,feedbackDescription2,feedbackDescription3" /> - - <h:panelGrid columns="2" cellpadding="5"> - <h:outputLabel value="Type:" /> - <p:selectOneMenu id="feedbackList" value="#{feedbackMB.feedbackElement}" style="width:300px;padding-right: 8px;"> - <p:ajax update="feedbackDescription1,feedbackDescription2,feedbackDescription3" event="change" /> - <f:selectItems value="#{feedbackMB.feedbackElementStringList}"/> - </p:selectOneMenu> - <h:outputLabel value="" /> - <h:outputLabel id="feedbackDescription1" value="#{feedbackMB.elementDescription1}" /> - <h:outputLabel value="" /> - <h:outputLabel id="feedbackDescription2" value="#{feedbackMB.elementDescription2}" /> - <h:outputLabel value="" /> - <h:outputLabel id="feedbackDescription3" value="#{feedbackMB.elementDescription3}" /> - <h:outputText value="Pinned: " /> - <p:selectBooleanButton value="#{feedbackMB.pinned}" onLabel="Yes" offLabel="No" onIcon="ui-icon-check" offIcon="ui-icon-close"/> - <h:panelGroup> - <h:outputLabel for="feedbackName" value="Name:" /> - <br/> - <h:outputLabel for="feedbackName" value="(Visible to moderators only)" /> - </h:panelGroup> - - <p:inputText id="feedbackName" value="#{feedbackMB.name}" label="username" style="font-size: 120%;width:100%"/> - <h:panelGroup> - <h:outputLabel for="feedbackEmail" value="Email:" /> - <br/> - <h:outputLabel for="feedbackName" value="(Visible to moderators only)" /> - </h:panelGroup> - <p:inputText id="feedbackEmail" value="#{feedbackMB.email}" label="email" style="font-size: 120%;width:100%"/> - <h:outputLabel for="feedbackContent" value="Content:" /> - <p:inputTextarea id="feedbackContent" value="#{feedbackMB.content}" label="content" style="font-size: 120%;width:100%"/> - <f:facet name="footer"> - <p:commandButton id="sendFedbackButton" value="Send" actionListener="#{feedbackMB.sendFeedback}" onsuccess="commentDialog.hide()" /> - </f:facet> - </h:panelGrid> + <p:dialog id="feedbackDialog" header="Comment" widgetVar="jsfCommentDialog" modal="true" > + <div id="feedbackContent"/> </p:dialog> - </h:form> - - <h:form id="feedbackUpdateForm"> - <p:remoteCommand name="_updateCommentList" actionListener="#{feedbackMB.updateCommentList}" update=":feedbackForm:feedbackDialog"/> - </h:form> -<h:form id="_commentConnector"> - <p:remoteCommand name="_requestCommentDetailDataFunction" actionListener="#{feedbackMB.requestDetailData}"/> - <p:remoteCommand name="_refreshCommentOverlayCollection" actionListener="#{feedbackMB.refreshOverlayCollection}"/> - <p:remoteCommand name="_registerCommentOverlayCollection" actionListener="#{feedbackMB.registerOverlayCollection}"/> - <p:remoteCommand name="_clearCommentOverlayCollection" actionListener="#{feedbackMB.clear}" /> - <p:remoteCommand name="_setShowComments " actionListener="#{feedbackMB.setShowComments}" /> -</h:form> -</html> \ No newline at end of file +</html> -- GitLab