diff --git a/frontend-js/package.json b/frontend-js/package.json index 3ef40c46bae2aa51b177d90f0a69c3c9a0cb0b95..d042631c548bdf2e1572c92cb86070d3abc5f134 100644 --- a/frontend-js/package.json +++ b/frontend-js/package.json @@ -22,8 +22,8 @@ "browserify": "^13.1.1", "chai": "^3.5.0", "clean-css-cli": "^4.1.10", - "del": "^3.0.0", "datatables.net": "^1.10.13", + "del": "^3.0.0", "exorcist": "^0.4.0", "file-url": "^2.0.0", "istanbul": "0.4.5", diff --git a/frontend-js/src/main/js/gui/leftPanel/GuiUtils.js b/frontend-js/src/main/js/gui/leftPanel/GuiUtils.js index 033761cb459461969bd3e7476027be35e5222e7e..5ce556159b468e99eed2e403db839f87131446f2 100644 --- a/frontend-js/src/main/js/gui/leftPanel/GuiUtils.js +++ b/frontend-js/src/main/js/gui/leftPanel/GuiUtils.js @@ -11,6 +11,7 @@ var logger = require('../../logger'); function GuiUtils(configuration) { var self = this; self.setConfiguration(configuration); + self._tabIdCounter = 0; } GuiUtils.prototype = Object.create(AbstractGuiElement.prototype); @@ -376,7 +377,7 @@ GuiUtils.prototype.createAliasElement = function (params) { div.appendChild(self.createParamLine("Formula: ", alias.getFormula())); div.appendChild(self.createArrayParamLine("Former symbols: ", alias.getFormerSymbols())); div.appendChild(self.createPostTranslationalModifications("PostTranslational modifications: ", alias - .getOther('modifications'))); + .getOther('modifications'))); div.appendChild(self.createParamLine("Charge: ", alias.getCharge())); div.appendChild(self.createArrayParamLine("Synonyms: ", alias.getSynonyms())); div.appendChild(self.createLabelText(alias.getDescription())); @@ -416,7 +417,7 @@ GuiUtils.prototype.createModifiersLine = function (label, value) { return result; }; -GuiUtils.prototype.createTabMenuObject = function(params) { +GuiUtils.prototype.createTabMenuObject = function (params) { var name = params.name; var id = params.id; var navigationBar = params.navigationBar; @@ -434,7 +435,7 @@ GuiUtils.prototype.createTabMenuObject = function(params) { if (name !== undefined) { navLink.innerHTML = name; } - navLink.onclick = function() { + navLink.onclick = function () { $(this).tab('show'); }; navLi.appendChild(navLink); @@ -444,7 +445,7 @@ GuiUtils.prototype.createTabMenuObject = function(params) { return navLi; }; -GuiUtils.prototype.createTabContentObject = function(params) { +GuiUtils.prototype.createTabContentObject = function (params) { var navigationObject = params.navigationObject; var tabId = params.id; var result = document.createElement("div"); @@ -460,4 +461,81 @@ GuiUtils.prototype.createTabContentObject = function(params) { return result; }; +GuiUtils.prototype.createTabDiv = function (params) { + var tabDiv = Functions.createElement({ + type: "div", + name: "tabView", + className: "tabbable boxed parentTabs" + }); + + var tabMenuDiv = Functions.createElement({ + type: "ul", + className: "nav nav-tabs" + }); + tabDiv.appendChild(tabMenuDiv); + + var tabContentDiv = Functions.createElement({ + type: "div", + className: "tab-content" + }); + tabDiv.appendChild(tabContentDiv); + + if (params !== undefined && params.element !== undefined) { + params.element.appendChild(tabDiv); + } + + return { + element: tabDiv, + menu: tabMenuDiv, + content: tabContentDiv, + tabId: params.id + } +}; + +GuiUtils.prototype.createTab = function (params) { + var self = this; + var tabData = params.tabData; + + var tabId = tabData.tabId + "_tab_" + self._tabIdCounter; + self._tabIdCounter++; + + var navClass = ''; + var contentClass = 'tab-pane'; + if (tabData.menu.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; + + navLink.innerHTML = params.title; + + navLink.onclick = function () { + $(this).tab('show'); + }; + navLi.appendChild(navLink); + tabData.menu.appendChild(navLi); + + var contentDiv = document.createElement("div"); + contentDiv.style.height = "100%"; + contentDiv.className = contentClass; + contentDiv.id = tabId; + + if (Functions.isDomElement(params.content)) { + contentDiv.appendChild(params.content); + } else { + contentDiv.innerHTML = params.content; + } + tabData.content.appendChild(contentDiv); + + return { + title: navLink, + content: contentDiv + } +}; + module.exports = GuiUtils; diff --git a/frontend-js/src/main/js/gui/leftPanel/LeftPanel.js b/frontend-js/src/main/js/gui/leftPanel/LeftPanel.js index f3b3203fa21e750190e9f38c7c56d02bee5560c7..6e87c331574d71621b4d26703621d2c3cc3245f9 100644 --- a/frontend-js/src/main/js/gui/leftPanel/LeftPanel.js +++ b/frontend-js/src/main/js/gui/leftPanel/LeftPanel.js @@ -21,13 +21,13 @@ var Functions = require('../../Functions'); var logger = require('../../logger'); function LeftPanel(params) { - AbstractGuiElement.call(this, params); - var self = this; + AbstractGuiElement.call(this, params); + var self = this; - this._tabIdCount = 0; - this._panels = []; + this._tabIdCount = 0; + this._panels = []; - self._createPanelGui(); + self._createPanelGui(); } @@ -35,297 +35,281 @@ LeftPanel.prototype = Object.create(AbstractGuiElement.prototype); LeftPanel.prototype.constructor = LeftPanel; LeftPanel.prototype._createPanelGui = function () { - var self = this; - var panels = self.getPanelsDefinition(); - - var headerDiv = Functions.createElement({ - type: "div", - id: "headerPanel" - }); - var header = new Header({ - element: headerDiv, - customMap: self.getMap(), - }); - self.getElement().appendChild(headerDiv); - - var loginDialogDiv = Functions.createElement({ - type: "div", - name: "loginDialog", - style: "display:none", - }); - self.getElement().appendChild(loginDialogDiv); - this.setLoginDialog(new LoginDialog({ - element: loginDialogDiv, - customMap: self.getMap(), - })); - - var tabDiv = Functions.createElement({ - type: "div", - name: "tabView", - className: "tabbable boxed parentTabs" - }); - self.getElement().appendChild(tabDiv); - - var tabMenuDiv = Functions.createElement({ - type: "ul", - className: "nav nav-tabs" - }); - tabDiv.appendChild(tabMenuDiv); - - var tabContentDiv = Functions.createElement({ - type: "div", - className: "tab-content" - }); - tabDiv.appendChild(tabContentDiv); - - self.setHeader(header); - - var elementInfoDiv = Functions.createElement({ - name: "elementInfoDiv", - type: "div", - style: "background-color:#f3f3f3", - className: "minerva-element-info-div", - }); - self.elementInfoDiv = elementInfoDiv; - - for (var i = 0; i < panels.length; i++) { - self.addTab(panels[i], tabMenuDiv, tabContentDiv); - } + var self = this; + var panels = self.getPanelsDefinition(); + + var headerDiv = Functions.createElement({ + type: "div", + id: "headerPanel" + }); + var header = new Header({ + element: headerDiv, + customMap: self.getMap() + }); + self.getElement().appendChild(headerDiv); + + var loginDialogDiv = Functions.createElement({ + type: "div", + name: "loginDialog", + style: "display:none" + }); + self.getElement().appendChild(loginDialogDiv); + this.setLoginDialog(new LoginDialog({ + element: loginDialogDiv, + customMap: self.getMap() + })); + + var guiUtils = self.getGuiUtils(); + var tabData = guiUtils.createTabDiv({ + element: self.getElement(), + id: "left_panel" + }); + + self.setHeader(header); + + var elementInfoDiv = Functions.createElement({ + name: "elementInfoDiv", + type: "div", + style: "background-color:#f3f3f3", + className: "minerva-element-info-div" + }); + self.elementInfoDiv = elementInfoDiv; + + for (var i = 0; i < panels.length; i++) { + self.addTab(panels[i], tabData); + } }; LeftPanel.prototype.getPanelsDefinition = function () { - return [{ - name: "SEARCH", - className: "fa-search", - panelClass: SearchPanel, - }, { - name: "OVERLAYS", - className: "fa-th-list", - panelClass: OverlayPanel, - }, { - name: "SUBMAPS", - className: "fa-sitemap", - panelClass: SubmapPanel, - }, { - name: "INFO", - className: "fa-info", - panelClass: ProjectInfoPanel, - }]; + return [{ + name: "SEARCH", + className: "fa-search", + panelClass: SearchPanel + }, { + name: "OVERLAYS", + className: "fa-th-list", + panelClass: OverlayPanel + }, { + name: "SUBMAPS", + className: "fa-sitemap", + panelClass: SubmapPanel + }, { + name: "INFO", + className: "fa-info", + panelClass: ProjectInfoPanel + }]; }; LeftPanel.prototype.hideTab = function (panel) { - var self = this; - var panelDefinitions = self.getPanelsDefinition(); - for (var i = 0; i < panelDefinitions.length; i++) { - if (panel instanceof panelDefinitions[i].panelClass) { - var liElement = $("li:has(a[href='#left_panel_tab_" + i + "'])", $(self.getElement()))[0]; - if (liElement !== undefined) { - liElement.style.display = "none"; - } else { - logger.warn("Cannot find tab link for panel: " + panel.getPanelName()); - } - } + var self = this; + var panelDefinitions = self.getPanelsDefinition(); + for (var i = 0; i < panelDefinitions.length; i++) { + if (panel instanceof panelDefinitions[i].panelClass) { + var liElement = $("li:has(a[href='#left_panel_tab_" + i + "'])", $(self.getElement()))[0]; + if (liElement !== undefined) { + liElement.style.display = "none"; + } else { + logger.warn("Cannot find tab link for panel: " + panel.getPanelName()); + } } + } }; LeftPanel.prototype.init = function () { - var self = this; + var self = this; - var promises = []; - for (var i = 0; i < self._panels.length; i++) { - promises.push(self._panels[i].init()); - } - promises.push(self.getHeader().init()); - - var initEvents = new Promise(function (resolve) { - self.getMap().addListener("onBioEntityClick", function (e) { - return self.showElementDetails(e.arg); - }); - self.getMap().getOverlayByName("search").addListener("onSearch", function (e) { - if (e.arg.type === AbstractDbOverlay.QueryType.SEARCH_BY_COORDINATES) { - return self.showElementDetails(e.arg.identifiedElements[0][0]); - } else { - return self.showElementDetails(undefined); - } - }); - resolve(); + var promises = []; + for (var i = 0; i < self._panels.length; i++) { + promises.push(self._panels[i].init()); + } + promises.push(self.getHeader().init()); + + var initEvents = new Promise(function (resolve) { + self.getMap().addListener("onBioEntityClick", function (e) { + return self.showElementDetails(e.arg); + }); + self.getMap().getOverlayByName("search").addListener("onSearch", function (e) { + if (e.arg.type === AbstractDbOverlay.QueryType.SEARCH_BY_COORDINATES) { + return self.showElementDetails(e.arg.identifiedElements[0][0]); + } else { + return self.showElementDetails(undefined); + } }); - promises.push(initEvents); + resolve(); + }); + promises.push(initEvents); promises.push(self.getLoginDialog().init()); - return Promise.all(promises); + return Promise.all(promises); }; LeftPanel.prototype.showElementDetails = function (element) { - var self = this; - var div = self.elementInfoDiv; - if (!$(div).hasClass("ui-dialog-content")) { - $(div).dialog({ - resizable: false, - width: $(self.getElement()).width(), - height: 200, - beforeclose: function () { - $(this).dialog('option', 'position', [$(this).offset().left, $(this).offset().top]); - $(this).dialog('option', 'width', $(this).width()); - $(this).dialog('option', 'height', $(this).height()); - }, - position: { - my: "left bottom", - at: "left bottom", - of: $(self.getElement()) - } - }).siblings('.ui-dialog-titlebar').css("background", "gray"); - } - - var openTabName = $("[name='tabView'] > ul li.active a > .maintabdiv")[0].innerHTML; - var searchTabName = $("[name='tabView'] > ul li.active a > .maintabdiv")[1].innerHTML; - var isPanelHidden = (self.getElement().style.display === "none"); - if (isPanelHidden) { - openTabName = undefined; - } + var self = this; + var div = self.elementInfoDiv; + if (!$(div).hasClass("ui-dialog-content")) { + $(div).dialog({ + resizable: false, + width: $(self.getElement()).width(), + height: 200, + beforeclose: function () { + $(this).dialog('option', 'position', [$(this).offset().left, $(this).offset().top]); + $(this).dialog('option', 'width', $(this).width()); + $(this).dialog('option', 'height', $(this).height()); + }, + position: { + my: "left bottom", + at: "left bottom", + of: $(self.getElement()) + } + }).siblings('.ui-dialog-titlebar').css("background", "gray"); + } + + var openTabName = $("[name='tabView'] > ul li.active a > .maintabdiv")[0].innerHTML; + var searchTabName = $("[name='tabView'] > ul li.active a > .maintabdiv")[1].innerHTML; + var isPanelHidden = (self.getElement().style.display === "none"); + if (isPanelHidden) { + openTabName = undefined; + } if (element !== undefined && openTabName !== undefined && (openTabName.indexOf("SEARCH") === -1 || searchTabName !== "GENERIC")) { var model = self.getMap().getSubmapById(element.getModelId()).getModel(); return model.getByIdentifiedElement(element, true).then(function (bioEntity) { div.innerHTML = ""; div.appendChild(self.prepareElementDetailsContent(bioEntity)); - $(div).dialog("open"); - $(div).dialog("option", "title", self.getElementTitle(bioEntity)); - $(div).scrollTop(0); - }); - } else { - $(div).dialog("close"); - return Promise.resolve(); - } + $(div).dialog("open"); + $(div).dialog("option", "title", self.getElementTitle(bioEntity)); + $(div).scrollTop(0); + }); + } else { + $(div).dialog("close"); + return Promise.resolve(); + } }; LeftPanel.prototype.prepareElementDetailsContent = function (bioEntity) { - var guiUtils = new GuiUtils(this.getMap().getConfiguration()); - guiUtils.setMap(this.getMap()); - if (bioEntity instanceof Reaction) { - return guiUtils.createReactionElement({ - reaction: bioEntity, - showTitle: false, - }); - } else if (bioEntity instanceof Alias) { - return guiUtils.createAliasElement({ - alias: bioEntity, - showTitle: false, - }); - } else if (bioEntity instanceof PointData) { - return Functions.createElement({ - type: "div" - }); - } else { - throw new Error("Unknown element type:" + bioEntity); - } + var guiUtils = this.getGuiUtils(); + if (bioEntity instanceof Reaction) { + return guiUtils.createReactionElement({ + reaction: bioEntity, + showTitle: false + }); + } else if (bioEntity instanceof Alias) { + return guiUtils.createAliasElement({ + alias: bioEntity, + showTitle: false + }); + } else if (bioEntity instanceof PointData) { + return Functions.createElement({ + type: "div" + }); + } else { + throw new Error("Unknown element type:" + bioEntity); + } +}; + +LeftPanel.prototype.getGuiUtils = function () { + var self = this; + if (self._guiUtils === undefined) { + self._guiUtils = new GuiUtils(self.getMap().getConfiguration()); + self._guiUtils.setMap(self.getMap()); + } + return self._guiUtils; }; + LeftPanel.prototype.getElementTitle = function (bioEntity) { - if (bioEntity instanceof Reaction) { - return bioEntity.getType() + ": " + bioEntity.getReactionId(); - } else if (bioEntity instanceof Alias) { - return bioEntity.getType() + ": " + bioEntity.getName(); - } else if (bioEntity instanceof PointData) { - return "POINT: " + bioEntity.getId(); - } else { - throw new Error("Unknown element type:" + bioEntity); - } + if (bioEntity instanceof Reaction) { + return bioEntity.getType() + ": " + bioEntity.getReactionId(); + } else if (bioEntity instanceof Alias) { + return bioEntity.getType() + ": " + bioEntity.getName(); + } else if (bioEntity instanceof PointData) { + return "POINT: " + bioEntity.getId(); + } else { + throw new Error("Unknown element type:" + bioEntity); + } }; -LeftPanel.prototype.addTab = function (params, navElement, contentElement) { - var self = this; +LeftPanel.prototype.addTab = function (params, tabData) { + var self = this; - var name = params.name; + var name = params.name; + if (name !== undefined) { + if (name.length > 12) { + name = name.substring(0, 10) + "..."; + } + } else { + name = ""; + } - var tabId = "left_panel_tab_" + this._tabIdCount; - self._tabIdCount++; - var navClass = ''; - var contentClass = 'tab-pane'; - if (navElement.children.length === 0) { - navClass = "active"; - contentClass = "tab-pane active"; - } + var guiUtils = self.getGuiUtils(); + var data = guiUtils.createTab({ + tabData: tabData, + title: '<div class="maintabdiv"><i class="fa ' + params.className + ' maintab"></i><br>' + name + + '</div>', + content: "" + }); + + this._panels.push(new params.panelClass({ + element: data.content, + customMap: self.getMap(), + parent: self + })); - var navLi = document.createElement("li"); - navLi.className = navClass; - - var navLink = document.createElement("a"); - navLink.href = "#" + tabId; - if (name !== undefined) { - if (name.length > 12) { - name = name.substring(0, 10) + "..."; - } - } else { - name = ""; - } - // add this when we want to have triangle below - // '<div class="tngContainer"><div class="tng"></div></div>' - - navLink.innerHTML = '<div class="maintabdiv"><i class="fa ' + params.className + ' maintab"></i><br>' + name - + '</div>'; - - navLink.onclick = function () { - $(this).tab('show'); - }; - navLi.appendChild(navLink); - navElement.appendChild(navLi); - - var contentDiv = document.createElement("div"); - contentDiv.style.height = "100%"; - contentDiv.className = contentClass; - contentDiv.id = tabId; - - contentElement.appendChild(contentDiv); - - this._panels.push(new params.panelClass({ - element: contentDiv, - customMap: self.getMap(), - parent: self - })); }; LeftPanel.prototype.hide = function () { - this.getElement().style.display = "none"; + this.getElement().style.display = "none"; }; LeftPanel.prototype.show = function () { - this.getElement().style.display = "block"; + this.getElement().style.display = "block"; }; LeftPanel.prototype.setHeader = function (header) { - this._header = header; + this._header = header; }; LeftPanel.prototype.getHeader = function () { - return this._header; + return this._header; }; LeftPanel.prototype.setLoginDialog = function (loginDialog) { - this._loginDialog = loginDialog; + this._loginDialog = loginDialog; }; LeftPanel.prototype.getLoginDialog = function () { - return this._loginDialog; + return this._loginDialog; +}; + +LeftPanel.prototype.setPluginManager = function (pluginManager) { + this._pluginManager = pluginManager; +}; + +LeftPanel.prototype.getPluginManager = function () { + return this._pluginManager; }; LeftPanel.prototype.destroy = function () { - var self = this; - var promises = []; - promises.push(self.getHeader().destroy()); - var div = self.elementInfoDiv; - - var destroyPanel = new Promise(function (resolve) { - if ($(div).hasClass("ui-dialog-content")) { - $(div).dialog("destroy"); - } - resolve(); - }); - promises.push(destroyPanel); - promises.push(self.getLoginDialog().destroy()); - for (var i = 0; i < self._panels.length; i++) { - promises.push(self._panels[i].destroy()); + var self = this; + var promises = []; + promises.push(self.getHeader().destroy()); + var div = self.elementInfoDiv; + + var destroyPanel = new Promise(function (resolve) { + if ($(div).hasClass("ui-dialog-content")) { + $(div).dialog("destroy"); } - - return Promise.all(promises); + resolve(); + }); + promises.push(destroyPanel); + promises.push(self.getLoginDialog().destroy()); + for (var i = 0; i < self._panels.length; i++) { + promises.push(self._panels[i].destroy()); + } + + if (self._pluginManager !== undefined) { + promises.push(self._pluginManager.destroy()); + } + + return Promise.all(promises); }; module.exports = LeftPanel; diff --git a/frontend-js/src/main/js/gui/leftPanel/ProjectInfoPanel.js b/frontend-js/src/main/js/gui/leftPanel/ProjectInfoPanel.js index 02a6ba3c99d4c402811aa41d33cea96fb902c9f9..977d2d1da1de9a9d63650cec03bf6436329b67d6 100644 --- a/frontend-js/src/main/js/gui/leftPanel/ProjectInfoPanel.js +++ b/frontend-js/src/main/js/gui/leftPanel/ProjectInfoPanel.js @@ -20,6 +20,7 @@ function ProjectInfoPanel(params) { self._createInfoPanelLogic(); self._createUserDataTab(); + self._createPluginGui(); var logoutButton = self.getControlElement(PanelControlElementType.USER_TAB_LOGOUT_BUTTON); logoutButton.onclick = function () { @@ -328,6 +329,55 @@ ProjectInfoPanel.prototype._createUserDataTab = function () { }; +ProjectInfoPanel.prototype._createPluginGui = function () { + var self = this; + var guiUtils = self.getGuiUtils(); + var pluginDataDiv = Functions.createElement({ + type: "div", + className: "searchPanel" + }); + self.getElement().appendChild(pluginDataDiv); + + var pluginTitle = Functions.createElement({ + type: "h3", + content: '<br/>Plugins<br/>' + }); + pluginDataDiv.appendChild(pluginTitle); + + var pluginFormTab = Functions.createElement({ + type: "div", + style: "width:100%;display: table;border-spacing: 10px;" + }); + pluginDataDiv.appendChild(pluginFormTab); + + var pluginUrlLabel = Functions.createElement({ + type: "span", + content: "URL:", + style: "color:#999999" + }); + var pluginUrlInput = Functions.createElement({ + type: "input", + name: "pluginUrlValue" + }); + pluginDataDiv.appendChild(guiUtils.createTableRow([pluginUrlLabel, pluginUrlInput])); + + var centerTag = Functions.createElement({ + type: "center" + }); + pluginDataDiv.appendChild(centerTag); + + var logoutButton = Functions.createElement({ + type: "button", + name: "loadPluginButton", + content: "LOAD PLUGIN" + }); + centerTag.appendChild(logoutButton); + logoutButton.onclick = function () { + return self.getParent().getPluginManager().addPlugin({url: pluginUrlInput.value}).then(null, GuiConnector.alert); + } + +}; + ProjectInfoPanel.prototype.showUserProfilePage = function (user) { var self = this; diff --git a/frontend-js/src/main/js/minerva.js b/frontend-js/src/main/js/minerva.js index 938bb1c8ca1d52b1b37f7ab872539d3cd5ef57f7..fa515ec945176bdf6eb100ea0a3b7711d09d8383 100644 --- a/frontend-js/src/main/js/minerva.js +++ b/frontend-js/src/main/js/minerva.js @@ -11,6 +11,7 @@ var ConfigurationType = require('./ConfigurationType'); var CustomMap = require('./map/CustomMap'); var CustomMapOptions = require('./map/CustomMapOptions'); var Export = require('./Export'); +var PluginManager = require('./plugin/PluginManager'); var SearchDbOverlay = require('./map/overlay/SearchDbOverlay'); var LeftPanel = require('./gui/leftPanel/LeftPanel'); @@ -89,17 +90,24 @@ function createDivStructure(element) { className: "minerva-left-panel", }); element.appendChild(leftPanelDiv); - var rightPanelDiv = functions.createElement({ + var middlePanelDiv = functions.createElement({ type: "div", style: "display: table-cell;height:100%;width:100%;", }); - element.appendChild(rightPanelDiv); + element.appendChild(middlePanelDiv); var rightPanelContainerDiv = functions.createElement({ type: "div", style: "height:100%;width:100%;position:relative", }); - rightPanelDiv.appendChild(rightPanelContainerDiv); + middlePanelDiv.appendChild(rightPanelContainerDiv); + + var rightPanelDiv = functions.createElement({ + type: "div", + className: "minerva-plugin", + name: "plugin-div" + }); + element.appendChild(rightPanelDiv); var menuDiv = functions.createElement({ type: "div", @@ -529,6 +537,14 @@ function create(params) { customMap: customMap }); + var pluginManager = new PluginManager({ + element: functions.getElementByName(element, "plugin-div"), + map: customMap, + project: params.getProject(), + configuration: params.getConfiguration() + }); + leftPanel.setPluginManager(pluginManager); + topMenu = new TopMenu({ element: functions.getElementByName(element, "menuDiv"), customMap: customMap diff --git a/frontend-js/src/main/js/plugin/MinervaPluginProxy.js b/frontend-js/src/main/js/plugin/MinervaPluginProxy.js new file mode 100644 index 0000000000000000000000000000000000000000..0ea647889c7eb2df4a144fda49aff7e1ab0b6aec --- /dev/null +++ b/frontend-js/src/main/js/plugin/MinervaPluginProxy.js @@ -0,0 +1,29 @@ +"use strict"; + +var ObjectWithListeners = require('../ObjectWithListeners'); + +var Promise = require("bluebird"); + +var logger = require('../logger'); +var Functions = require('../Functions'); + +var id = 0; + +function createProject(options) { + return {}; +} + +function createConfiguration(options) { + return {}; +} + +function MinervaPluginProxy(options) { + return { + pluginId: options.pluginId, + element: options.element, + project: createProject(options), + configuration: createConfiguration(options) + }; +} + +module.exports = MinervaPluginProxy; diff --git a/frontend-js/src/main/js/plugin/Plugin.js b/frontend-js/src/main/js/plugin/Plugin.js new file mode 100644 index 0000000000000000000000000000000000000000..a0652a669739af66f342c0b76fe1834e47921078 --- /dev/null +++ b/frontend-js/src/main/js/plugin/Plugin.js @@ -0,0 +1,113 @@ +"use strict"; + +var MinervaPluginProxy = require('./MinervaPluginProxy'); +var ObjectWithListeners = require('../ObjectWithListeners'); + +var Promise = require("bluebird"); + +var logger = require('../logger'); +var Functions = require('../Functions'); + +var pluginId = 0; + +function Plugin(options) { + ObjectWithListeners.call(this); + var self = this; + self.setOptions(options); +} + +Plugin.prototype = Object.create(ObjectWithListeners.prototype); +Plugin.prototype.constructor = ObjectWithListeners; + +Plugin.prototype.setOptions = function (options) { + this._options = options; +}; + +Plugin.prototype.getOptions = function () { + return this._options; +}; + +Plugin.prototype.setLoadedPluginData = function (loadedPluginData) { + this._loadedPluginData = loadedPluginData; +}; +Plugin.prototype.getLoadedPluginData = function () { + return this._loadedPluginData; +}; + +Plugin.prototype.setMinervaPluginProxy = function (minervaPluginProxy) { + this._minervaPluginProxy = minervaPluginProxy; +}; +Plugin.prototype.getMinervaPluginProxy = function () { + return this._minervaPluginProxy; +}; + +Plugin.prototype.load = function () { + var self = this; + var options = self.getOptions(); + + + return ServerConnector.sendRequest({ + url: options.url, + description: "Loading plugin: " + options.url, + method: "GET" + }).then(function (content) { + var oldDefine = global.define; + var pluginData = undefined; + var error = false; + global.define = function (pluginFunction) { + try { + if (typeof pluginFunction === "function") { + pluginData = pluginFunction(); + } else { + pluginData = pluginFunction; + } + + var minervaPluginProxy = new MinervaPluginProxy({ + map: options.map, + pluginId: "plugin" + (pluginId++) + }); + self.setLoadedPluginData(pluginData); + self.setMinervaPluginProxy(minervaPluginProxy); + pluginData.register(minervaPluginProxy); + } catch (e) { + error = e; + } + }; + define = global.define; + try { + eval(content); + } catch (e) { + error = e; + } + global.define = oldDefine; + if (error) { + return Promise.reject(error); + } else if (pluginData === undefined) { + return Promise.reject("Invalid plugin. Expected 'define(...)' expression."); + } + }) + +}; + +Plugin.prototype.unload = function () { + var self = this; + return Promise.resolve().then(function () { + return self.getLoadedPluginData().unregister(); + }).then(function () { + return self.destroy(); + }); +}; + +Plugin.prototype.getName = function () { + return this.getLoadedPluginData().getName(); +}; + +Plugin.prototype.getVersion = function () { + return this.getLoadedPluginData().getVersion(); +}; + +Plugin.prototype.destroy = function () { + return Promise.resolve(); +}; + +module.exports = Plugin; diff --git a/frontend-js/src/main/js/plugin/PluginManager.js b/frontend-js/src/main/js/plugin/PluginManager.js new file mode 100644 index 0000000000000000000000000000000000000000..076bb0679230a1fd63b5c2a5b10457f6e39c5b85 --- /dev/null +++ b/frontend-js/src/main/js/plugin/PluginManager.js @@ -0,0 +1,111 @@ +"use strict"; + +var ObjectWithListeners = require('../ObjectWithListeners'); +var Plugin = require('./Plugin'); +var GuiUtils = require('../gui/leftPanel/GuiUtils'); + +var Promise = require("bluebird"); + +var logger = require('../logger'); +var Functions = require('../Functions'); + + +function PluginManager(options) { + ObjectWithListeners.call(this); + var self = this; + self.setProject(options.project); + self.setMap(options.map); + self.setElement(options.element); + self.setConfiguration(options.configuration); + self._plugins = []; +} + +PluginManager.prototype = Object.create(ObjectWithListeners.prototype); +PluginManager.prototype.constructor = ObjectWithListeners; + +PluginManager.prototype.setProject = function (project) { + this._project = project; +}; +PluginManager.prototype.setMap = function (map) { + this._map = map; +}; +PluginManager.prototype.getMap = function () { + return this._map; +}; +PluginManager.prototype.setElement = function (element) { + this._element = element; +}; +PluginManager.prototype.getElement = function () { + return this._element; +}; + +PluginManager.prototype.setConfiguration = function (configuration) { + this._configuration = configuration; +}; + +PluginManager.prototype.getConfiguration = function () { + return this._configuration; +}; + +PluginManager.prototype.getPlugins = function () { + return this._plugins; +}; + +PluginManager.prototype.addPlugin = function (options) { + var self = this; + var tabData = self.createTabForPlugin(); + var plugin = new Plugin({ + url: options.url, + element: tabData.content, + configuration: self.getConfiguration(), + map: self.getMap() + }); + self._plugins.push(plugin); + return plugin.load().then(function () { + tabData.title.innerHTML = plugin.getName(); + return plugin; + }); +}; + +PluginManager.prototype.createTabForPlugin = function () { + var self = this; + var tabData = self._tabData; + var guiUtils = new GuiUtils(self.getConfiguration()); + if (tabData === undefined) { + self.getElement().style.width = "300px"; + self.getElement().style.height = "100%"; + self._tabData = guiUtils.createTabDiv({element: self.getElement(), id: "plugin_tab"}); + tabData = self._tabData; + } + return guiUtils.createTab({ + title: "", + content: "", + tabData: tabData + }); +}; +PluginManager.prototype.removePlugin = function (plugin) { + var self = this; + var found = false; + for (var i = 0; i < self._plugins.length; i++) { + if (plugin === self._plugins[i]) { + self._plugins.splice(i, 1); + found = true; + } + } + if (!found) { + throw new Error("Plugin not registered"); + } + return plugin.unload(); +}; + +PluginManager.prototype.destroy = function () { + var self = this; + var promises = []; + for (var i = 0; i < self._plugins.length; i++) { + promises.push(self._plugins[i].unload()); + } + return Promise.all(promises); +}; + + +module.exports = PluginManager; diff --git a/frontend-js/src/test/js/gui/leftPanel/LeftPanel-test.js b/frontend-js/src/test/js/gui/leftPanel/LeftPanel-test.js index ec31fe885e847a742fd04558cfe8c3000cbf6b79..a1157cfb8acfefab4d562d4732f5c12181daaf85 100644 --- a/frontend-js/src/test/js/gui/leftPanel/LeftPanel-test.js +++ b/frontend-js/src/test/js/gui/leftPanel/LeftPanel-test.js @@ -89,11 +89,11 @@ describe('LeftPanel', function () { }); }); - describe('showElementDetails', function() { - it('default', function() { + describe('showElementDetails', function () { + it('default', function () { var map; var panel; - return ServerConnector.getProject().then(function(project) { + return ServerConnector.getProject().then(function (project) { map = helper.createCustomMap(project); helper.createSearchDbOverlay(map); @@ -102,31 +102,31 @@ describe('LeftPanel', function () { helper.createMiRnaDbOverlay(map); panel = new LeftPanel({ - element : testDiv, - customMap : map + element: testDiv, + customMap: map }); return panel.init(); - }).then(function() { + }).then(function () { var element = new IdentifiedElement({ - id : 329163, - type : "ALIAS", - modelId : map.getId() + id: 329163, + type: "ALIAS", + modelId: map.getId() }); return map.getModel().getByIdentifiedElement(element, true); - }).then(function(alias) { + }).then(function (alias) { return panel.showElementDetails(alias); - }).then(function() { + }).then(function () { assert.notOk($(panel.elementInfoDiv).dialog('isOpen')); return panel.destroy(); }); }); - it('when panel is hidden', function() { + it('when panel is hidden', function () { var map; var panel; - return ServerConnector.getProject().then(function(project) { + return ServerConnector.getProject().then(function (project) { map = helper.createCustomMap(project); helper.createSearchDbOverlay(map); @@ -135,22 +135,22 @@ describe('LeftPanel', function () { helper.createMiRnaDbOverlay(map); panel = new LeftPanel({ - element : testDiv, - customMap : map + element: testDiv, + customMap: map }); return panel.init(); - }).then(function() { + }).then(function () { panel.hide(); var element = new IdentifiedElement({ - id : 329163, - type : "ALIAS", - modelId : map.getId() + id: 329163, + type: "ALIAS", + modelId: map.getId() }); return map.getModel().getByIdentifiedElement(element, true); - }).then(function(alias) { + }).then(function (alias) { return panel.showElementDetails(alias); - }).then(function() { + }).then(function () { assert.notOk($(panel.elementInfoDiv).dialog('isOpen')); return panel.destroy(); }); diff --git a/frontend-js/src/test/js/mocha-config.js b/frontend-js/src/test/js/mocha-config.js index a65e6f684a8ad0322d458827db3be8eef2a1b3a5..9822eb85167b59678c335bbc85b94560cbcd60a4 100644 --- a/frontend-js/src/test/js/mocha-config.js +++ b/frontend-js/src/test/js/mocha-config.js @@ -7,6 +7,8 @@ var Helper = require('./helper'); var GuiConnector = require('./GuiConnector-mock'); +var path = require('path'); + // ----------------------------- var logger = require('./logger'); @@ -25,11 +27,15 @@ function mockBootstrap() { } before(function () { + // requirejs.config({ + // baseUrl: path.dirname(require.main.filename) + "/../../../" + // }); + // GLOBAL configuration global.navigator = { userAgent: 'node.js', appName: 'MinervaUnitTest', - appVersion: '0.0.1', + appVersion: '0.0.1' }; @@ -93,7 +99,7 @@ before(function () { require('datatables.net')(window, $); require('spectrum-colorpicker'); - global.tinycolor= window.tinycolor; + global.tinycolor = window.tinycolor; require('jstree'); global.google = require('./google-map-mock'); diff --git a/frontend-js/src/test/js/plugin/MinervaPluginProxy-test.js b/frontend-js/src/test/js/plugin/MinervaPluginProxy-test.js new file mode 100644 index 0000000000000000000000000000000000000000..97acbd9403c77e19fc0758d30a025295c402952c --- /dev/null +++ b/frontend-js/src/test/js/plugin/MinervaPluginProxy-test.js @@ -0,0 +1,28 @@ +"use strict"; + +/* exported logger */ +/* exported assert */ + +require("../mocha-config"); + +var MinervaPluginProxy = require('../../../main/js/plugin/MinervaPluginProxy'); + +var logger = require('../logger'); +var assert = require('assert'); + +describe('MinervaPluginProxy', function () { + it('constructor', function () { + var map = helper.createCustomMap(); + var proxy = new MinervaPluginProxy({ + map: map, + element: testDiv, + pluginId: "xx", + configuration: helper.getConfiguration() + }); + assert.ok(proxy); + assert.ok(proxy.pluginId); + assert.ok(proxy.element); + assert.ok(proxy.project); + assert.ok(proxy.configuration); + }); +}); diff --git a/frontend-js/src/test/js/plugin/Plugin-test.js b/frontend-js/src/test/js/plugin/Plugin-test.js new file mode 100644 index 0000000000000000000000000000000000000000..5aa485d681ff3e3d57161e7f3af9ec310908219c --- /dev/null +++ b/frontend-js/src/test/js/plugin/Plugin-test.js @@ -0,0 +1,27 @@ +"use strict"; + +/* exported logger */ +/* exported assert */ + +require("../mocha-config"); + +var Plugin = require('../../../main/js/plugin/Plugin'); + +var logger = require('../logger'); +var assert = require('assert'); + +describe('PluginManager', function () { + it('constructor', function () { + var plugin = new Plugin({url: "./testFiles/plugin/empty.js"}); + assert.ok(plugin); + }); + it('load', function () { + var plugin = new Plugin({url: "./testFiles/plugin/empty.js"}); + return plugin.load().then(function () { + assert.equal("test plugin", plugin.getName()); + assert.equal("0.0.1", plugin.getVersion()); + assert.equal(0, logger.getWarnings().length); + + }); + }); +}); diff --git a/frontend-js/src/test/js/plugin/PluginManager-test.js b/frontend-js/src/test/js/plugin/PluginManager-test.js new file mode 100644 index 0000000000000000000000000000000000000000..bd8430290b1c3c2b16cc6cfa19a287980298e0b7 --- /dev/null +++ b/frontend-js/src/test/js/plugin/PluginManager-test.js @@ -0,0 +1,60 @@ +"use strict"; + +/* exported logger */ +/* exported assert */ + +require("../mocha-config"); + +var PluginManager = require('../../../main/js/plugin/PluginManager'); + +var logger = require('../logger'); +var assert = require('assert'); + +describe('PluginManager', function () { + var createParams = function () { + var map = helper.createCustomMap(); + return { + map: map, + element: testDiv + }; + }; + it('constructor', function () { + var manager = new PluginManager(createParams()); + assert.ok(manager); + assert.equal(0, logger.getWarnings().length); + }); + + it('getPlugins', function () { + var manager = new PluginManager(createParams()); + assert.equal(0, manager.getPlugins().length); + }); + + describe('addPlugin', function () { + it('default', function () { + var manager = new PluginManager(createParams()); + return manager.addPlugin({url: "./testFiles/plugin/empty.js"}).then(function () { + assert.equal(1, manager.getPlugins().length); + }); + }); + it('after removal', function () { + var manager = new PluginManager(createParams()); + return manager.addPlugin({url: "./testFiles/plugin/empty.js"}).then(function (plugin) { + return manager.removePlugin(plugin); + }).then(function () { + return manager.addPlugin({url: "./testFiles/plugin/empty.js"}); + }).then(function () { + assert.equal(1, manager.getPlugins().length); + }); + }); + }); + + it('removePlugin', function () { + var manager = new PluginManager(createParams()); + return manager.addPlugin({url: "testFiles/plugin/empty.js"}).then(function (plugin) { + return manager.removePlugin(plugin); + }).then(function () { + assert.equal(0, manager.getPlugins().length); + }); + }); + +}); diff --git a/web/src/main/webapp/index.xhtml b/web/src/main/webapp/index.xhtml index 086a2b1b27aeeeda422c626983d0a9a2d79269c6..15e033cd63a3a9f9a5aafbc156a8ef2bdc10994d 100644 --- a/web/src/main/webapp/index.xhtml +++ b/web/src/main/webapp/index.xhtml @@ -19,7 +19,7 @@ <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <script src="https://cdn.datatables.net/1.10.13/js/jquery.dataTables.min.js"></script> - + <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.13/css/jquery.dataTables.min.css"/> <link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"/>