From 0e51f61b38b04ee2dd0602932af32948bbabc0f5 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 6 Mar 2017 17:52:13 +0000 Subject: [PATCH 001/339] added geppetto/[pathparameter] support --- .../frontend/controllers/Application.java | 23 ++++++++++++++++--- src/main/webapp/WEB-INF/spring/app-config.xml | 1 + .../webapp/js/pages/geppetto/geppetto.ejs | 5 ++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/Application.java b/src/main/java/org/geppetto/frontend/controllers/Application.java index 9d3201502..5c25a554b 100644 --- a/src/main/java/org/geppetto/frontend/controllers/Application.java +++ b/src/main/java/org/geppetto/frontend/controllers/Application.java @@ -2,9 +2,12 @@ import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; +import java.util.Scanner; +import javax.servlet.ServletContext; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -26,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.PathVariable; import com.google.gson.Gson; @@ -38,9 +42,8 @@ public class Application private static Log logger = LogFactory.getLog(Application.class); - @RequestMapping(value = "/geppetto", method = RequestMethod.GET) - public String geppetto(HttpServletRequest req) - { + + private String getGeppetto(HttpServletRequest req){ try { IAuthService authService = AuthServiceCreator.getService(); @@ -99,6 +102,20 @@ else if(geppettoManager.getUser() == null) return "redirect:http://geppetto.org"; } + @RequestMapping(value = "/geppetto", method = RequestMethod.GET) + public String geppetto(HttpServletRequest req) + { + return getGeppetto(req); + } + + @RequestMapping(value = "/geppetto/{page}", method = RequestMethod.GET) + public String geppettoWithContent(HttpServletRequest req, Model model, @PathVariable("page") String page) + { + InputStream content = Application.class.getResourceAsStream("/build/static/" + page + ".html"); + model.addAttribute("content", new String(new Scanner(content, "UTF-8").useDelimiter("\\A").next())); + return getGeppetto(req); + } + @RequestMapping(value = "/geppettotestingprojects", method = RequestMethod.GET) public @ResponseBody Test getTestingProjects(@RequestParam String url) throws IOException { diff --git a/src/main/webapp/WEB-INF/spring/app-config.xml b/src/main/webapp/WEB-INF/spring/app-config.xml index 0fb5340d5..bad0fa7e8 100644 --- a/src/main/webapp/WEB-INF/spring/app-config.xml +++ b/src/main/webapp/WEB-INF/spring/app-config.xml @@ -16,6 +16,7 @@ + diff --git a/src/main/webapp/js/pages/geppetto/geppetto.ejs b/src/main/webapp/js/pages/geppetto/geppetto.ejs index 648f2c4c7..b992b4cb1 100644 --- a/src/main/webapp/js/pages/geppetto/geppetto.ejs +++ b/src/main/webapp/js/pages/geppetto/geppetto.ejs @@ -24,6 +24,11 @@ + + +
From 0f44b3789341982f83cdc59038811f562bd8416d Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 6 Mar 2017 17:53:03 +0000 Subject: [PATCH 002/339] add geppetto hm extension --- src/main/webapp/extensions/extensionsConfiguration.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/extensions/extensionsConfiguration.json b/src/main/webapp/extensions/extensionsConfiguration.json index 16982d7eb..170bd5bca 100644 --- a/src/main/webapp/extensions/extensionsConfiguration.json +++ b/src/main/webapp/extensions/extensionsConfiguration.json @@ -1,6 +1,7 @@ { - "geppetto-default/ComponentsInitialization": true, + "geppetto-default/ComponentsInitialization": false, "geppetto-osb/ComponentsInitialization": false, "geppetto-vfb/ComponentsInitialization": false, - "geppetto-neuron/ComponentsInitialization": false + "geppetto-neuron/ComponentsInitialization": false, + "geppetto-hm/ComponentsInitialization": true } From 2c5f30a04fd2b53e60ce888742d37e800d83404c Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 6 Mar 2017 17:54:09 +0000 Subject: [PATCH 003/339] allows componentinitialization depending on url path parameter --- src/main/webapp/extensions/extensions.js | 17 ++++++++++++ src/main/webapp/js/common/GEPPETTO.Utility.js | 12 +++++++++ src/main/webapp/package.json | 2 ++ src/main/webapp/webpack.config.js | 27 +++++++++++++++++-- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/extensions/extensions.js b/src/main/webapp/extensions/extensions.js index b5322fb08..b27aa434b 100644 --- a/src/main/webapp/extensions/extensions.js +++ b/src/main/webapp/extensions/extensions.js @@ -8,9 +8,26 @@ define(['./extensionsConfiguration.json', 'geppetto','../js/components/Component } //Require your extension in extensionConfiguration.json + var availableExtensions = []; for (var extension in extensionConfiguration){ if (extensionConfiguration[extension]){ + availableExtensions.push(extension.split("/")[0]); loadExtension(extension); } } + + var paths = GEPPETTO.Utility.getPathStringParameters(); + for (var pathIndex in paths){ + for (var availableExtensionIndex in availableExtensions){ + try { + require(['../extensions/' + availableExtensions[availableExtensionIndex] + "/" + paths[pathIndex]], function(componentsInitialization){ + componentsInitialization(GEPPETTO); + }); + } + catch( e ) { + console.log('Components Initialization ' + paths[pathIndex] + ' can not be found in extension ' + availableExtensions[availableExtensionIndex]); + } + } + } + }); \ No newline at end of file diff --git a/src/main/webapp/js/common/GEPPETTO.Utility.js b/src/main/webapp/js/common/GEPPETTO.Utility.js index 26918a79c..3840d36e3 100644 --- a/src/main/webapp/js/common/GEPPETTO.Utility.js +++ b/src/main/webapp/js/common/GEPPETTO.Utility.js @@ -148,6 +148,18 @@ define(function (require) { var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); }, + + getPathStringParameters: function () { + var paths = [] + var locationPaths = location.pathname.split("/"); + for (var pathIndex in locationPaths){ + var locationPath = locationPaths[pathIndex]; + if (locationPath != 'geppetto' && locationPath != 'org.geppetto.frontend' && locationPath != ''){ + paths.push(locationPath); + } + } + return paths; + }, persistedAndWriteMessage: function (caller) { var message = GEPPETTO.Resources.OPERATION_NOT_SUPPORTED; diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 77a255d21..2c89bc95c 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -25,6 +25,7 @@ "bloodhound-js": "^1.2.1", "bootstrap": "^3.3.7", "codemirror": "^5.19.0", + "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.26.2", "d3": "^4.2.7", "d3-plugins-dist": "^3.2.0", @@ -55,6 +56,7 @@ "pixi.js": "4.2.3", "plotly.js": "^1.20.5", "qunitjs": "^2.1.1", + "raw-loader": "^0.5.1", "react": "15.4.1", "react-dom": "15.4.1", "react-simpletabs": "^0.7.0", diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index ca5b4b34d..d9e1a59bd 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -1,6 +1,7 @@ var path = require('path'); var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); +var CopyWebpackPlugin = require('copy-webpack-plugin'); console.log("\nThe arguments passed to webpack are:\n"); console.log(process.argv); @@ -43,16 +44,34 @@ if (generateTestsBundle) { console.log("The Webpack entries are:\n"); console.log(entries); -module.exports = { +var extensionConfiguration = require('./extensions/extensionsConfiguration.json'); +var availableExtensions = []; +for (var extension in extensionConfiguration){ + if (extensionConfiguration[extension]){ + availableExtensions.push({from: 'extensions/' + extension.split("/")[0] + "/static/*", to: 'static', flatten: true}); + } +} +console.log("Static pages coming from extensions are:\n"); +console.log(availableExtensions); + + +module.exports = { +// context: __dirname, entry: entries, output: { path: './build/', + //path: path.resolve(__dirname, 'build/'), filename: '[name].bundle.js', publicPath: publicPath }, plugins: [ + new CopyWebpackPlugin(availableExtensions +// [ { from: 'extensions/geppetto-tibs/static/biography.html', +// to: 'biography.html' } ] + + ), new HtmlWebpackPlugin({ filename: 'geppetto.vm', template: './js/pages/geppetto/geppetto.ejs', @@ -142,7 +161,11 @@ module.exports = { { test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file?name=/fonts/[name].[ext]' - } + }, + { + test: /\.html$/, + loader: 'raw-loader' + } ] }, node: { From 01317456865be13ee7ab214056f6718c987128bd Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 13 Mar 2017 07:47:50 +0000 Subject: [PATCH 004/339] implement jquery ui dialog as a react component, wrap component with newWidget component, create addWidget api in factory, modify form component accordingly --- .../webapp/js/components/ComponentFactory.js | 23 +- .../js/components/interface/form/Form.js | 5 + .../webapp/js/components/widgets/NewWidget.js | 682 ++++++++++++++++++ .../webapp/js/pages/geppetto/geppetto.ejs | 2 + src/main/webapp/js/pages/geppetto/main.js | 5 +- 5 files changed, 708 insertions(+), 9 deletions(-) create mode 100644 src/main/webapp/js/components/widgets/NewWidget.js diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index d9be7d004..59d2b0f75 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -38,6 +38,8 @@ define(function (require) { var ReactDOM = require('react-dom'); var spinner=require('./interface/loadingSpinner/LoadingSpinner.js'); + var addWidget = require('./widgets/NewWidget.js'); + //All the components potentially instantiable go here var components = { 'FORM':'interface/form/Form', @@ -63,6 +65,7 @@ define(function (require) { 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', 'RAISEDBUTTON': 'controls/RaisedButton' + //'WIDGETCONTAINER': 'widgets/WidgetContainer' } @@ -77,17 +80,25 @@ define(function (require) { var that=this; require(["./" + components[componentID]], function(loadedModule){ var component = React.createFactory(loadedModule)(properties) - var renderedComponent = that.renderComponent(component, container); - if(callback!=undefined){ - callback(renderedComponent); - } + var renderedComponent = that.renderComponent(component, container, callback); return renderedComponent; }); }, + addWidget: function(componentID, properties, callback){ + var that=this; + require(["./" + components[componentID]], function(loadedModule){ + var component = React.createFactory(addWidget(loadedModule))(properties); + var renderedComponent = that.renderComponent(component, document.getElementById('widgetContainer'), callback); + + //GEPPETTO.widgetContainer.addChildren(component) + return renderedComponent; + }); + }, + - renderComponent: function(component, container){ + renderComponent: function(component, container, callback){ //Let's create a dialog if (container == undefined){ @@ -122,7 +133,7 @@ define(function (require) { container = dialog.get(0); } - return ReactDOM.render(component, container); + return ReactDOM.render(component, container, callback); } }; }; diff --git a/src/main/webapp/js/components/interface/form/Form.js b/src/main/webapp/js/components/interface/form/Form.js index f476f6d1d..13a8f5b86 100644 --- a/src/main/webapp/js/components/interface/form/Form.js +++ b/src/main/webapp/js/components/interface/form/Form.js @@ -14,6 +14,11 @@ define(function (require) { var uiSchema ={}; var formComponent = React.createClass({ + + shouldComponentUpdate() { + return false; + }, + render: function(){ return (
0) { + + this.historyMenu.show({ + top: event.pageY, + left: event.pageX + 1, + groups: that.getItems(that.controller.history, "controller.history"), + data: that + }); + } + + if (event != null) { + event.preventDefault(); + } + return false; + }, + + showContextMenu: function (event, data) { + var handlers = GEPPETTO.MenuManager.getCommandsProvidersFor(data.getMetaType()); + + if (handlers.length > 0) { + var groups = []; + for (var handlerIndex = 0; handlerIndex < handlers.length; handlerIndex++) { + groups = groups.concat(handlers[handlerIndex](data)); + } + + this.contextMenu.show({ + top: event.pageY, + left: event.pageX + 1, + groups: groups, + //registeredItems: registeredItems, + data: data + }); + } + + if (event != null) { + event.preventDefault(); + } + + return false; + }, + + /** + * hides / shows the title bar + */ + showTitleBar: function (show) { + if (show) { + this.$el.parent().find(".ui-dialog-titlebar").show(); + } else { + this.$el.parent().find(".ui-dialog-titlebar").hide(); + } + return this; + }, + + updateNavigationHistoryBar: function () { + var disabled = "arrow-disabled"; + if (this.getItems(this.controller.history, "controller.history").length <= 1) { + if (!$("#" + this.id + "-left-nav").hasClass(disabled)) { + $("#" + this.id + "-left-nav").addClass(disabled); + $("#" + this.id + "-right-nav").addClass(disabled); + } + } else { + if ($("#" + this.id + "-left-nav").hasClass(disabled)) { + $("#" + this.id + "-left-nav").removeClass(disabled); + $("#" + this.id + "-right-nav").removeClass(disabled); + } + } + }, + + showHistoryNavigationBar: function (show) { + var leftNav = $("#" + this.id + "-left-nav"); + var rightNav = $("#" + this.id + "-right-nav"); + + if (show) { + if ((leftNav.length == 0) && (rightNav.length == 0)) { + + var disabled = ""; + if (this.getItems(this.controller.history, "controller.history").length <= 1) { + disabled = "arrow-disabled "; + } + + var that = this; + var button = $("
" + + "
").click(function (event) { + var historyItems = that.getItems(that.controller.history, "controller.history"); + var item; + if (event.target.id == (that.id + "-left-nav") || (that.id + "-right-nav")) { + that.executedAction = historyItems.length - 1; + } + item = historyItems[that.executedAction].action[0]; + GEPPETTO.Console.executeImplicitCommand(item); + $("#" + that.id).parent().find(".ui-dialog-title").html(historyItems[that.executedAction].label); + event.stopPropagation(); + }); + + var dialogParent = this.$el.parent(); + button.insertBefore(dialogParent.find("span.ui-dialog-title")); + $(button).addClass("widget-title-bar-button"); + } + } else { + if (leftNav.is(":visible") && rightNav.is(":visible")) { + leftNav.remove(); + rightNav.remove(); + this.executedAction = 0; + } + } + }, + + /** + * hides / shows the exit button + */ + showCloseButton: function (show) { + if (show) { + this.$el.parent().find(".ui-dialog-titlebar-close").show(); + } else { + this.$el.parent().find(".ui-dialog-titlebar-close").hide(); + } + }, + + addButtonToTitleBar: function (button) { + var dialogParent = this.$el.parent(); + dialogParent.find("div.ui-dialog-titlebar").prepend(button); + $(button).addClass("widget-title-bar-button"); + }, + + addHelpButton: function () { + var that = this; + this.addButtonToTitleBar($("
").click(function () { + GEPPETTO.ComponentFactory.addComponent('MDMODAL', { + title: that.id.slice(0, -1) + ' help', + content: that.getHelp(), + show: true + }, document.getElementById("modal-region")); + })); + }, + + /** + * Makes the widget draggable or not + * + * @param draggable + */ + setDraggable: function (draggable) { + if (draggable) { + this.$el.parent().draggable({ disabled: false }); + // NOTE: this will wipe any class applied to the widget... + this.setClass(''); + } else { + this.$el.parent().draggable({ disabled: true }); + this.setClass('noStyleDisableDrag'); + } + }, + + /** + * Set background as transparent + * + * @param isTransparent + */ + setTrasparentBackground: function (isTransparent) { + if (isTransparent) { + this.$el.parent().addClass('transparent-back'); + this.previousMaxTransparency = true; + } else { + this.$el.parent().removeClass('transparent-back'); + } + return this; + }, + + /** + * Inject CSS for custom behaviour + */ + setClass: function (className) { + this.$el.dialog({ dialogClass: className }).dialogExtend(); + }, + + /** + * Perform a jquery ui effect to the widget + */ + perfomEffect: function (effect, options, speed) { + this.$el.parent().effect(effect, options, speed) + }, + + /** + * Perform a shake effect to the widget + */ + shake: function (options, speed) { + if (options === undefined) { + options = { distance: 5, times: 3 } + } + if (speed === undefined) { + speed = 500 + } + + this.$el.parent().effect('shake', options, speed) + }, + + shouldComponentUpdate() { + return false; + }, + + componentDidMount: function () { + var that = this; + + //create the dialog window for the widget + this.dialog = $("#" + this.props.id).dialog( + { + //appendTo: "#widgetContainer", + //autoOpen: false, + resizable: true, + draggable: true, + top: 10, + height: 300, + width: 350, + close: function (event, ui) { + if (event.originalEvent && + $(event.originalEvent.target).closest(".ui-dialog-titlebar-close").length) { + that.destroy(); + } + } + }).dialogExtend({ + "closable": true, + "maximizable": true, + "minimizable": true, + "collapsable": true, + "restore": true, + "minimizeLocation": "right", + "icons": { + "maximize": "fa fa-window-maximize", + "minimize": "fa fa-window-minimize", + "collapse": "fa fa-chevron-circle-up", + "restore": "fa fa-window-restore", + }, + "load": function (evt, dlg) { + var icons = $("#" + that.id).parent().find(".ui-icon"); + for (var i = 0; i < icons.length; i++) { + //remove text from span added by vendor library + $(icons[i]).text(""); + } + }, + "beforeMinimize": function (evt, dlg) { + var label = that.name; + label = label.substring(0, 6); + that.$el.dialog({ title: label }); + }, + "beforeMaximize": function (evt, dlg) { + var divheight = that.size.height; + var divwidth = that.size.width; + that.previousMaxSize = { width: divwidth, height: divheight }; + }, + "minimize": function (evt, dlg) { + that.$el.dialog({ title: that.name }); + }, + "maximize": function (evt, dlg) { + that.setTrasparentBackground(false); + $(this).trigger('resizeEnd'); + var divheight = $(window).height(); + var divwidth = $(window).width(); + that.$el.dialog({ height: divheight, width: divwidth }); + that.maximize = true; + }, + "restore": function (evt, dlg) { + if (that.maximize) { + that.setSize(that.previousMaxSize.height, that.previousMaxSize.width); + $(this).trigger('restored', [that.id]); + } + that.setTrasparentBackground(that.previousMaxTransparency); + $(this).trigger('resizeEnd'); + that.maximize = false; + that.collapsed = false; + }, + "collapse": function (evt, dlg) { + that.collapsed = true; + } + }); + + //this.dialog.parent('.ui-dialog').prependTo($('#widgetContainer').find('div')); + //this.dialog.dialog('open') + + this.$el = $("#" + this.props.id); + var dialogParent = this.$el.parent(); + + + //add history + this.showHistoryIcon(true); + + //remove the jQuery UI icon + dialogParent.find("button.ui-dialog-titlebar-close").html(""); + dialogParent.find("button").append(""); + + + //Take focus away from close button + dialogParent.find("button.ui-dialog-titlebar-close").blur(); + + //add help button + this.addHelpButton(); + + // initialize content + this.size = this.state.defaultSize; + this.position = this.state.defaultPosition; + this.contextMenu = new GEPPETTO.ContextMenuView(); + this.historyMenu = new GEPPETTO.ContextMenuView(); + this.registeredEvents = []; + $(this.historyMenu.el).on('click', function (event) { + var itemId = $(event.target).attr('id'); + var registeredItem = that.historyMenu.getClickedItem(itemId); + if (registeredItem != null || registeredItem != undefined) { + var label = registeredItem["label"]; + that.title = label; + $("#" + that.id).parent().find(".ui-dialog-title").html(that.title); + } + }); + + window.addEventListener('resize', function (event) { + if (that.maximize) { + that.maximize = false; + that.setSize(window.innerHeight, window.innerWidth); + that.$el.trigger('resizeEnd', ["maximize"]); + that.maximize = true; + } + }); + }, + + /** + * Renders the widget dialog window + */ + render: function () { + return ( +
+ +
+ ) + }, + + /** + * Register event with widget + * + * @command registerEvent(event) + */ + registerEvent: function (event, callback) { + this.registeredEvents.push({ id: event, callback: callback }); + }, + + /** + * Unregister event with widget + * + * @command unregisterEvent(event) + */ + unregisterEvent: function (event) { + this.registeredEvents = _.reject(this.registeredEvents, function (el) { + return el.id === event + }); + }, + + getHelp: function () { + return '### Inline help not yet available for this widget! \n\n' + + 'Try the online documentation instead.'; + }, + + setController: function (controller) { + this.controller = controller; + }, + + showHistoryIcon: function (show) { + var that = this; + if (show && this.$el.parent().find(".history-icon").length == 0) { + this.addButtonToTitleBar($("
").click(function (event) { + that.showHistoryMenu(event); + event.stopPropagation(); + })); + } + else { + this.$el.parent().find(".history-icon").remove(); + } + } + }); + + return Widget; + } +}) diff --git a/src/main/webapp/js/pages/geppetto/geppetto.ejs b/src/main/webapp/js/pages/geppetto/geppetto.ejs index b992b4cb1..2fbdc419f 100644 --- a/src/main/webapp/js/pages/geppetto/geppetto.ejs +++ b/src/main/webapp/js/pages/geppetto/geppetto.ejs @@ -29,6 +29,8 @@ $content +
+
diff --git a/src/main/webapp/js/pages/geppetto/main.js b/src/main/webapp/js/pages/geppetto/main.js index cab18bfe1..11430821b 100644 --- a/src/main/webapp/js/pages/geppetto/main.js +++ b/src/main/webapp/js/pages/geppetto/main.js @@ -46,10 +46,9 @@ var GEPPETTO = require('geppetto'); require('../../components/ComponentFactory')(GEPPETTO); -GEPPETTO.ComponentFactory.loadSpinner(); - - +//GEPPETTO.ComponentFactory.addComponent('WIDGETCONTAINER',{}, document.getElementById('widgetContainer'), function(){GEPPETTO.widgetContainer=this;}) +GEPPETTO.ComponentFactory.loadSpinner(); jQuery(function () { window.GEPPETTO = require('geppetto'); From e69c563c6862f1adb661e250623a7c393b38ac5d Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 13 Mar 2017 07:48:12 +0000 Subject: [PATCH 005/339] tentative example with widget container --- .../js/components/widgets/WidgetContainer.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/webapp/js/components/widgets/WidgetContainer.js diff --git a/src/main/webapp/js/components/widgets/WidgetContainer.js b/src/main/webapp/js/components/widgets/WidgetContainer.js new file mode 100644 index 000000000..2af50762c --- /dev/null +++ b/src/main/webapp/js/components/widgets/WidgetContainer.js @@ -0,0 +1,30 @@ +define(function (require) { + + var React = require('react'); + + var widgetContainer = React.createClass({ + getInitialState: function () { + return { + items: [] + }; + }, + + addChildren: function (items) { + this.setState({ items: this.state.items.concat(items) }); + }, + + setChildren: function (items) { + this.setState({ items: items }); + }, + + render: function () { + return ( +
+ {this.state.items} +
+ ); + } + }); + + return widgetContainer; +}); \ No newline at end of file From 96bd1b5c3027609f1c519c244eee01b6ca89d2ba Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 13 Mar 2017 09:35:41 +0000 Subject: [PATCH 006/339] remove unused require --- src/main/webapp/js/components/widgets/NewWidget.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/webapp/js/components/widgets/NewWidget.js b/src/main/webapp/js/components/widgets/NewWidget.js index c218e2852..a81e229b7 100644 --- a/src/main/webapp/js/components/widgets/NewWidget.js +++ b/src/main/webapp/js/components/widgets/NewWidget.js @@ -12,8 +12,6 @@ define(function (require) { require("./jquery.dialogextend.min"); var React = require('react'); - var newWidget = require("../controls/newWidget/newWidget") - return function addWidget(WrappedComponent) { /** From 78b35ff05805c393c52aa1a79bd173e6d7b5432d Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 15 Mar 2017 18:01:45 +0000 Subject: [PATCH 007/339] added skeleton for view controller --- .../org.eclipse.wst.common.project.facet.core.xml | 3 +-- src/main/webapp/js/common/GEPPETTO.ViewController.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/main/webapp/js/common/GEPPETTO.ViewController.js diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml index 3817e58da..521457e61 100644 --- a/.settings/org.eclipse.wst.common.project.facet.core.xml +++ b/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -1,10 +1,9 @@ - - + diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js new file mode 100644 index 000000000..06704d939 --- /dev/null +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -0,0 +1,12 @@ +/** + * Manages view-state behaviour + * + */ +define(function(require) +{ + return function(GEPPETTO) { + GEPPETTO.ViewController = { + + }; + }; +}); From eb595517e36ff976720f58204362aad7171ef2a6 Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 15 Mar 2017 18:23:11 +0000 Subject: [PATCH 008/339] more skeleton TODO pseudo-code --- .../js/common/GEPPETTO.ViewController.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index 06704d939..2464d1ccc 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -6,7 +6,28 @@ define(function(require) { return function(GEPPETTO) { GEPPETTO.ViewController = { + /** + * Applies initial view state for project / experiment and sets up monitor + */ + applyView: function(projectView, experimentView){ + // TODO: apply project view + // TODO: apply experirment view + // TODO: foreach view item + // TODO: create widget if it doesn't exist + // TODO: apply properties using setView API + // TODO: setup monitor loop to track changes every 1000ms + }, + /** + * Monitors changes in the view + */ + monitorView: function(){ + // TODO: retrieve list of widgets (components in future) + // TODO: foreach widget/component in the list + // TODO: call getView API + // TODO: build view json with view state for all the widgets/components + // TODO: call a new setExperimentView method on the *new* web socket api + } }; }; }); From 03ce7a8d6eb91ed8f5e9f9dbb366c04f00dcd0b8 Mon Sep 17 00:00:00 2001 From: johnidol Date: Thu, 16 Mar 2017 12:48:11 +0000 Subject: [PATCH 009/339] frontend entry point + backend pipeline to set experiment view --- .../controllers/ConnectionHandler.java | 29 +++++++++++++++++++ .../controllers/WebsocketConnection.java | 7 +++++ .../frontend/messages/InboundMessages.java | 1 + .../geppettoProject/ExperimentsController.js | 19 ++++++++++++ 4 files changed, 56 insertions(+) diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index 373efd5b5..61147809c 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -719,6 +719,35 @@ public void setParameters(String requestID, Map modelParameters, } } } + + /** + * @param requestID + * @param view + * @param projectId + * @param experimentID + */ + public void setExperimentView(String requestID, String view, long projectId, long experimentID) + { + + IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); + IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); + if(!geppettoProject.isVolatile()) + { + try + { + geppettoManager.setExperimentView(view, experiment, geppettoProject); + } + catch(GeppettoExecutionException | GeppettoAccessException e) + { + error(e, "There was an error setting parameters"); + } + } + else + { + String msg = "Set Experiment View: Cannot set view on volatile experiment (not persisted)"; + error(new GeppettoExecutionException(msg), msg); + } + } /** * @param experimentID diff --git a/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java b/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java index 2aeeeed1a..282933c50 100644 --- a/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java +++ b/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java @@ -355,6 +355,12 @@ protected void onTextMessage(CharBuffer message) throws IOException connectionHandler.setParameters(requestID, receivedObject.modelParameters, receivedObject.projectId, receivedObject.experimentId); break; } + case SET_EXPERIMENT_VIEW: + { + ReceivedObject receivedObject = new Gson().fromJson(gmsg.data, ReceivedObject.class); + connectionHandler.setExperimentView(requestID, receivedObject.view, receivedObject.projectId, receivedObject.experimentId); + break; + } case LINK_DROPBOX: { parameters = new Gson().fromJson(gmsg.data, new TypeToken>() @@ -504,6 +510,7 @@ class ReceivedObject boolean watch; Map modelParameters; Map properties; + String view; } class GeppettoModelAPIParameters diff --git a/src/main/java/org/geppetto/frontend/messages/InboundMessages.java b/src/main/java/org/geppetto/frontend/messages/InboundMessages.java index b937f1571..daba613de 100644 --- a/src/main/java/org/geppetto/frontend/messages/InboundMessages.java +++ b/src/main/java/org/geppetto/frontend/messages/InboundMessages.java @@ -33,6 +33,7 @@ public enum InboundMessages { GET_WATCH("get_watch"), CLEAR_WATCHED_VARIABLES("clear_watch"), SET_PARAMETERS("set_parameters"), + SET_EXPERIMENT_VIEW("set_experiment_view"), LINK_DROPBOX("link_dropbox"), UNLINK_DROPBOX("unlink_drobpox"), diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index 2c8ecd983..f6166d4b2 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -193,6 +193,25 @@ define(function (require) { } }, + /** + * Sets view for this experiment. + * + * @command ExperimentNode.setView(view) + */ + setView: function (view) { + // go to server to persist only if experiment is persisted + if(Project.persisted){ + var parameters = {}; + parameters["experimentId"] = Project.getActiveExperiment().getId(); + parameters["projectId"] = Project.getId(); + parameters["view"] = view; + + GEPPETTO.MessageSocket.send("set_experiment_view", parameters); + } else { + // TODO: store view in local storage for this project/experiment/user + } + }, + watchVariables: function (variables, watch) { var watchedVariables = []; for (var i = 0; i < variables.length; i++) { From 0c153a881461a06365f00426ce6fbee7df1b5a0f Mon Sep 17 00:00:00 2001 From: johnidol Date: Fri, 17 Mar 2017 13:43:34 +0000 Subject: [PATCH 010/339] work in progress --- src/main/webapp/js/common/GEPPETTO.ViewController.js | 3 +++ src/main/webapp/js/communication/MessageHandler.js | 3 +++ src/main/webapp/js/pages/geppetto/GEPPETTO.js | 8 +------- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index 2464d1ccc..6686bde86 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -10,11 +10,14 @@ define(function(require) * Applies initial view state for project / experiment and sets up monitor */ applyView: function(projectView, experimentView){ + // TODO: stop monitor timer loop if there is alredy one active + // TODO: apply project view // TODO: apply experirment view // TODO: foreach view item // TODO: create widget if it doesn't exist // TODO: apply properties using setView API + // TODO: setup monitor loop to track changes every 1000ms }, diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 56b963074..d95a6714a 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -91,6 +91,9 @@ define(function (require) { GEPPETTO.SimulationHandler.loadExperiment(payload); GEPPETTO.trigger(GEPPETTO.Events.Experiment_loaded); + + // TODO: get project and experiment views + // TODO: GEPPETTO.ViewController.applyView(projectView, experimentView); if(window.Project.getActiveExperiment()!=null || undefined){ if (window.Project.getActiveExperiment().getScript() != undefined) { diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.js index c78dc9bd6..911ad988b 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.js @@ -86,7 +86,6 @@ define(function (require) { * containerp - HTML element to draw the 3D Scene * @returns {Boolean} */ - //AQP: Almost same code!!!!!!!!!!! init: function (containerp) { if (!isWebglEnabled) { Detector.addGetWebGLMessage(); @@ -109,12 +108,6 @@ define(function (require) { Detector.addGetWebGLMessage(); return false; } - // if (!Detector.webgl) { - // Detector.addGetWebGLMessage(); - // return false; - // } else { - // return true; - // } }, /** @@ -447,6 +440,7 @@ define(function (require) { require('../../components/interface/jsConsole/SandboxConsole')(GEPPETTO); require('../../common/GEPPETTO.Resources')(GEPPETTO); + require('../../common/GEPPETTO.ViewController')(GEPPETTO); require('./GEPPETTO.Events')(GEPPETTO); require('./GEPPETTO.Init')(GEPPETTO); require('../../components/interface/3dCanvas/GEPPETTO.SceneFactory')(GEPPETTO); From 938e0489bec01135106102f307fc41ebad5b85c1 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 20 Mar 2017 15:02:32 +0000 Subject: [PATCH 011/339] keep track of currently instantiated widgets / components in a map in component factory --- .../webapp/js/communication/MessageHandler.js | 9 ++++++-- .../webapp/js/components/ComponentFactory.js | 22 ++++++++++++++----- .../webapp/js/components/widgets/Widget.js | 14 +++++------- .../js/components/widgets/WidgetFactory.js | 5 +++++ .../js/components/widgets/WidgetsListener.js | 18 ++++++++++++--- 5 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index d95a6714a..c3385a4b5 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -92,8 +92,13 @@ define(function (require) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_loaded); - // TODO: get project and experiment views - // TODO: GEPPETTO.ViewController.applyView(projectView, experimentView); + // get project and experiment views + var projectView = window.Project.view; + var experimentView = null; + if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ + experimentView = window.Project.getActiveExperiment().view; + } + GEPPETTO.ViewController.applyView(projectView, experimentView); if(window.Project.getActiveExperiment()!=null || undefined){ if (window.Project.getActiveExperiment().getScript() != undefined) { diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index d9be7d004..219fa21e0 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -63,30 +63,40 @@ define(function (require) { 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', 'RAISEDBUTTON': 'controls/RaisedButton' - } - - + }; + GEPPETTO.ComponentFactory = { + + componentsMap: {}, loadSpinner:function(){ //We require this synchronously to properly show spinner when loading projects this.renderComponent(React.createFactory(spinner)(),document.getElementById("load-spinner")); }, - + + getComponents: function(){ + return this.componentsMap; + }, + addComponent: function(componentID, properties, container, callback){ var that=this; + require(["./" + components[componentID]], function(loadedModule){ - var component = React.createFactory(loadedModule)(properties) + var component = React.createFactory(loadedModule)(properties); var renderedComponent = that.renderComponent(component, container); if(callback!=undefined){ callback(renderedComponent); } + + // keep track of components in dictionary + // NOTE: after widgets/components refactoring we need to keep track of all components created + that.componentsMap[componentID] = renderedComponent; + return renderedComponent; }); }, - renderComponent: function(component, container){ //Let's create a dialog if (container == undefined){ diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index 589109a6e..a3e0ada2e 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -83,8 +83,8 @@ define(function (require) { * @returns {String} - Action Message */ destroy: function () { - this.$el.remove(); - this.destroyed=true; + this.$el.remove(); + this.destroyed = true; return this.name + " destroyed"; }, @@ -96,7 +96,7 @@ define(function (require) { * @returns {String} - Action Message */ hide: function () { - this.$el.dialog('close').dialogExtend(); + this.$el.dialog('close').dialogExtend(); this.visible = false; @@ -110,7 +110,7 @@ define(function (require) { * @returns {String} - Action Message */ show: function () { - this.$el.dialog('open').dialogExtend(); + this.$el.dialog('open').dialogExtend(); this.visible = true; //Unfocused close button @@ -654,8 +654,6 @@ define(function (require) { } } }) - } - ; + }; -}) -; +}); diff --git a/src/main/webapp/js/components/widgets/WidgetFactory.js b/src/main/webapp/js/components/widgets/WidgetFactory.js index 03c16bec6..cd323b6f1 100644 --- a/src/main/webapp/js/components/widgets/WidgetFactory.js +++ b/src/main/webapp/js/components/widgets/WidgetFactory.js @@ -97,6 +97,11 @@ define(function (require) { break; } + // add to component factory stack + // NOTE: this will go away after widgets/components refactoring + var components = GEPPETTO.ComponentFactory.getComponents(); + components[widget.getId()] = widget; + return widget; }, diff --git a/src/main/webapp/js/components/widgets/WidgetsListener.js b/src/main/webapp/js/components/widgets/WidgetsListener.js index 7c3c2524a..5c943a715 100644 --- a/src/main/webapp/js/components/widgets/WidgetsListener.js +++ b/src/main/webapp/js/components/widgets/WidgetsListener.js @@ -54,8 +54,10 @@ define(function (require) { GEPPETTO.Console.debugLog('added new observer'); } + var widgetSelector = $("#" + widgetID); + //registers remove handler for widget - $("#" + widgetID).on("remove", function () { + widgetSelector.on("remove", function () { //remove tags and delete object upon destroying widget GEPPETTO.Console.removeCommands(widgetID); @@ -67,10 +69,20 @@ define(function (require) { break; } } + + // remove from component factory dictionary + // NOTE: this will go away after widgets/components refactoring + var comps = GEPPETTO.ComponentFactory.getComponents(); + for(var c in comps){ + if(c == widgetID){ + delete comps[c]; + break; + } + } }); //register resize handler for widget - $("#" + widgetID).on("dialogresizestop", function (event, ui) { + widgetSelector.on("dialogresizestop", function (event, ui) { var height = ui.size.height; var width = ui.size.width; @@ -84,7 +96,7 @@ define(function (require) { }); //register drag handler for widget - $("#" + widgetID).on("dialogdragstop", function (event, ui) { + widgetSelector.on("dialogdragstop", function (event, ui) { var left = ui.position.left; var top = ui.position.top; From f58d7fd5aa6b73a38236568521f3d5944f823370 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Mon, 20 Mar 2017 16:48:22 +0000 Subject: [PATCH 012/339] Tutorials enhancement, work in progress. --- .../interface/tutorial/TutorialModule.js | 322 ++++++++++-------- .../interface/tutorial/tutorial.css | 13 +- .../components/widgets/contextMenuTemplate.js | 2 +- 3 files changed, 194 insertions(+), 143 deletions(-) diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index f5a987f9f..70a83179c 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -51,12 +51,6 @@ define(function (require) { $.cookie=require('js-cookie'); var Tutorial = React.createClass({ - stepIndex : 0, - totalSteps : 0, - dontShowTutorial : false, - started : false, - tutorialLoaded : false, - tutorialMap : {}, /** * Stores cookie to avoid showing tutorial next time at startup @@ -68,14 +62,8 @@ define(function (require) { getInitialState: function() { return { tutorialData: {}, - tutorialTitle : "", - tutorialMessage : "", - iconClass: "", - cookieClass : "checkbox-inline cookieTutorial", - prevBtnDisabled : true, - nextBtnLast : false, - nextBtnLabel : "", - visible : true, + activeTutorial : undefined, + currentStep : 0 }; }, @@ -83,86 +71,59 @@ define(function (require) { * Initial message at launch of tutorial */ start : function(){ - this.stepIndex = 0; - var title = this.state.tutorialData.steps[this.stepIndex].title; - var message = this.state.tutorialData.steps[this.stepIndex].message; - var action = this.state.tutorialData.steps[this.stepIndex].action; - var icon = this.state.tutorialData.steps[this.stepIndex].icon; + this.state.currentStep=0; this.open(true); - this.updateTutorialWindow(title,message,action,icon); + this.updateTutorialWindow(); this.started = true; }, + + getActiveTutorial : function(){ + return this.state.tutorialData[this.state.activeTutorial]; + }, - updateTutorialWindow : function(title, message, action,icon){ - var newIconClass = ""; - if(icon!=null || undefined){ - if(icon!=""){ - newIconClass = icon+" fa-3x"; - } - } + updateTutorialWindow : function(){ + var self = this; + var step = this.getActiveTutorial().steps[this.state.currentStep]; - //Hides checkbox after initial welcome window - var newCookieClass = this.state.cookieClass; - if(this.stepIndex==1){ - newCookieClass = "hide"; + if(step.content_url!=undefined){ + $.ajax({ + type: 'GET', + dataType: 'html', + url: step.content_url, + success: function (responseData, textStatus, jqXHR) { + step.message = responseData; + self.forceUpdate(); + }, + error: function (responseData, textStatus, errorThrown) { + throw ("Error retrieving tutorial: " + responseData + " with error " + errorThrown); + } + }); } - - var prevDisabled = true; - var lastStep = false; - var lastStepLabel = ""; - - if(this.stepIndex ==this.totalSteps){ - lastStep = true; - lastStepLabel = "Restart"; - prevDisabled = false; - }else if(this.stepIndex >= 1){ - prevDisabled = false; - }else if(this.stepIndex <=0){ - prevDisabled = true; + else{ + this.forceUpdate(); } - - this.setState({nextBtnLast : lastStep, nextBtnLabel : lastStepLabel, prevBtnDisabled : prevDisabled, tutorialTitle: title, iconClass: newIconClass, tutorialMessage : message, cookieClass : newCookieClass}); //execute action associated with message - if(action!=null || undefined){ - if(action!=""){ - eval(action); + if(step.action!=undefined){ + if(step.action!=""){ + eval(step.action); } } }, nextStep: function(){ - this.stepIndex++; - if(this.stepIndex <= this.totalSteps){ - var title = this.state.tutorialData.steps[this.stepIndex].title; - var message = this.state.tutorialData.steps[this.stepIndex].message; - var action = this.state.tutorialData.steps[this.stepIndex].action; - var icon = this.state.tutorialData.steps[this.stepIndex].icon; - - this.updateTutorialWindow(title,message,action,icon); - }else if(this.stepIndex>this.totalSteps){ - this.start(); + this.state.currentStep++; + if(this.state.currentStep <= this.getActiveTutorial().steps.length-1){ + this.updateTutorialWindow(); }else{ - this.close(); - this.started = false; + this.start(); } }, prevStep: function() { - if(this.stepIndex ==0){ - this.close(); - }else if(this.stepIndex > this.totalSteps){ - this.stepIndex = this.totalSteps; - }else{ - this.stepIndex--; - GEPPETTO.tutorialEnabled = false; - var title = this.state.tutorialData.steps[this.stepIndex].title; - var message = this.state.tutorialData.steps[this.stepIndex].message; - var action = this.state.tutorialData.steps[this.stepIndex].action; - var icon = this.state.tutorialData.steps[this.stepIndex].icon; - - this.updateTutorialWindow(title,message,action,icon); - } + this.state.currentStep--; + GEPPETTO.tutorialEnabled = false; + this.updateTutorialWindow(); }, close : function(){ @@ -175,30 +136,24 @@ define(function (require) { $('#tutorialMain').removeClass("hideTutorial"); } }, - - setTutorial : function(event, configurationURL){ - this.tutorialMap[event] = configurationURL; - }, - loadTutorial : function(tutorialData){ - this.setState({ tutorialData: tutorialData }); - this.totalSteps = this.state.tutorialData.steps.length-1; - if(this.state.visible){ - this.start(); - } - this.tutorialLoaded = true; + setTutorial : function(tutorialURL){ + this.state.tutorialData={}; + this.addTutorial(tutorialURL); }, - /** - * Extracts tutorial data for given configuration - */ - tutorialData : function(configurationURL){ + goToChapter: function(chapter){ + this.state.activeTutorial=chapter; + this.start(); + }, + + addTutorial : function(tutorialURL){ var self = this; $.ajax({ type: 'GET', dataType: 'json', - url: configurationURL, + url: tutorialURL, success: function (responseData, textStatus, jqXHR) { self.loadTutorial(responseData); }, @@ -207,22 +162,72 @@ define(function (require) { } }); }, + + loadTutorial : function(tutorialData){ + this.state.tutorialData[tutorialData.name] = tutorialData; + this.state.activeTutorial=tutorialData.name; + this.forceUpdate(); + if(!this.getCookie()){ + this.start(); + } + }, + + showChaptersMenu: function (event) { + var that = this; + var allTutorials = Object.keys(this.state.tutorialData); + if (allTutorials.length > 0) { + + var data = []; + for (var i = 0; i < allTutorials.length; i++) { + data.push({ + "label": allTutorials[i], + "action": ["GEPPETTO.Tutorial.goToChapter('"+allTutorials[i]+"')"], + "icon": null, + "position": i + }) + } + + this.chaptersMenu.show({ + top: event.pageY, + left: event.pageX + 1, + groups: data, + data: that + }); + } + if (event != null) { + event.preventDefault(); + } + return false; + }, + + + componentDidUpdate:function(){ + if(this.chaptersMenu==undefined){ + var that = this; + this.chaptersMenu = new GEPPETTO.ContextMenuView(); + + var button = $("
").on('click', function(event) { + that.showChaptersMenu(event); + event.stopPropagation(); + }); + + var dialog = $("#tutorialMain"); + dialog.find("div.ui-dialog-titlebar").prepend(button); + $(button).addClass("widget-title-bar-button"); + } + }, + componentDidMount:function(){ this.close(); var self = this; - + //launches specific tutorial is experiment is loaded GEPPETTO.on(GEPPETTO.Events.Model_loaded,function(){ if(!self.dontShowTutorial){ //default tutorial when user doesn't specify one for this event if(self.props.tutorialURL){ - var tutorialURL = self.props.tutorialURL; - if(self.tutorialMap[GEPPETTO.Events.Model_loaded]!=null || undefined){ - tutorialURL = self.tutorialMap[GEPPETTO.Events.Model_loaded]; - } - - self.tutorialData(tutorialURL); + self.addTutorial(self.props.tutorialURL); } else if(self.props.tutorialData){ self.loadTutorial(self.props.tutorialData); @@ -241,12 +246,14 @@ define(function (require) { self.open(false); }else{ //default tutorial when user doesn't specify one for this event - var tutorialURL = "/org.geppetto.frontend/geppetto/js/components/interface/tutorial/configuration/experiment_loaded_tutorial.json"; - if(self.tutorialMap[GEPPETTO.Events.Show_Tutorial]!=null || undefined){ - tutorialURL = self.tutorialMap[GEPPETTO.Events.Show_Tutorial]; + if(self.state.tutorialData=={}){ + self.setTutorial("/org.geppetto.frontend/geppetto/js/components/interface/tutorial/configuration/experiment_loaded_tutorial.json", "Geppetto tutorial"); + } + else{ + if(!this.getCookie()){ + this.start(); + } } - - self.tutorialData(tutorialURL); } } }); @@ -259,56 +266,91 @@ define(function (require) { GEPPETTO.Tutorial = this; if(GEPPETTO.ForegroundControls != undefined){ - GEPPETTO.ForegroundControls.refresh(); - } + GEPPETTO.ForegroundControls.refresh(); + } }, - /** - * Allows for using HTML tags as part of messages defined in .json configuration - */ - createTutorialMessage : function(){ - return {__html: this.state.tutorialMessage}; - }, getCookie : function(){ var ignoreTutorial = $.cookie('ignore_tutorial'); if(ignoreTutorial == undefined){ - //sets to string instead of boolean since $.cookie returns string even - //when storing as boolean - ignoreTutorial = false; + //sets to string instead of boolean since $.cookie returns string even when storing as boolean + return false; }else{ - ignoreTutorial = (ignoreTutorial === "true"); + return ignoreTutorial === "true"; } - - return ignoreTutorial; + }, + + getHTML(message){ + return {__html: message}; }, render: function () { + var ignoreTutorial = this.getCookie(); - - this.state.visible = !ignoreTutorial; - return
-
- {this.state.tutorialTitle} - -
-
-
-

-
-
-
- - -
- -
+ var activeTutorial = this.getActiveTutorial(); + if(activeTutorial!=undefined){ + + + var step = activeTutorial.steps[this.state.currentStep]; + + var iconClass = ""; + if(step.icon!=null && step.icon!=undefined && step.icon!=""){ + iconClass = step.icon+" fa-3x"; + } + + var prevDisabled = true; + var lastStep = false; + var lastStepLabel = ""; + + if(this.state.currentStep == activeTutorial.steps.length-1){ + lastStep = true; + lastStepLabel = "Restart"; + prevDisabled = false; + }else if(this.state.currentStep >= 1){ + prevDisabled = false; + }else if(this.state.currentStep <= 0){ + prevDisabled = true; + } -
+ var cookieClass=this.state.currentStep==0?"checkbox-inline cookieTutorial":"hide"; + + + var style={}; + if(activeTutorial.height!=undefined || activeTutorial.width!=undefined){ + style = { + width: activeTutorial.width+"px", + height: activeTutorial.height+"px" + }; + } + + return
+
+ {step.title} + +
+
+
+
+
+
+
+ + +
+ +
+ +
+ } + else{ + return null; + } } }); diff --git a/src/main/webapp/js/components/interface/tutorial/tutorial.css b/src/main/webapp/js/components/interface/tutorial/tutorial.css index dc7de72cd..f3c266bcb 100644 --- a/src/main/webapp/js/components/interface/tutorial/tutorial.css +++ b/src/main/webapp/js/components/interface/tutorial/tutorial.css @@ -10,13 +10,22 @@ display: block; z-index:500; } - +.invert{ + filter: invert(100%); +} +.center{ + margin: 0 auto; + display: block; + margin-bottom:20px; + margin-top:20px; +} .tutorial-message{ display: inline-block; width: auto; minHeight: 0px; maxHeight: none; - height: 180px; + height: 80%; + width:100%; color: #fc6320; } diff --git a/src/main/webapp/js/components/widgets/contextMenuTemplate.js b/src/main/webapp/js/components/widgets/contextMenuTemplate.js index e60aa7863..e64d8d9f6 100644 --- a/src/main/webapp/js/components/widgets/contextMenuTemplate.js +++ b/src/main/webapp/js/components/widgets/contextMenuTemplate.js @@ -1,6 +1,6 @@ define(function (require) { return { tplContextMenu: '', - tplContextMenuItems: '
  • <% if (icon != null && icon != undefined) { %><% } %>,<%= label %>
  • ', + tplContextMenuItems: '
  • <% if (icon != null && icon != undefined) { %><% } %> <%= label %>
  • ', } }); \ No newline at end of file From 32e032672d00cdbae52665c33e2c398d5e8b6585 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 20 Mar 2017 16:57:26 +0000 Subject: [PATCH 013/339] First view controller implementation - work in progress --- .../js/common/GEPPETTO.ViewController.js | 49 ++++++++++++++----- .../webapp/js/components/ComponentFactory.js | 1 - .../webapp/js/components/widgets/Widget.js | 38 +++++++++++++- .../geppettoProject/ExperimentsController.js | 2 +- .../js/geppettoProject/ProjectFactory.js | 2 + .../geppettoProject/model/ExperimentNode.js | 1 + .../js/geppettoProject/model/ProjectNode.js | 1 + 7 files changed, 78 insertions(+), 16 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index 6686bde86..92249bead 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -6,30 +6,55 @@ define(function(require) { return function(GEPPETTO) { GEPPETTO.ViewController = { + monitorInterval: undefined, + /** * Applies initial view state for project / experiment and sets up monitor */ applyView: function(projectView, experimentView){ - // TODO: stop monitor timer loop if there is alredy one active + // stop monitor timer loop if there is already one active + if(this.monitorInterval != undefined){ + clearInterval(this.monitorInterval); + } + + // apply project and experiment view + this.applyViewToComponentOrCreate(projectView.views); + this.applyViewToComponentOrCreate(experimentView.views); - // TODO: apply project view - // TODO: apply experirment view - // TODO: foreach view item - // TODO: create widget if it doesn't exist - // TODO: apply properties using setView API + // setup monitor loop to track changes every 1000ms + this.monitorInterval = setInterval(this.monitorView, 1000); + }, - // TODO: setup monitor loop to track changes every 1000ms + applyViewToComponentOrCreate: function(componentViews){ + if(componentViews != undefined){ + for(var cv in componentViews){ + // TODO: check if exists and create widget/component if not + var component = GEPPETTO.ComponentFactory.getComponents()[cv]; + if(component != undefined && typeof components[c].setView == 'function'){ + component.setView(componentViews[cv]); + } + } + } }, /** * Monitors changes in the view */ monitorView: function(){ - // TODO: retrieve list of widgets (components in future) - // TODO: foreach widget/component in the list - // TODO: call getView API - // TODO: build view json with view state for all the widgets/components - // TODO: call a new setExperimentView method on the *new* web socket api + // retrieve list of widgets (components in future) + var components = GEPPETTO.ComponentFactory.getComponents(); + var viewState = { views: {} }; + + for(var c in components){ + // call getView API if the method is exposed + if(typeof components[c].getView == 'function'){ + // build object literal with view state for all the widgets/components + viewState.views[c] = components[c].getView(); + } + } + + // persist view + GEPPETTO.ExperimentsController.setView(viewState); } }; }; diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 219fa21e0..a6644be9b 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -89,7 +89,6 @@ define(function (require) { } // keep track of components in dictionary - // NOTE: after widgets/components refactoring we need to keep track of all components created that.componentsMap[componentID] = renderedComponent; return renderedComponent; diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index a3e0ada2e..b6d0b4566 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -89,7 +89,6 @@ define(function (require) { }, /** - *ff * Hides the widget * * @command hide() @@ -641,7 +640,7 @@ define(function (require) { this.controller = controller; }, - showHistoryIcon : function(show){ + showHistoryIcon: function(show){ var that=this; if(show && this.$el.parent().find(".history-icon").length==0){ this.addButtonToTitleBar($("
    ").click(function (event) { @@ -652,6 +651,41 @@ define(function (require) { else{ this.$el.parent().find(".history-icon").remove(); } + }, + + /** + * Get view with attributes common to all widgets + * + * @returns {{size: {height: *, width: *}, position: {left: *, top: *}}} + */ + getView: function(){ + // get default stuff such as id, position and size + return { + size: { + height: this.size.height, + width: this.size.width + }, + position: { + left: this.position.left, + top: this.position.top + } + } + }, + + /** + * Set attributes common to all widgets - override for widget specific behaviour + * + * @param view + */ + setView: function(view){ + // set default stuff such as position and size + if(view.size != undefined){ + this.setSize(view.size.height, view.size.width); + } + + if(view.position != undefined){ + this.setPosition(view.position.left, view.position.top); + } } }) }; diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index f6166d4b2..d2b51869f 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -204,7 +204,7 @@ define(function (require) { var parameters = {}; parameters["experimentId"] = Project.getActiveExperiment().getId(); parameters["projectId"] = Project.getId(); - parameters["view"] = view; + parameters["view"] = JSON.stringify(view); GEPPETTO.MessageSocket.send("set_experiment_view", parameters); } else { diff --git a/src/main/webapp/js/geppettoProject/ProjectFactory.js b/src/main/webapp/js/geppettoProject/ProjectFactory.js index db61e1ef1..034c9af72 100644 --- a/src/main/webapp/js/geppettoProject/ProjectFactory.js +++ b/src/main/webapp/js/geppettoProject/ProjectFactory.js @@ -23,6 +23,7 @@ define(function (require) { name: project.name, type: project.type, id: project.id, + view: project.view, _metaType: GEPPETTO.Resources.PROJECT_NODE, }); @@ -56,6 +57,7 @@ define(function (require) { lastModified: node.lastModified, status: node.status, script: node.script, + view: node.view, _metaType: GEPPETTO.Resources.EXPERIMENT_NODE, }); diff --git a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js index bc45a8238..c01ae7a4c 100644 --- a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js +++ b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js @@ -18,6 +18,7 @@ define(['backbone'], function (require) { variables: null, parameters: null, script: "", + view: {}, writePermission: null, login: null, runPermission: null, diff --git a/src/main/webapp/js/geppettoProject/model/ProjectNode.js b/src/main/webapp/js/geppettoProject/model/ProjectNode.js index 3f09da2b3..298c9095c 100644 --- a/src/main/webapp/js/geppettoProject/model/ProjectNode.js +++ b/src/main/webapp/js/geppettoProject/model/ProjectNode.js @@ -21,6 +21,7 @@ define(['backbone'], function (require) { downloadPermission : null, readOnly : true, isPublicProject : false, + view: {}, /** * Initializes this project with passed attributes From 68aa2c118971e8eddaa096b6ea2f49a5f0b63337 Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 21 Mar 2017 14:52:16 +0000 Subject: [PATCH 014/339] tweak error message + other fixes --- .../org/geppetto/frontend/controllers/ConnectionHandler.java | 2 +- src/main/webapp/js/common/GEPPETTO.ViewController.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index 61147809c..e6a2454b0 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -739,7 +739,7 @@ public void setExperimentView(String requestID, String view, long projectId, lon } catch(GeppettoExecutionException | GeppettoAccessException e) { - error(e, "There was an error setting parameters"); + error(e, "There was an error setting experiment view"); } } else diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index 92249bead..5c478b3df 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -30,7 +30,7 @@ define(function(require) for(var cv in componentViews){ // TODO: check if exists and create widget/component if not var component = GEPPETTO.ComponentFactory.getComponents()[cv]; - if(component != undefined && typeof components[c].setView == 'function'){ + if(component != undefined && typeof component.setView == 'function'){ component.setView(componentViews[cv]); } } From 0497144a88987badabe5826b220e6a227931645d Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 21 Mar 2017 16:07:21 +0000 Subject: [PATCH 015/339] widgets store what kind of widgets they are based on GEPPETTO.Widgets enum --- .../webapp/js/components/widgets/Widget.js | 18 +++++++++++++++--- .../controllers/ButtonBarController.js | 2 +- .../controllers/ConnectivityController.js | 2 +- .../plot/controllers/PlotsController.js | 2 +- .../popup/controllers/PopupController.js | 2 +- .../controllers/StackViewerController.js | 2 +- .../controllers/TreeVisualiserControllerDAT.js | 3 ++- .../VariableVisualiserController.js | 2 +- 8 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index b6d0b4566..f7c12bc6c 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -36,6 +36,7 @@ define(function (require) { previousMaxSize : {}, maximize : false, collapsed : false, + widgetType: null, defaultSize : function(){return {height: 300, width: 350}}, defaultPosition : function(){return {left: "50%", top: "50%"}}, @@ -54,6 +55,7 @@ define(function (require) { this.position = this.defaultPosition(); this.contextMenu = new GEPPETTO.ContextMenuView(); this.historyMenu = new GEPPETTO.ContextMenuView(); + this.widgetType = options.widgetType; this.registeredEvents = []; var self = this; @@ -106,7 +108,7 @@ define(function (require) { * Opens widget dialog * * @command show() - * @returns {String} - Action Message + * @returns {Object} - Action Message */ show: function () { this.$el.dialog('open').dialogExtend(); @@ -118,6 +120,15 @@ define(function (require) { return this; }, + /** + * Returns widget type as defined in GEPPETTO.Widgets + * + * @returns {int} + */ + getWidgetType: function(){ + return this.widgetType; + }, + /** * Gets the name of the widget * @@ -179,7 +190,7 @@ define(function (require) { * @param {Integer} h - Minimum Height of the widget */ setMinHeight: function (h) { - this.$el.dialog('option', 'minHeight', h).dialogExtend(); + this.$el.dialog('option', 'minHeight', h).dialogExtend(); return this; }, @@ -188,7 +199,7 @@ define(function (require) { * @param {Integer} w - Minimum Width of the widget */ setMinWidth: function (w) { - this.$el.dialog('option', 'minWidth', w).dialogExtend(); + this.$el.dialog('option', 'minWidth', w).dialogExtend(); return this; }, @@ -661,6 +672,7 @@ define(function (require) { getView: function(){ // get default stuff such as id, position and size return { + widgetType: this.widgetType, size: { height: this.size.height, width: this.size.width diff --git a/src/main/webapp/js/components/widgets/buttonBar/controllers/ButtonBarController.js b/src/main/webapp/js/components/widgets/buttonBar/controllers/ButtonBarController.js index bae201176..3d35cd6b9 100644 --- a/src/main/webapp/js/components/widgets/buttonBar/controllers/ButtonBarController.js +++ b/src/main/webapp/js/components/widgets/buttonBar/controllers/ButtonBarController.js @@ -56,7 +56,7 @@ define(function (require) { //look for a name and id for the new widget var id = this.getAvailableWidgetId("ButtonBar", this.widgets); var name = id; - var vv = window[name] = new BuBar({id: id, name: name, visible: true}); + var vv = window[name] = new BuBar({id: id, name: name, visible: true, widgetType: GEPPETTO.Widgets.BUTTONBAR}); vv.help = function () { return GEPPETTO.Console.getObjectCommands(id); }; diff --git a/src/main/webapp/js/components/widgets/connectivity/controllers/ConnectivityController.js b/src/main/webapp/js/components/widgets/connectivity/controllers/ConnectivityController.js index 11fdb04a3..66632f2e1 100644 --- a/src/main/webapp/js/components/widgets/connectivity/controllers/ConnectivityController.js +++ b/src/main/webapp/js/components/widgets/connectivity/controllers/ConnectivityController.js @@ -37,7 +37,7 @@ define(function (require) { //create tree visualiser widget - var cnt = window[name] = new Connectivity({id: id, name: name, visible: false, width: 500, height: 500, controller: this}); + var cnt = window[name] = new Connectivity({id: id, name: name, visible: false, width: 500, height: 500, controller: this, widgetType: GEPPETTO.Widgets.CONNECTIVITY}); //create help command for connw cnt.help = function () { diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index ad6352d98..4f4914dbe 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -29,7 +29,7 @@ define(function (require) { var name = id; //create plotting widget - var p = window[name] = new Plot({id: id, name: name, visible: true}); + var p = window[name] = new Plot({id: id, name: name, visible: true, widgetType: GEPPETTO.Widgets.PLOT}); p.setController(this); //create help command for plot diff --git a/src/main/webapp/js/components/widgets/popup/controllers/PopupController.js b/src/main/webapp/js/components/widgets/popup/controllers/PopupController.js index 3bd0bc3ab..c6fbadaaf 100644 --- a/src/main/webapp/js/components/widgets/popup/controllers/PopupController.js +++ b/src/main/webapp/js/components/widgets/popup/controllers/PopupController.js @@ -30,7 +30,7 @@ define(function (require) { var name = id; //create popup widget - var p = window[name] = new Popup({id: id, name: name, visible: true, controller: this}); + var p = window[name] = new Popup({id: id, name: name, visible: true, controller: this, widgetType: GEPPETTO.Widgets.POPUP}); p.setController(this); p.setSize(394,490); //create help command for plot diff --git a/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js b/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js index 851a17325..dff7e081d 100644 --- a/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js +++ b/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js @@ -57,7 +57,7 @@ define(function (require) { //look for a name and id for the new widget var id = this.getAvailableWidgetId("StackViewer", this.widgets); var name = id; - var vv = window[name] = new Stack({id: id, name: name, visible: true}); + var vv = window[name] = new Stack({id: id, name: name, visible: true, widgetType: GEPPETTO.Widgets.STACKVIEWER}); vv.help = function () { return GEPPETTO.Console.getObjectCommands(id); }; diff --git a/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js b/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js index 2accceaaf..d58e7956c 100644 --- a/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js +++ b/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js @@ -34,7 +34,8 @@ define(function (require) { name: name, visible: true, width: 260, - height: 350 + height: 350, + widgetType: GEPPETTO.Widgets.TREEVISUALISERDAT }); // create help command for plot tvdat.help = function () { diff --git a/src/main/webapp/js/components/widgets/variablevisualiser/controllers/VariableVisualiserController.js b/src/main/webapp/js/components/widgets/variablevisualiser/controllers/VariableVisualiserController.js index 50d4d9595..9fbdf771e 100644 --- a/src/main/webapp/js/components/widgets/variablevisualiser/controllers/VariableVisualiserController.js +++ b/src/main/webapp/js/components/widgets/variablevisualiser/controllers/VariableVisualiserController.js @@ -56,7 +56,7 @@ define(function (require) { //look for a name and id for the new widget var id = this.getAvailableWidgetId("VarVis", this.widgets); var name = id; - var vv = window[name] = new VarVis({id: id, name: name, visible: true}); + var vv = window[name] = new VarVis({id: id, name: name, visible: true, widgetType: GEPPETTO.Widgets.VARIABLEVISUALISER}); vv.help = function () { return GEPPETTO.Console.getObjectCommands(id); }; From b95ded1ea2274653a81440b3e38c2a8fca25b347 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 21 Mar 2017 17:19:51 +0000 Subject: [PATCH 016/339] More work on tutorials, still work in progress. --- .../webapp/js/components/ComponentFactory.js | 8 ++- .../interface/tutorial/TutorialModule.js | 65 +++++++++---------- .../interface/tutorial/tutorial.css | 17 ++--- .../webapp/js/components/widgets/Widget.css | 2 +- .../webapp/js/components/widgets/Widget.js | 2 +- src/main/webapp/js/pages/geppetto/G.js | 2 +- 6 files changed, 43 insertions(+), 53 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index d9be7d004..cc8389d91 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -106,7 +106,7 @@ define(function (require) { $("#" + this.id).remove(); } } - }); + }); var dialogParent = dialog.parent(); var that = this; @@ -122,7 +122,11 @@ define(function (require) { container = dialog.get(0); } - return ReactDOM.render(component, container); + + var renderedComponent = ReactDOM.render(component, container); + renderedComponent.__container = container; + return renderedComponent; + } }; }; diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index 70a83179c..d1d435938 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -58,7 +58,13 @@ define(function (require) { dontShowAtStartup: function(val){ $.cookie('ignore_tutorial', true); }, - + + getDefaultProps: function() { + return { + id: "tutorial" + }; + }, + getInitialState: function() { return { tutorialData: {}, @@ -127,14 +133,13 @@ define(function (require) { }, close : function(){ - $('#tutorial').hide(); + $(this.__container).parent().hide(); }, open : function(started){ - $('#tutorial').show(); - if(!started){ - $('#tutorialMain').removeClass("hideTutorial"); - } + var p=$(this.__container).parent(); + p.show(); + p.effect("shake", {distance:5, times: 3}, 500); }, setTutorial : function(tutorialURL){ @@ -166,6 +171,7 @@ define(function (require) { loadTutorial : function(tutorialData){ this.state.tutorialData[tutorialData.name] = tutorialData; this.state.activeTutorial=tutorialData.name; + this.state.currentStep=0; this.forceUpdate(); if(!this.getCookie()){ this.start(); @@ -182,7 +188,7 @@ define(function (require) { data.push({ "label": allTutorials[i], "action": ["GEPPETTO.Tutorial.goToChapter('"+allTutorials[i]+"')"], - "icon": null, + "icon": "fa fa-bookmark", "position": i }) } @@ -212,7 +218,10 @@ define(function (require) { event.stopPropagation(); }); - var dialog = $("#tutorialMain"); + var dialog = $(this.__container).parent(); + var closeButton=dialog.find("button.ui-dialog-titlebar-close"); + closeButton.off("click"); + closeButton.click(this.close); dialog.find("div.ui-dialog-titlebar").prepend(button); $(button).addClass("widget-title-bar-button"); } @@ -294,43 +303,27 @@ define(function (require) { var step = activeTutorial.steps[this.state.currentStep]; + var dialog = $(this.__container).parent(); + dialog.find(".ui-dialog-title").html(step.title); var iconClass = ""; if(step.icon!=null && step.icon!=undefined && step.icon!=""){ iconClass = step.icon+" fa-3x"; } - var prevDisabled = true; - var lastStep = false; - var lastStepLabel = ""; - - if(this.state.currentStep == activeTutorial.steps.length-1){ - lastStep = true; - lastStepLabel = "Restart"; - prevDisabled = false; - }else if(this.state.currentStep >= 1){ - prevDisabled = false; - }else if(this.state.currentStep <= 0){ - prevDisabled = true; - } - + var prevDisabled = this.state.currentStep == 0; + var lastStep = this.state.currentStep == activeTutorial.steps.length-1; + var lastStepLabel = (this.state.currentStep == activeTutorial.steps.length-1)?"Restart":""; var cookieClass=this.state.currentStep==0?"checkbox-inline cookieTutorial":"hide"; - - var style={}; - if(activeTutorial.height!=undefined || activeTutorial.width!=undefined){ - style = { - width: activeTutorial.width+"px", - height: activeTutorial.height+"px" - }; + if(activeTutorial.height!=undefined){ + dialog.height(activeTutorial.height+"px"); + } + if(activeTutorial.width!=undefined){ + dialog.width(activeTutorial.width+"px"); } - return
    -
    - {step.title} - -
    -
    + return
    +
    diff --git a/src/main/webapp/js/components/interface/tutorial/tutorial.css b/src/main/webapp/js/components/interface/tutorial/tutorial.css index f3c266bcb..f0579fbdd 100644 --- a/src/main/webapp/js/components/interface/tutorial/tutorial.css +++ b/src/main/webapp/js/components/interface/tutorial/tutorial.css @@ -1,15 +1,3 @@ -.tutorial{ - position: absolute; - height: 300px; - width: 400px; - top:140px; - left: 0; - right: 0; - margin-left: auto; - margin-right: auto; - display: block; - z-index:500; -} .invert{ filter: invert(100%); } @@ -27,6 +15,11 @@ height: 80%; width:100%; color: #fc6320; + overflow: scroll!important; +} + +.tutorial-message a{ + cursor:pointer; } #ignoreTutorial { diff --git a/src/main/webapp/js/components/widgets/Widget.css b/src/main/webapp/js/components/widgets/Widget.css index 55c7f1352..f88ee4670 100644 --- a/src/main/webapp/js/components/widgets/Widget.css +++ b/src/main/webapp/js/components/widgets/Widget.css @@ -185,7 +185,7 @@ } .contextMenuView i { - width: 10%; + width: 13px; } .contextMenuView li { diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index 589109a6e..9f4553bf1 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -499,7 +499,7 @@ define(function (require) { * Perform a jquery ui effect to the widget */ perfomEffect: function (effect, options, speed){ - this.$el.parent().effect(effect, options, speed) + this.$el.parent().effect(effect, options, speed); }, /** diff --git a/src/main/webapp/js/pages/geppetto/G.js b/src/main/webapp/js/pages/geppetto/G.js index aa6ce4fa6..edb8a72fb 100644 --- a/src/main/webapp/js/pages/geppetto/G.js +++ b/src/main/webapp/js/pages/geppetto/G.js @@ -265,7 +265,7 @@ define(function (require) { toggleTutorial : function() { var returnMessage; - var modalVisible = $('#tutorial').is(':visible'); + var modalVisible = $('#tutorial_dialog').is(':visible'); if (modalVisible) { GEPPETTO.trigger(GEPPETTO.Events.Hide_Tutorial); From 38c62f5a4d18e3af0e97f72c6e7fffb6fd9af737 Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 21 Mar 2017 17:51:02 +0000 Subject: [PATCH 017/339] create widget if it doesn't exist already and the view has state for it --- .../webapp/js/common/GEPPETTO.ViewController.js | 15 +++++++++++++-- src/main/webapp/js/components/widgets/Widget.js | 5 +++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index 5c478b3df..af3183aa2 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -28,9 +28,20 @@ define(function(require) applyViewToComponentOrCreate: function(componentViews){ if(componentViews != undefined){ for(var cv in componentViews){ - // TODO: check if exists and create widget/component if not + // retrieve widget / component from factory var component = GEPPETTO.ComponentFactory.getComponents()[cv]; - if(component != undefined && typeof component.setView == 'function'){ + // widget / component not found, need to create it + if(component == undefined) { + // NOTE: this bit needs to be refactored once widgets/components are consolidated + if(componentViews[cv].widgetType != undefined){ + component = GEPPETTO.WidgetFactory.addWidget(componentViews[cv].widgetType); + } else if(componentViews[cv].componentType != undefined) { + // TODO: create component with component factory + } + } + + if(typeof component.setView == 'function'){ + // if the interface is exposed, set view component.setView(componentViews[cv]); } } diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index f7c12bc6c..4244719f7 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -673,6 +673,7 @@ define(function (require) { // get default stuff such as id, position and size return { widgetType: this.widgetType, + name: this.name, size: { height: this.size.height, width: this.size.width @@ -698,6 +699,10 @@ define(function (require) { if(view.position != undefined){ this.setPosition(view.position.left, view.position.top); } + + if(view.name != undefined){ + this.setName(view.name); + } } }) }; From 1f538725340d4b6e6a4332c8e3346c994666e7cf Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 21 Mar 2017 20:12:20 +0000 Subject: [PATCH 018/339] changes to retrieve view from project / experiment --- .../webapp/js/common/GEPPETTO.ViewController.js | 9 +++++++-- .../webapp/js/communication/MessageHandler.js | 4 ++-- .../webapp/js/geppettoProject/ProjectFactory.js | 6 +++--- .../js/geppettoProject/model/ExperimentNode.js | 16 ++++++++++++++++ .../js/geppettoProject/model/ProjectNode.js | 16 ++++++++++++++++ 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index af3183aa2..eda4f7a62 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -18,8 +18,13 @@ define(function(require) } // apply project and experiment view - this.applyViewToComponentOrCreate(projectView.views); - this.applyViewToComponentOrCreate(experimentView.views); + if(projectView != undefined && projectView.views != undefined){ + this.applyViewToComponentOrCreate(projectView.views); + } + + if(experimentView != undefined && experimentView.views != undefined) { + this.applyViewToComponentOrCreate(experimentView.views); + } // setup monitor loop to track changes every 1000ms this.monitorInterval = setInterval(this.monitorView, 1000); diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index c3385a4b5..237b50929 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -93,10 +93,10 @@ define(function (require) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_loaded); // get project and experiment views - var projectView = window.Project.view; + var projectView = window.Project.getView(); var experimentView = null; if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ - experimentView = window.Project.getActiveExperiment().view; + experimentView = window.Project.getActiveExperiment().getView(); } GEPPETTO.ViewController.applyView(projectView, experimentView); diff --git a/src/main/webapp/js/geppettoProject/ProjectFactory.js b/src/main/webapp/js/geppettoProject/ProjectFactory.js index 034c9af72..8089b5c7d 100644 --- a/src/main/webapp/js/geppettoProject/ProjectFactory.js +++ b/src/main/webapp/js/geppettoProject/ProjectFactory.js @@ -23,8 +23,8 @@ define(function (require) { name: project.name, type: project.type, id: project.id, - view: project.view, - _metaType: GEPPETTO.Resources.PROJECT_NODE, + view: (project.view != undefined) ? project.view.viewString : undefined, + _metaType: GEPPETTO.Resources.PROJECT_NODE }); p.persisted = persisted; @@ -57,7 +57,7 @@ define(function (require) { lastModified: node.lastModified, status: node.status, script: node.script, - view: node.view, + view: (node.view != undefined) ? node.view.viewString : undefined, _metaType: GEPPETTO.Resources.EXPERIMENT_NODE, }); diff --git a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js index c01ae7a4c..859b8f743 100644 --- a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js +++ b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js @@ -477,6 +477,22 @@ define(['backbone'], function (require) { } }, + /** + * Gets experiment view + * + * @returns {exports.view|{}} + */ + getView: function(){ + var viewsString = this.get('view'); + var views = undefined; + + if(viewsString != undefined){ + views = JSON.parse(viewsString); + } + + return views; + }, + /** * Print out formatted node */ diff --git a/src/main/webapp/js/geppettoProject/model/ProjectNode.js b/src/main/webapp/js/geppettoProject/model/ProjectNode.js index 298c9095c..20cc56e09 100644 --- a/src/main/webapp/js/geppettoProject/model/ProjectNode.js +++ b/src/main/webapp/js/geppettoProject/model/ProjectNode.js @@ -347,6 +347,22 @@ define(['backbone'], function (require) { } }, + /** + * Gets project view + * + * @returns {exports.view|{}} + */ + getView: function(){ + var viewsString = this.get('view'); + var views = undefined; + + if(viewsString != undefined){ + views = JSON.parse(viewsString); + } + + return views; + }, + /** * Print out formatted node */ From bf3d266d0381dca300461e2ac23b7794de036d11 Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 22 Mar 2017 18:18:56 +0000 Subject: [PATCH 019/339] popup widget implements setView / getView --- .../js/components/widgets/popup/Popup.js | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/popup/Popup.js b/src/main/webapp/js/components/widgets/popup/Popup.js index 7ac26ac6f..2609c70e0 100644 --- a/src/main/webapp/js/components/widgets/popup/Popup.js +++ b/src/main/webapp/js/components/widgets/popup/Popup.js @@ -92,6 +92,8 @@ define(function (require) { * @param {String} msg - The message that is displayed inside the widget */ setMessage: function (msg) { + this.data = msg; + $("#" + this.id).html(msg); GEPPETTO.Console.debugLog("Set new Message for " + this.id); @@ -346,6 +348,45 @@ define(function (require) { ReactDOM.unmountComponentAtNode(bar); } Widget.View.prototype.destroy.call(this); - } + }, + + getView: function(){ + var baseView = Widget.View.prototype.getView.call(this); + + // add data-type and data field + any other custom fields in the component-specific attribute + baseView.dataType = (typeof this.data == "string") ? "string" : "object"; + baseView.data = this.data; + baseView.componentSpecific = { + customHandlers: this.customHandlers + }; + + return baseView; + }, + + setView: function(view){ + // set base properties + Widget.View.prototype.setView.call(this, view); + + // set data + if(view.data != undefined){ + if(view.dataType == 'string'){ + this.setMessage(view.data); + } else { + // it's an object + this.setData(view.data); + } + } + + // set component specific stuff, only custom handlers for popup widget + if(view.componentSpecific != undefined && view.componentSpecific.customHandlers != undefined){ + for(var i=0; i Date: Thu, 23 Mar 2017 11:40:35 +0000 Subject: [PATCH 020/339] skeleton for plot to extend view state methods --- .../webapp/js/components/widgets/plot/Plot.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index f9f15c7f1..0c54a17e1 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -993,6 +993,23 @@ define(function (require) { this.updateAxis(dataY.getInstancePath()); this.resize(); return this; + }, + + getView: function(){ + var baseView = Widget.View.prototype.getView.call(this); + + // TODO: add data-type and data field + any other custom fields in the component-specific attribute + + return baseView; + }, + + setView: function(view){ + // set base properties + Widget.View.prototype.setView.call(this, view); + + // TODO: set data + + // TODO: set component specific stuff } }); From 396c385354e5f365be4eaeb9005fe46eba6720a7 Mon Sep 17 00:00:00 2001 From: johnidol Date: Thu, 23 Mar 2017 15:30:53 +0000 Subject: [PATCH 021/339] basic plot view-state example working --- .../webapp/js/components/widgets/plot/Plot.js | 27 ++++++++++++++++--- .../plot/controllers/PlotsController.js | 8 +++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 0c54a17e1..8556b6fd8 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -15,7 +15,7 @@ define(function (require) { var FileSaver = require('file-saver'); var pako = require('pako'); var JSZip = require("jszip"); - + var widgetUtility = require("../WidgetUtility"); widgetUtility.loadCss("geppetto/js/components/widgets/plot/Plot.css"); @@ -998,7 +998,14 @@ define(function (require) { getView: function(){ var baseView = Widget.View.prototype.getView.call(this); - // TODO: add data-type and data field + any other custom fields in the component-specific attribute + // add options, data-type and data field + // baseView.options = this.options; + // TODO: handle case of function node, data function and x,y data + baseView.dataType = 'object'; + baseView.data = this.datasets.map(function(item){ + // build array of paths + return item.name; + }); return baseView; }, @@ -1007,9 +1014,21 @@ define(function (require) { // set base properties Widget.View.prototype.setView.call(this, view); - // TODO: set data + if(view.options != undefined){ + this.setOptions(view.options); + } - // TODO: set component specific stuff + // set data + options based on data-type field + for (var index in view.data) { + var path = view.data[index]; + // TODO: project and experiment id could be any project and any experiment + this.controller.plotStateVariable( + Project.getId(), + Project.getActiveExperiment().getId(), + path, + this + ) + } } }); diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index 4f4914dbe..4b00a2034 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -151,6 +151,7 @@ define(function (require) { */ plotStateVariable: function(projectId, experimentId, path, plotWidget){ if(window.Project.getId() == projectId && window.Project.getActiveExperiment().getId() == experimentId){ + var self = this; // try to resolve path var inst = undefined; try { @@ -173,7 +174,7 @@ define(function (require) { var i = window.Instances.getInstance(path); if(plotWidget != undefined){ plotWidget.plotData(i); - this.updateLegend(plotWidget,i,projectId,experimentId); + self.updateLegend(plotWidget,i,projectId,experimentId); } else { G.addWidget(0).plotData(i).setName(path); } @@ -182,7 +183,6 @@ define(function (require) { GEPPETTO.ExperimentsController.getExperimentState(projectId, experimentId, [path], cb); } } else { - var self = this; // we are dealing with external instances, define re-usable callback for plotting external instances var plotExternalCallback = function(){ var i = GEPPETTO.ExperimentsController.getExternalInstance(projectId, experimentId, path); @@ -214,12 +214,12 @@ define(function (require) { if(window.Project.getId() == projectId){ if(window.Project.getActiveExperiment()!= null || undefined){ //variable belongs to same project,but different experiment - if(window.Project.getActiveExperiment.getId()!= experimentId){ + if(window.Project.getActiveExperiment().getId()!= experimentId){ legend = this.getLegendName(projectId,experimentId,instance,true); } } }else{ - //variablel belongs to different project and different experiment + //variable belongs to different project and different experiment var experimentPath = projectName + " - " + experimentName; legend = this.getLegendName(projectId,experimentId,instance,false); } From b40893c50944e623160bb73a8fdc817b40781bda Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 23 Mar 2017 20:00:03 +0100 Subject: [PATCH 022/339] Layout updates --- .../js/components/interface/tutorial/TutorialModule.js | 2 ++ .../js/components/interface/tutorial/tutorial.css | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index d1d435938..1cafe2994 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -224,6 +224,8 @@ define(function (require) { closeButton.click(this.close); dialog.find("div.ui-dialog-titlebar").prepend(button); $(button).addClass("widget-title-bar-button"); + $(this.__container).css("overflow","scroll"); + $(this.__container).css("height","93%"); } }, diff --git a/src/main/webapp/js/components/interface/tutorial/tutorial.css b/src/main/webapp/js/components/interface/tutorial/tutorial.css index f0579fbdd..180878d57 100644 --- a/src/main/webapp/js/components/interface/tutorial/tutorial.css +++ b/src/main/webapp/js/components/interface/tutorial/tutorial.css @@ -1,7 +1,7 @@ -.invert{ +.tutorial-message .invert{ filter: invert(100%); } -.center{ +.tutorial-message .center{ margin: 0 auto; display: block; margin-bottom:20px; @@ -19,9 +19,15 @@ } .tutorial-message a{ + color: #fc6320; cursor:pointer; + text-decoration:none; } +.tutorial-message a:hover{ + color: #fc401a; +} + #ignoreTutorial { width: auto; margin-top:15px; From 29eaaedd35b750adf6154444b886102a4b794167 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sat, 25 Mar 2017 23:58:19 +0000 Subject: [PATCH 023/339] add dicom viewer lib and ignore dcm files in webpack parsing --- src/main/webapp/package.json | 1 + src/main/webapp/webpack.config.js | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 2c89bc95c..7679f4710 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -12,6 +12,7 @@ "postinstall": "node -e \"var srcpath='../js/pages/geppetto/GEPPETTO.js'; var dstpath='node_modules/geppetto';var fs=require('fs'); fs.exists(dstpath,function(exists){if(!exists){fs.symlinkSync(srcpath, dstpath,'dir');}});\"" }, "devDependencies": { + "ami.js": "0.0.15", "anchorme": "^0.7.1", "babel-core": "^6.17.0", "babel-loader": "^6.2.5", diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index d9e1a59bd..821c0655d 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -143,12 +143,18 @@ module.exports = { presets: ['react', 'es2015'] } }, + { + test: /\.(js)$/, include: [/node_modules\/ami.js/], loader: ['babel-loader'], + query: { + presets: ['react', 'es2015'] + } + }, { test: /\.json$/, loader: "json-loader" }, { - test: /\.(py|png|svg|gif|css|jpg|md|hbs)$/, + test: /\.(py|png|svg|gif|css|jpg|md|hbs|dcm)$/, loader: 'ignore-loader' }, { test: /\.css$/, From 43c49550415880f6e37266339a7406344ae901be Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sat, 25 Mar 2017 23:58:44 +0000 Subject: [PATCH 024/339] add component to componentfactory --- src/main/webapp/js/components/ComponentFactory.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 59d2b0f75..cce1c0daf 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -39,7 +39,7 @@ define(function (require) { var spinner=require('./interface/loadingSpinner/LoadingSpinner.js'); var addWidget = require('./widgets/NewWidget.js'); - + //All the components potentially instantiable go here var components = { 'FORM':'interface/form/Form', @@ -64,7 +64,8 @@ define(function (require) { 'PYTHONCONSOLE': 'interface/pythonConsole/PythonConsole', 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', - 'RAISEDBUTTON': 'controls/RaisedButton' + 'RAISEDBUTTON': 'controls/RaisedButton', + 'DICOMVIEWER': 'interface/dicomViewer/DicomViewer' //'WIDGETCONTAINER': 'widgets/WidgetContainer' } From e269fd2a43ae68c88c2b4fa182912e89b33af7e8 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sat, 25 Mar 2017 23:59:48 +0000 Subject: [PATCH 025/339] add initial version --- .../interface/dicomViewer/DicomViewer.css | 60 +++ .../interface/dicomViewer/DicomViewer.js | 485 ++++++++++++++++++ 2 files changed, 545 insertions(+) create mode 100644 src/main/webapp/js/components/interface/dicomViewer/DicomViewer.css create mode 100644 src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.css b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.css new file mode 100644 index 000000000..43fea684e --- /dev/null +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.css @@ -0,0 +1,60 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2011, 2013 OpenWorm. + * http://openworm.org + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the MIT License + * which accompanies this distribution, and is available at + * http://opensource.org/licenses/MIT + * + * Contributors: + * OpenWorm - http://openworm.org/people.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + *******************************************************************************/ +.geppettoForm label { + width: 50%; + color: white; +} + +.geppettoForm input, +.geppettoForm select { + background: rgba(33, 29, 29, 0.4); + color: white; + width: 50%; + display: inline-block; + padding: 4px; + font-size: 13px; + line-height: 18px; + border: 1px solid #ccc; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.geppettoForm button{ + background-color: #fc6320 !important; + color: white !important; + float: right; + margin-top: -10px; + margin-bottom: 20px; + border: 0px; +} \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js new file mode 100644 index 000000000..6dedcd8a0 --- /dev/null +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -0,0 +1,485 @@ +define(function (require) { + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/interface/dicomViewer/DicomViewer.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + var React = require('react'); + window.THREE = require('three'); + var AMI = require('ami.js'); + + var dicomViewerComponent = React.createClass({ + + shouldComponentUpdate() { + return false; + }, + + componentDidMount: function () { + /* globals Stats, dat, AMI*/ + + var LoadersVolume = AMI.default.Loaders.Volume; + var CamerasOrthographic = AMI.default.Cameras.Orthographic; + var ControlsOrthographic = AMI.default.Controls.TrackballOrtho; + var HelpersLut = AMI.default.Helpers.Lut; + var HelpersStack = AMI.default.Helpers.Stack; + + // Shaders + // Data + var ShadersDataUniforms = AMI.default.Shaders.DataUniform; + var ShadersDataFragment = AMI.default.Shaders.DataFragment; + var ShadersDataVertex = AMI.default.Shaders.DataVertex; + // Layer + var ShadersLayerUniforms = AMI.default.Shaders.LayerUniform; + var ShadersLayerFragment = AMI.default.Shaders.LayerFragment; + var ShadersLayerVertex = AMI.default.Shaders.LayerVertex; + + // standard global variables + var controls; + var renderer; + var camera; + //var statsyay; + var threeD; + // + var sceneLayer0TextureTarget; + var sceneLayer1TextureTarget; + // + var sceneLayer0; + // + var lutLayer0; + var sceneLayer1; + var meshLayer1; + var uniformsLayer1; + var materialLayer1; + var lutLayer1; + var sceneLayerMix; + var meshLayerMix; + var uniformsLayerMix; + var materialLayerMix; + + var layerMix = { + opacity1: 1.0, + }; + + /** + * Init the scene + */ + function init() { + /** + * Animation loop + */ + function animate() { + // render + controls.update(); + // render first layer offscreen + renderer.render(sceneLayer0, camera, sceneLayer0TextureTarget, true); + // render second layer offscreen + renderer.render(sceneLayer1, camera, sceneLayer1TextureTarget, true); + // mix the layers and render it ON screen! + renderer.render(sceneLayerMix, camera); + //statsyay.update(); + + // request new frame + requestAnimationFrame(function () { + animate(); + }); + } + + // renderer + threeD = document.getElementById('dicomViewer'); + threeD.style.height = "300px"; + threeD.style.width = "350px"; + renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: true, + }); + renderer.setSize(threeD.clientWidth, threeD.clientHeight); + renderer.setClearColor(0x607D8B, 1); + + threeD.appendChild(renderer.domElement); + + // stats + // statsyay = new Stats(); + // threeD.appendChild(statsyay.domElement); + + // scene + sceneLayer0 = new THREE.Scene(); + sceneLayer1 = new THREE.Scene(); + sceneLayerMix = new THREE.Scene(); + + // render to texture!!!! + sceneLayer0TextureTarget = new THREE.WebGLRenderTarget( + threeD.clientWidth, + threeD.clientHeight, + { + minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat, + }); + + sceneLayer1TextureTarget = new THREE.WebGLRenderTarget( + threeD.clientWidth, + threeD.clientHeight, + { + minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat, + }); + + // camera + camera = new CamerasOrthographic( + threeD.clientWidth / -2, threeD.clientWidth / 2, + threeD.clientHeight / 2, threeD.clientHeight / -2, + 0.1, 10000); + + // controls + controls = new ControlsOrthographic(camera, threeD); + controls.staticMoving = true; + controls.noRotate = true; + camera.controls = controls; + + animate(); + } + + // init threeJS... + init(); + + var data = [ + '000183.dcm', '000219.dcm', '000117.dcm', + '000240.dcm', '000033.dcm', '000060.dcm', + '000211.dcm', '000081.dcm', '000054.dcm', + '000090.dcm', '000042.dcm', '000029.dcm', + '000239.dcm', '000226.dcm', '000008.dcm', + '000128.dcm', '000089.dcm', '000254.dcm', + '000208.dcm', '000047.dcm', '000067.dcm', + ]; + + var rawgit = + 'https://cdn.rawgit.com/FNNDSC/data/master/dicom/andrei_abdomen/'; + + var dataFullPath = data.map(function (v) { + return rawgit + 'data/' + v; + }); + + var labelmap = [ + '000000.dcm', + ]; + + var labelmapFullPath = labelmap.map(function (v) { + return rawgit + 'segmentation/' + v; + }); + + var files = dataFullPath.concat(labelmapFullPath); + + // load sequence for each file + // instantiate the loader + // it loads and parses the dicom image + var loader = new LoadersVolume(threeD); + + /** + * Build GUI + */ + function buildGUI(stackHelper) { + /** + * Update Layer 1 + */ + function updateLayer1() { + // update layer1 geometry... + if (meshLayer1) { + // dispose geometry first + meshLayer1.geometry.dispose(); + meshLayer1.geometry = stackHelper.slice.geometry; + meshLayer1.geometry.verticesNeedUpdate = true; + } + } + + /** + * Update layer mix + */ + function updateLayerMix() { + // update layer1 geometry... + if (meshLayerMix) { + sceneLayerMix.remove(meshLayerMix); + meshLayerMix.material.dispose(); + meshLayerMix.material = null; + meshLayerMix.geometry.dispose(); + meshLayerMix.geometry = null; + + // add mesh in this scene with right shaders... + meshLayerMix = new THREE.Mesh( + stackHelper.slice.geometry, materialLayerMix); + // go the LPS space + meshLayerMix.applyMatrix(stackHelper.stack._ijk2LPS); + + sceneLayerMix.add(meshLayerMix); + } + } + + var stack = stackHelper.stack; + + // var gui = new dat.GUI({ + // autoPlace: false, + // }); + + // var customContainer = document.getElementById('my-gui-container'); + // customContainer.appendChild(gui.domElement); + + // var layer0Folder = gui.addFolder('CT'); + // layer0Folder.add(stackHelper.slice, 'invert'); + + // var lutUpdate = layer0Folder.add( + // stackHelper.slice, 'lut', lutLayer0.lutsAvailable()); + // lutUpdate.onChange(function (value) { + // lutLayer0.lut = value; + // stackHelper.slice.lutTexture = lutLayer0.texture; + // }); + + // var indexUpdate = layer0Folder.add( + // stackHelper, 'index', 0, stack.dimensionsIJK.z - 1).step(1).listen(); + // indexUpdate.onChange(function () { + // updateLayer1(); + // updateLayerMix(); + // }); + + // layer0Folder.add( + // stackHelper.slice, 'interpolation', 0, 1).step(1).listen(); + + // layer0Folder.open(); + + // // layer mix folder + // var layerMixFolder = gui.addFolder('Segmentation'); + // var opacityLayerMix1 = layerMixFolder.add( + // layerMix, 'opacity1', 0, 1).step(0.01); + // opacityLayerMix1.onChange(function (value) { + // uniformsLayerMix.uOpacity1.value = value; + // }); + + // layerMixFolder.open(); + + // hook up callbacks + controls.addEventListener('OnScroll', function (e) { + if (e.delta > 0) { + if (stackHelper.index >= stack.dimensionsIJK.z - 1) { + return false; + } + stackHelper.index += 1; + } else { + if (stackHelper.index <= 0) { + return false; + } + stackHelper.index -= 1; + } + + updateLayer1(); + updateLayerMix(); + }); + + updateLayer1(); + updateLayerMix(); + + /** + * Handle window resize + */ + function onWindowResize() { + var threeD = document.getElementById('dicomViewer'); + camera.canvas = { + width: threeD.clientWidth, + height: threeD.clientHeight, + }; + camera.fitBox(2); + + renderer.setSize(threeD.clientWidth, threeD.clientHeight); + } + window.addEventListener('resize', onWindowResize, false); + onWindowResize(); + } + + /** + * Handle series + */ + function handleSeries() { + // + // + // first stack of first series + var mergedSeries = loader.data[0].mergeSeries(loader.data); + var stack = mergedSeries[0].stack[0]; + var stack2 = mergedSeries[1].stack[0]; + loader.free(); + loader = null; + + if (stack.modality === 'SEG') { + stack = mergedSeries[0].stack[0]; + stack2 = mergedSeries[1].stack[0]; + } + + var stackHelper = new HelpersStack(stack); + stackHelper.bbox.visible = false; + stackHelper.border.visible = false; + stackHelper.index = 10; + + sceneLayer0.add(stackHelper); + + // + // + // create labelmap.... + // we only care about the geometry.... + // get first stack from series + // prepare it + // * ijk2LPS transforms + // * Z spacing + // * etc. + // + stack2.prepare(); + // pixels packing for the fragment shaders now happens there + stack2.pack(); + + var textures2 = []; + for (var m = 0; m < stack2._rawData.length; m++) { + var tex = new THREE.DataTexture( + stack2.rawData[m], + stack2.textureSize, + stack2.textureSize, + stack2.textureType, + THREE.UnsignedByteType, + THREE.UVMapping, + THREE.ClampToEdgeWrapping, + THREE.ClampToEdgeWrapping, + THREE.NearestFilter, + THREE.NearestFilter); + tex.needsUpdate = true; + tex.flipY = true; + textures2.push(tex); + } + + // create material && mesh then add it to sceneLayer1 + uniformsLayer1 = ShadersDataUniforms.uniforms(); + uniformsLayer1.uTextureSize.value = stack2.textureSize; + uniformsLayer1.uTextureContainer.value = textures2; + uniformsLayer1.uWorldToData.value = stack2.lps2IJK; + uniformsLayer1.uNumberOfChannels.value = stack2.numberOfChannels; + uniformsLayer1.uPixelType.value = stack2.pixelType; + uniformsLayer1.uBitsAllocated.value = stack2.bitsAllocated; + uniformsLayer1.uWindowCenterWidth.value = + [stack2.windowCenter, stack2.windowWidth]; + uniformsLayer1.uRescaleSlopeIntercept.value = + [stack2.rescaleSlope, stack2.rescaleIntercept]; + uniformsLayer1.uDataDimensions.value = [stack2.dimensionsIJK.x, + stack2.dimensionsIJK.y, + stack2.dimensionsIJK.z]; + uniformsLayer1.uInterpolation.value = 0; + + // generate shaders on-demand! + var fs = new ShadersDataFragment(uniformsLayer1); + var vs = new ShadersDataVertex(); + materialLayer1 = new THREE.ShaderMaterial( + { + side: THREE.DoubleSide, + uniforms: uniformsLayer1, + vertexShader: vs.compute(), + fragmentShader: fs.compute(), + }); + + // add mesh in this scene with right shaders... + meshLayer1 = new THREE.Mesh(stackHelper.slice.geometry, materialLayer1); + // go the LPS space + meshLayer1.applyMatrix(stack._ijk2LPS); + sceneLayer1.add(meshLayer1); + + // Create the Mix layer + uniformsLayerMix = ShadersLayerUniforms.uniforms(); + uniformsLayerMix.uTextureBackTest0.value = sceneLayer0TextureTarget.texture; + uniformsLayerMix.uTextureBackTest1.value = sceneLayer1TextureTarget.texture; + + let fls = new ShadersLayerFragment(uniformsLayerMix); + let vls = new ShadersLayerVertex(); + materialLayerMix = new THREE.ShaderMaterial( + { + side: THREE.DoubleSide, + uniforms: uniformsLayerMix, + vertexShader: vls.compute(), + fragmentShader: fls.compute(), + transparent: true, + }); + + // add mesh in this scene with right shaders... + meshLayerMix = new THREE.Mesh(stackHelper.slice.geometry, materialLayer1); + // go the LPS space + meshLayerMix.applyMatrix(stack._ijk2LPS); + sceneLayerMix.add(meshLayerMix); + + // + // set camera + var worldbb = stack.worldBoundingBox(); + var lpsDims = new THREE.Vector3( + worldbb[1] - worldbb[0], + worldbb[3] - worldbb[2], + worldbb[5] - worldbb[4] + ); + + // box: {halfDimensions, center} + var box = { + center: stack.worldCenter().clone(), + halfDimensions: + new THREE.Vector3(lpsDims.x + 10, lpsDims.y + 10, lpsDims.z + 10), + }; + + // init and zoom + var canvas = { + width: threeD.clientWidth, + height: threeD.clientHeight, + }; + camera.directions = [stack.xCosine, stack.yCosine, stack.zCosine]; + camera.box = box; + camera.canvas = canvas; + camera.update(); + camera.fitBox(2); + + // CREATE LUT + lutLayer0 = new HelpersLut( + 'my-lut-canvases-l0', + 'default', + 'linear', + [[0, 0, 0, 0], [1, 1, 1, 1]], + [[0, 1], [1, 1]]); + lutLayer0.luts = HelpersLut.presetLuts(); + + lutLayer1 = new HelpersLut( + 'my-lut-canvases-l1', + 'default', + 'linear', + stack2.segmentationLUT, + stack2.segmentationLUTO, + true); + uniformsLayer1.uLut.value = 1; + uniformsLayer1.uTextureLUT.value = lutLayer1.texture; + + buildGUI(stackHelper); + } + + loader.load(files) + .then(function () { + handleSeries(); + }) + .catch(function (error) { + window.console.log('oops... something went wrong...'); + window.console.log(error); + }); + + }, + + render: function () { + return ( +
    +
    +
    +
    +
    CT
    +
    Segmentation
    +
    +
    + ) + } + }); + return dicomViewerComponent; +}); \ No newline at end of file From a97cec047ff07fa816ea6a7cc4ff8d4617d1f8e9 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 26 Mar 2017 13:39:32 +0100 Subject: [PATCH 026/339] simplified version and remove tests bits --- .../interface/dicomViewer/DicomViewer.js | 570 +++++------------- 1 file changed, 151 insertions(+), 419 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index 6dedcd8a0..b7fe45481 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -17,455 +17,191 @@ define(function (require) { }, componentDidMount: function () { - /* globals Stats, dat, AMI*/ + // VJS classes we will be using in this lesson var LoadersVolume = AMI.default.Loaders.Volume; var CamerasOrthographic = AMI.default.Cameras.Orthographic; var ControlsOrthographic = AMI.default.Controls.TrackballOrtho; - var HelpersLut = AMI.default.Helpers.Lut; var HelpersStack = AMI.default.Helpers.Stack; - // Shaders - // Data - var ShadersDataUniforms = AMI.default.Shaders.DataUniform; - var ShadersDataFragment = AMI.default.Shaders.DataFragment; - var ShadersDataVertex = AMI.default.Shaders.DataVertex; - // Layer - var ShadersLayerUniforms = AMI.default.Shaders.LayerUniform; - var ShadersLayerFragment = AMI.default.Shaders.LayerFragment; - var ShadersLayerVertex = AMI.default.Shaders.LayerVertex; - - // standard global variables - var controls; - var renderer; - var camera; - //var statsyay; - var threeD; - // - var sceneLayer0TextureTarget; - var sceneLayer1TextureTarget; - // - var sceneLayer0; - // - var lutLayer0; - var sceneLayer1; - var meshLayer1; - var uniformsLayer1; - var materialLayer1; - var lutLayer1; - var sceneLayerMix; - var meshLayerMix; - var uniformsLayerMix; - var materialLayerMix; - - var layerMix = { - opacity1: 1.0, - }; + // Setup renderer + var container = document.getElementById('dicomViewer'); + container.style.height = "300px"; + container.style.width = "350px"; + var renderer = new THREE.WebGLRenderer({ + antialias: true, + }); + renderer.setSize(container.offsetWidth, container.offsetHeight); + renderer.setClearColor(0x353535, 1); + renderer.setPixelRatio(window.devicePixelRatio); + container.appendChild(renderer.domElement); + + // Setup scene + var scene = new THREE.Scene(); + + // Setup camera + var camera = new CamerasOrthographic( + container.clientWidth / -2, container.clientWidth / 2, + container.clientHeight / 2, container.clientHeight / -2, + 0.1, 10000); + + // Setup controls + var controls = new ControlsOrthographic(camera, container); + controls.staticMoving = true; + controls.noRotate = true; + camera.controls = controls; /** - * Init the scene + * Handle window resize */ - function init() { - /** - * Animation loop - */ - function animate() { - // render - controls.update(); - // render first layer offscreen - renderer.render(sceneLayer0, camera, sceneLayer0TextureTarget, true); - // render second layer offscreen - renderer.render(sceneLayer1, camera, sceneLayer1TextureTarget, true); - // mix the layers and render it ON screen! - renderer.render(sceneLayerMix, camera); - //statsyay.update(); - - // request new frame - requestAnimationFrame(function () { - animate(); - }); - } - - // renderer - threeD = document.getElementById('dicomViewer'); - threeD.style.height = "300px"; - threeD.style.width = "350px"; - renderer = new THREE.WebGLRenderer({ - antialias: true, - alpha: true, - }); - renderer.setSize(threeD.clientWidth, threeD.clientHeight); - renderer.setClearColor(0x607D8B, 1); - - threeD.appendChild(renderer.domElement); - - // stats - // statsyay = new Stats(); - // threeD.appendChild(statsyay.domElement); - - // scene - sceneLayer0 = new THREE.Scene(); - sceneLayer1 = new THREE.Scene(); - sceneLayerMix = new THREE.Scene(); - - // render to texture!!!! - sceneLayer0TextureTarget = new THREE.WebGLRenderTarget( - threeD.clientWidth, - threeD.clientHeight, - { - minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat, - }); - - sceneLayer1TextureTarget = new THREE.WebGLRenderTarget( - threeD.clientWidth, - threeD.clientHeight, - { - minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat, - }); - - // camera - camera = new CamerasOrthographic( - threeD.clientWidth / -2, threeD.clientWidth / 2, - threeD.clientHeight / 2, threeD.clientHeight / -2, - 0.1, 10000); - - // controls - controls = new ControlsOrthographic(camera, threeD); - controls.staticMoving = true; - controls.noRotate = true; - camera.controls = controls; + function onWindowResize() { + camera.canvas = { + width: container.offsetWidth, + height: container.offsetHeight, + }; + camera.fitBox(2,2); - animate(); + renderer.setSize(container.offsetWidth, container.offsetHeight); } + window.addEventListener('resize', onWindowResize, false); - // init threeJS... - init(); - - var data = [ - '000183.dcm', '000219.dcm', '000117.dcm', - '000240.dcm', '000033.dcm', '000060.dcm', - '000211.dcm', '000081.dcm', '000054.dcm', - '000090.dcm', '000042.dcm', '000029.dcm', - '000239.dcm', '000226.dcm', '000008.dcm', - '000128.dcm', '000089.dcm', '000254.dcm', - '000208.dcm', '000047.dcm', '000067.dcm', - ]; - - var rawgit = - 'https://cdn.rawgit.com/FNNDSC/data/master/dicom/andrei_abdomen/'; + $("#" + this.props.containerid ).on("dialogresizestop", function (event, ui) { + camera.canvas = { + width: ui.size.width-30, + height: ui.size.height-30, + }; + camera.fitBox(2,2); - var dataFullPath = data.map(function (v) { - return rawgit + 'data/' + v; + renderer.setSize( ui.size.width-30, ui.size.height-30); + }); - var labelmap = [ - '000000.dcm', - ]; + /** + * Start animation loop + */ + function animate() { + controls.update(); + renderer.render(scene, camera); - var labelmapFullPath = labelmap.map(function (v) { - return rawgit + 'segmentation/' + v; - }); + // request new frame + requestAnimationFrame(function () { + animate(); + }); + } + animate(); - var files = dataFullPath.concat(labelmapFullPath); + // Setup loader + // var loader = new LoadersVolume(container); + // var file = + // 'https://cdn.rawgit.com/FNNDSC/data/master/nifti/adi_brain/adi_brain.nii.gz'; - // load sequence for each file // instantiate the loader // it loads and parses the dicom image - var loader = new LoadersVolume(threeD); - - /** - * Build GUI - */ - function buildGUI(stackHelper) { - /** - * Update Layer 1 - */ - function updateLayer1() { - // update layer1 geometry... - if (meshLayer1) { - // dispose geometry first - meshLayer1.geometry.dispose(); - meshLayer1.geometry = stackHelper.slice.geometry; - meshLayer1.geometry.verticesNeedUpdate = true; - } - } - - /** - * Update layer mix - */ - function updateLayerMix() { - // update layer1 geometry... - if (meshLayerMix) { - sceneLayerMix.remove(meshLayerMix); - meshLayerMix.material.dispose(); - meshLayerMix.material = null; - meshLayerMix.geometry.dispose(); - meshLayerMix.geometry = null; - - // add mesh in this scene with right shaders... - meshLayerMix = new THREE.Mesh( - stackHelper.slice.geometry, materialLayerMix); - // go the LPS space - meshLayerMix.applyMatrix(stackHelper.stack._ijk2LPS); - - sceneLayerMix.add(meshLayerMix); - } - } - - var stack = stackHelper.stack; - - // var gui = new dat.GUI({ - // autoPlace: false, - // }); - - // var customContainer = document.getElementById('my-gui-container'); - // customContainer.appendChild(gui.domElement); - - // var layer0Folder = gui.addFolder('CT'); - // layer0Folder.add(stackHelper.slice, 'invert'); - - // var lutUpdate = layer0Folder.add( - // stackHelper.slice, 'lut', lutLayer0.lutsAvailable()); - // lutUpdate.onChange(function (value) { - // lutLayer0.lut = value; - // stackHelper.slice.lutTexture = lutLayer0.texture; - // }); - - // var indexUpdate = layer0Folder.add( - // stackHelper, 'index', 0, stack.dimensionsIJK.z - 1).step(1).listen(); - // indexUpdate.onChange(function () { - // updateLayer1(); - // updateLayerMix(); - // }); - - // layer0Folder.add( - // stackHelper.slice, 'interpolation', 0, 1).step(1).listen(); - - // layer0Folder.open(); - - // // layer mix folder - // var layerMixFolder = gui.addFolder('Segmentation'); - // var opacityLayerMix1 = layerMixFolder.add( - // layerMix, 'opacity1', 0, 1).step(0.01); - // opacityLayerMix1.onChange(function (value) { - // uniformsLayerMix.uOpacity1.value = value; - // }); - - // layerMixFolder.open(); - - // hook up callbacks - controls.addEventListener('OnScroll', function (e) { - if (e.delta > 0) { - if (stackHelper.index >= stack.dimensionsIJK.z - 1) { - return false; - } - stackHelper.index += 1; - } else { - if (stackHelper.index <= 0) { - return false; - } - stackHelper.index -= 1; - } - - updateLayer1(); - updateLayerMix(); - }); + var loader = new LoadersVolume(container); + + var t2 = [ + '00010001.dcm', '00010019.dcm', '00010037.dcm', '00010055.dcm', '00010073.dcm', '00010091.dcm', '00010109.dcm', '00010127.dcm', + '00010002.dcm', '00010020.dcm', '00010038.dcm', '00010056.dcm', '00010074.dcm', '00010092.dcm', '00010110.dcm', '00010128.dcm', + '00010003.dcm', '00010021.dcm', '00010039.dcm', '00010057.dcm', '00010075.dcm', '00010093.dcm', '00010111.dcm', '00010129.dcm', + '00010004.dcm', '00010022.dcm', '00010040.dcm', '00010058.dcm', '00010076.dcm', '00010094.dcm', '00010112.dcm', '00010130.dcm', + '00010005.dcm', '00010023.dcm', '00010041.dcm', '00010059.dcm', '00010077.dcm', '00010095.dcm', '00010113.dcm', '00010131.dcm', + '00010006.dcm', '00010024.dcm', '00010042.dcm', '00010060.dcm', '00010078.dcm', '00010096.dcm', '00010114.dcm', '00010132.dcm', + '00010007.dcm', '00010025.dcm', '00010043.dcm', '00010061.dcm', '00010079.dcm', '00010097.dcm', '00010115.dcm', '00010133.dcm', + '00010008.dcm', '00010026.dcm', '00010044.dcm', '00010062.dcm', '00010080.dcm', '00010098.dcm', '00010116.dcm', '00010134.dcm', + '00010009.dcm', '00010027.dcm', '00010045.dcm', '00010063.dcm', '00010081.dcm', '00010099.dcm', '00010117.dcm', '00010135.dcm', + '00010010.dcm', '00010028.dcm', '00010046.dcm', '00010064.dcm', '00010082.dcm', '00010100.dcm', '00010118.dcm', '00010136.dcm', + '00010011.dcm', '00010029.dcm', '00010047.dcm', '00010065.dcm', '00010083.dcm', '00010101.dcm', '00010119.dcm', '00010137.dcm', + '00010012.dcm', '00010030.dcm', '00010048.dcm', '00010066.dcm', '00010084.dcm', '00010102.dcm', '00010120.dcm', '00010138.dcm', + '00010013.dcm', '00010031.dcm', '00010049.dcm', '00010067.dcm', '00010085.dcm', '00010103.dcm', '00010121.dcm', + '00010014.dcm', '00010032.dcm', '00010050.dcm', '00010068.dcm', '00010086.dcm', '00010104.dcm', '00010122.dcm', + '00010015.dcm', '00010033.dcm', '00010051.dcm', '00010069.dcm', '00010087.dcm', '00010105.dcm', '00010123.dcm', + '00010016.dcm', '00010034.dcm', '00010052.dcm', '00010070.dcm', '00010088.dcm', '00010106.dcm', '00010124.dcm', + '00010017.dcm', '00010035.dcm', '00010053.dcm', '00010071.dcm', '00010089.dcm', '00010107.dcm', '00010125.dcm', + '00010018.dcm', '00010036.dcm', '00010054.dcm', '00010072.dcm', '00010090.dcm', '00010108.dcm', '00010126.dcm', + ]; - updateLayer1(); - updateLayerMix(); - - /** - * Handle window resize - */ - function onWindowResize() { - var threeD = document.getElementById('dicomViewer'); - camera.canvas = { - width: threeD.clientWidth, - height: threeD.clientHeight, - }; - camera.fitBox(2); + var files = t2.map(function(v) { + return 'geppetto/extensions/geppetto-hm/samples/lowResPACStypical/' + v; + }); - renderer.setSize(threeD.clientWidth, threeD.clientHeight); - } - window.addEventListener('resize', onWindowResize, false); - onWindowResize(); - } + loader.load(files) + .then(function () { + // merge files into clean series/stack/frame structure + var series = loader.data[0].mergeSeries(loader.data); + var stack = series[0].stack[0]; + loader.free(); + loader = null; + // be carefull that series and target stack exist! + var stackHelper = new HelpersStack(stack); + // stackHelper.orientation = 2; + // stackHelper.index = 56; + + // tune bounding box + stackHelper.bbox.visible = false; + + // tune slice border + stackHelper.border.color = 0xFF9800; + // stackHelper.border.visible = false; + + scene.add(stackHelper); + + + // hook up callbacks + controls.addEventListener('OnScroll', function (e) { + console.log("scrolling"); + if (e.delta > 0) { + if (stackHelper.index >= stack.dimensionsIJK.z - 1) { + return false; + } + stackHelper.index += 1; + } else { + if (stackHelper.index <= 0) { + return false; + } + stackHelper.index -= 1; + } - /** - * Handle series - */ - function handleSeries() { - // - // - // first stack of first series - var mergedSeries = loader.data[0].mergeSeries(loader.data); - var stack = mergedSeries[0].stack[0]; - var stack2 = mergedSeries[1].stack[0]; - loader.free(); - loader = null; - - if (stack.modality === 'SEG') { - stack = mergedSeries[0].stack[0]; - stack2 = mergedSeries[1].stack[0]; - } - - var stackHelper = new HelpersStack(stack); - stackHelper.bbox.visible = false; - stackHelper.border.visible = false; - stackHelper.index = 10; - - sceneLayer0.add(stackHelper); - - // - // - // create labelmap.... - // we only care about the geometry.... - // get first stack from series - // prepare it - // * ijk2LPS transforms - // * Z spacing - // * etc. - // - stack2.prepare(); - // pixels packing for the fragment shaders now happens there - stack2.pack(); - - var textures2 = []; - for (var m = 0; m < stack2._rawData.length; m++) { - var tex = new THREE.DataTexture( - stack2.rawData[m], - stack2.textureSize, - stack2.textureSize, - stack2.textureType, - THREE.UnsignedByteType, - THREE.UVMapping, - THREE.ClampToEdgeWrapping, - THREE.ClampToEdgeWrapping, - THREE.NearestFilter, - THREE.NearestFilter); - tex.needsUpdate = true; - tex.flipY = true; - textures2.push(tex); - } - - // create material && mesh then add it to sceneLayer1 - uniformsLayer1 = ShadersDataUniforms.uniforms(); - uniformsLayer1.uTextureSize.value = stack2.textureSize; - uniformsLayer1.uTextureContainer.value = textures2; - uniformsLayer1.uWorldToData.value = stack2.lps2IJK; - uniformsLayer1.uNumberOfChannels.value = stack2.numberOfChannels; - uniformsLayer1.uPixelType.value = stack2.pixelType; - uniformsLayer1.uBitsAllocated.value = stack2.bitsAllocated; - uniformsLayer1.uWindowCenterWidth.value = - [stack2.windowCenter, stack2.windowWidth]; - uniformsLayer1.uRescaleSlopeIntercept.value = - [stack2.rescaleSlope, stack2.rescaleIntercept]; - uniformsLayer1.uDataDimensions.value = [stack2.dimensionsIJK.x, - stack2.dimensionsIJK.y, - stack2.dimensionsIJK.z]; - uniformsLayer1.uInterpolation.value = 0; - - // generate shaders on-demand! - var fs = new ShadersDataFragment(uniformsLayer1); - var vs = new ShadersDataVertex(); - materialLayer1 = new THREE.ShaderMaterial( - { - side: THREE.DoubleSide, - uniforms: uniformsLayer1, - vertexShader: vs.compute(), - fragmentShader: fs.compute(), }); - // add mesh in this scene with right shaders... - meshLayer1 = new THREE.Mesh(stackHelper.slice.geometry, materialLayer1); - // go the LPS space - meshLayer1.applyMatrix(stack._ijk2LPS); - sceneLayer1.add(meshLayer1); - - // Create the Mix layer - uniformsLayerMix = ShadersLayerUniforms.uniforms(); - uniformsLayerMix.uTextureBackTest0.value = sceneLayer0TextureTarget.texture; - uniformsLayerMix.uTextureBackTest1.value = sceneLayer1TextureTarget.texture; - - let fls = new ShadersLayerFragment(uniformsLayerMix); - let vls = new ShadersLayerVertex(); - materialLayerMix = new THREE.ShaderMaterial( - { - side: THREE.DoubleSide, - uniforms: uniformsLayerMix, - vertexShader: vls.compute(), - fragmentShader: fls.compute(), - transparent: true, - }); + // center camera and interactor to center of bouding box + // for nicer experience + // set camera + var worldbb = stack.worldBoundingBox(); + var lpsDims = new THREE.Vector3( + worldbb[1] - worldbb[0], + worldbb[3] - worldbb[2], + worldbb[5] - worldbb[4] + ); + + // box: {halfDimensions, center} + var box = { + center: stack.worldCenter().clone(), + halfDimensions: + new THREE.Vector3(lpsDims.x + 10, lpsDims.y + 10, lpsDims.z + 10), + }; - // add mesh in this scene with right shaders... - meshLayerMix = new THREE.Mesh(stackHelper.slice.geometry, materialLayer1); - // go the LPS space - meshLayerMix.applyMatrix(stack._ijk2LPS); - sceneLayerMix.add(meshLayerMix); - - // - // set camera - var worldbb = stack.worldBoundingBox(); - var lpsDims = new THREE.Vector3( - worldbb[1] - worldbb[0], - worldbb[3] - worldbb[2], - worldbb[5] - worldbb[4] - ); - - // box: {halfDimensions, center} - var box = { - center: stack.worldCenter().clone(), - halfDimensions: - new THREE.Vector3(lpsDims.x + 10, lpsDims.y + 10, lpsDims.z + 10), - }; + // init and zoom + var canvas = { + width: container.clientWidth, + height: container.clientHeight, + }; - // init and zoom - var canvas = { - width: threeD.clientWidth, - height: threeD.clientHeight, - }; - camera.directions = [stack.xCosine, stack.yCosine, stack.zCosine]; - camera.box = box; - camera.canvas = canvas; - camera.update(); - camera.fitBox(2); - - // CREATE LUT - lutLayer0 = new HelpersLut( - 'my-lut-canvases-l0', - 'default', - 'linear', - [[0, 0, 0, 0], [1, 1, 1, 1]], - [[0, 1], [1, 1]]); - lutLayer0.luts = HelpersLut.presetLuts(); - - lutLayer1 = new HelpersLut( - 'my-lut-canvases-l1', - 'default', - 'linear', - stack2.segmentationLUT, - stack2.segmentationLUTO, - true); - uniformsLayer1.uLut.value = 1; - uniformsLayer1.uTextureLUT.value = lutLayer1.texture; - - buildGUI(stackHelper); - } + camera.directions = [stack.xCosine, stack.yCosine, stack.zCosine]; + camera.box = box; + camera.canvas = canvas; + camera.update(); - loader.load(files) - .then(function () { - handleSeries(); + // Not working properly. See issue: https://github.com/FNNDSC/ami/issues/120 + camera.fitBox(2,2); }) .catch(function (error) { window.console.log('oops... something went wrong...'); window.console.log(error); }); + + + }, render: function () { @@ -473,10 +209,6 @@ define(function (require) {
    -
    -
    CT
    -
    Segmentation
    -
    ) } From d8517cacbc49618950d5fbe505503db8d0f7513f Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 28 Mar 2017 11:44:30 +0100 Subject: [PATCH 027/339] add format to string prototype so we can stop string concatenation --- src/main/webapp/js/common/GEPPETTO.Utility.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.Utility.js b/src/main/webapp/js/common/GEPPETTO.Utility.js index 26918a79c..6563a705d 100644 --- a/src/main/webapp/js/common/GEPPETTO.Utility.js +++ b/src/main/webapp/js/common/GEPPETTO.Utility.js @@ -193,14 +193,28 @@ define(function (require) { }()); /** - * Adding method to javascript string class to test - * if beginning of string matches another string being passed. + * Adding method to javascript string class to test if beginning of string matches another string being passed. */ if (typeof String.prototype.startsWith != 'function') { String.prototype.startsWith = function (str) { return this.substring(0, str.length) === str; } } + + /** + * Extend string prototype to enable posh string formatting + */ + if (!String.prototype.format) { + String.prototype.format = function() { + var args = arguments; + return this.replace(/{(\d+)}/g, function(match, number) { + return typeof args[number] != 'undefined' + ? args[number] + : match + ; + }); + }; + } }; From d3e5a21ed71acf4a1f1f29959a825adaa8142c7d Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 28 Mar 2017 12:27:19 +0100 Subject: [PATCH 028/339] first draft - persist views in local storage in absence of db or non-persisted project + enable/disable local storage --- .../webapp/js/communication/MessageHandler.js | 21 +++++++++++++++++-- .../geppettoProject/ExperimentsController.js | 15 +++++++++---- src/main/webapp/js/pages/geppetto/G.js | 14 +++++++++++++ .../webapp/js/pages/geppetto/GEPPETTO.Main.js | 1 + 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 237b50929..cc97a0f8a 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -92,14 +92,31 @@ define(function (require) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_loaded); - // get project and experiment views + // always get project and experiment views + // NOTE: even if project is not persisted we may have static default views on the project/experiment var projectView = window.Project.getView(); var experimentView = null; if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ experimentView = window.Project.getActiveExperiment().getView(); } + + // apply persisted views GEPPETTO.ViewController.applyView(projectView, experimentView); - + + // local storage views + if(!Project.persisted && GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ + // get project and experiment views from local storage if project is not persisted + var localProjectView = localStorage.getItem("{0}_view".format(Project.getId())); + var localExperimentView = null; + if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined) { + localExperimentView = localStorage.getItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId())); + } + + // apply local storage views + GEPPETTO.ViewController.applyView(localProjectView, localExperimentView); + } + + // after applying views, run script if any if(window.Project.getActiveExperiment()!=null || undefined){ if (window.Project.getActiveExperiment().getScript() != undefined) { G.runScript(window.Project.getActiveExperiment().getScript()); diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index d2b51869f..2d50b6f9e 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -194,9 +194,9 @@ define(function (require) { }, /** - * Sets view for this experiment. + * Sets view for this experiment / project. * - * @command ExperimentNode.setView(view) + * @command ExperimentsController.setView(view) */ setView: function (view) { // go to server to persist only if experiment is persisted @@ -207,8 +207,15 @@ define(function (require) { parameters["view"] = JSON.stringify(view); GEPPETTO.MessageSocket.send("set_experiment_view", parameters); - } else { - // TODO: store view in local storage for this project/experiment/user + } else if(GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ + // store view in local storage for this project/experiment/user + if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ + // no experiment active - save at project level + localStorage.setItem("{0}_view".format(Project.getId()), JSON.stringify(view)); + } else { + // experiment active - save at experiment level + localStorage.setItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId()), JSON.stringify(view)); + } } }, diff --git a/src/main/webapp/js/pages/geppetto/G.js b/src/main/webapp/js/pages/geppetto/G.js index aa6ce4fa6..fae8882f6 100644 --- a/src/main/webapp/js/pages/geppetto/G.js +++ b/src/main/webapp/js/pages/geppetto/G.js @@ -176,10 +176,24 @@ define(function (require) { return GEPPETTO.Utility.extractCommandsFromFile("geppetto/js/pages/geppetto/G.js", GEPPETTO.G, "G"); }, + /** + * Sets idle timeout, -1 for no timeout + * + * @param timeOut + */ setIdleTimeOut: function (timeOut) { GEPPETTO.Main.idleTime = timeOut; }, + /** + * Enables Geppetto local storage features (persist views with no db) + * + * @param enabled + */ + enableLocalStorage: function (enabled) { + GEPPETTO.Main.localStorageEnabled = enabled; + }, + /** * Removes widget from Geppetto * diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js index 464315a28..0f48b1be6 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js @@ -30,6 +30,7 @@ define(function(require) { disconnected: false, status: 0, statusWorker : null, + localStorageEnabled : false, getVisitorStatus: function() { return this.status; From a85a1ce24d758caea12541701eb67cd2919616ca Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 28 Mar 2017 15:17:52 +0100 Subject: [PATCH 029/339] give precedence to experiment view is present and non-empty, apply project view only if experiment view is not there or empty --- src/main/webapp/js/common/GEPPETTO.ViewController.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index eda4f7a62..12848bb49 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -17,13 +17,13 @@ define(function(require) clearInterval(this.monitorInterval); } - // apply project and experiment view - if(projectView != undefined && projectView.views != undefined){ - this.applyViewToComponentOrCreate(projectView.views); - } - - if(experimentView != undefined && experimentView.views != undefined) { + // if we have an experiment view for the active experiment with something, apply that and ignore the project view + if(experimentView != undefined && experimentView.views != undefined && Object.keys(experimentView.views).length > 0) { this.applyViewToComponentOrCreate(experimentView.views); + } else if(projectView != undefined && projectView.views != undefined){ + // if the experiment view is not there or is empty, apply project view (default) + // NOTE: this will effectively propagate the view from project to experiment in the next monitor cycle + this.applyViewToComponentOrCreate(projectView.views); } // setup monitor loop to track changes every 1000ms From e05756699ad6543deb1fc16833835d491d9a20aa Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 28 Mar 2017 15:26:04 +0100 Subject: [PATCH 030/339] ignore js build artifacts --- src/main/webapp/.gitignore | 3 ++- src/main/webapp/js/.gitignore | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/main/webapp/js/.gitignore diff --git a/src/main/webapp/.gitignore b/src/main/webapp/.gitignore index 147e046e0..3067a0668 100644 --- a/src/main/webapp/.gitignore +++ b/src/main/webapp/.gitignore @@ -1,2 +1,3 @@ /jsdocs/ -/build/* \ No newline at end of file +/build/* +/build/ diff --git a/src/main/webapp/js/.gitignore b/src/main/webapp/js/.gitignore new file mode 100644 index 000000000..372db871d --- /dev/null +++ b/src/main/webapp/js/.gitignore @@ -0,0 +1,6 @@ +/1.1.bundle.js +/2.2.bundle.js +/3.3.bundle.js +/admin.bundle.js +/main.bundle.js +/tests.bundle.js From 3c6e3f59a8b5386f0667a89bf9518ade0cd38169 Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 28 Mar 2017 18:28:53 +0100 Subject: [PATCH 031/339] set views in runtime object on client too --- src/main/webapp/js/common/GEPPETTO.ViewController.js | 8 ++++++-- src/main/webapp/js/communication/MessageHandler.js | 2 +- .../webapp/js/geppettoProject/ExperimentsController.js | 6 ++++-- .../webapp/js/geppettoProject/model/ExperimentNode.js | 10 ++++++++++ .../webapp/js/geppettoProject/model/ProjectNode.js | 10 ++++++++++ 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index 12848bb49..0bef5f403 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -69,8 +69,12 @@ define(function(require) } } - // persist view - GEPPETTO.ExperimentsController.setView(viewState); + // set view on experiment or project + if(window.Project.getActiveExperiment()!=null && window.Project.getActiveExperiment()!=undefined){ + window.Project.getActiveExperiment().setView(viewState); + } else { + window.Project.setView(viewState); + } } }; }; diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index cc97a0f8a..ba13aaff1 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -117,7 +117,7 @@ define(function (require) { } // after applying views, run script if any - if(window.Project.getActiveExperiment()!=null || undefined){ + if(window.Project.getActiveExperiment()!=null && window.Project.getActiveExperiment()!=undefined){ if (window.Project.getActiveExperiment().getScript() != undefined) { G.runScript(window.Project.getActiveExperiment().getScript()); } diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index 2d50b6f9e..6f60b9983 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -199,17 +199,19 @@ define(function (require) { * @command ExperimentsController.setView(view) */ setView: function (view) { + var activeExperiment = (window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined); // go to server to persist only if experiment is persisted if(Project.persisted){ var parameters = {}; - parameters["experimentId"] = Project.getActiveExperiment().getId(); + var experimentId = activeExperiment ? Project.getActiveExperiment().getId() : null; + parameters["experimentId"] = experimentId; parameters["projectId"] = Project.getId(); parameters["view"] = JSON.stringify(view); GEPPETTO.MessageSocket.send("set_experiment_view", parameters); } else if(GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ // store view in local storage for this project/experiment/user - if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ + if(!activeExperiment){ // no experiment active - save at project level localStorage.setItem("{0}_view".format(Project.getId()), JSON.stringify(view)); } else { diff --git a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js index 859b8f743..b298b90c9 100644 --- a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js +++ b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js @@ -477,6 +477,16 @@ define(['backbone'], function (require) { } }, + /** + * Set experiment view + * + * @param view + */ + setView: function(view){ + this.set('view', JSON.stringify(view)); + GEPPETTO.ExperimentsController.setView(view); + }, + /** * Gets experiment view * diff --git a/src/main/webapp/js/geppettoProject/model/ProjectNode.js b/src/main/webapp/js/geppettoProject/model/ProjectNode.js index 20cc56e09..f2dd39f16 100644 --- a/src/main/webapp/js/geppettoProject/model/ProjectNode.js +++ b/src/main/webapp/js/geppettoProject/model/ProjectNode.js @@ -347,6 +347,16 @@ define(['backbone'], function (require) { } }, + /** + * Set project view + * + * @param view + */ + setView: function(view){ + this.set('view', JSON.stringify(view)); + GEPPETTO.ExperimentsController.setView(view); + }, + /** * Gets project view * From 45a034ce848f02613b4e88d6287e663284a9405c Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 28 Mar 2017 18:47:59 +0100 Subject: [PATCH 032/339] more work on separating project view state from experiment view state --- .../webapp/js/communication/MessageHandler.js | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index ba13aaff1..eda06cf87 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -48,6 +48,18 @@ define(function (require) { messageHandler[messageTypes.PROJECT_LOADED] = function (payload) { GEPPETTO.SimulationHandler.loadProject(payload); + + // apply persisted project view if any + var projectView = window.Project.getView(); + GEPPETTO.ViewController.applyView(projectView, undefined); + + // local storage views + if(!Project.persisted && GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ + // get project and experiment views from local storage if project is not persisted + var localProjectView = localStorage.getItem("{0}_view".format(Project.getId())); + // apply local project views + GEPPETTO.ViewController.applyView(localProjectView, undefined); + } }; messageHandler[messageTypes.MODEL_LOADED] = function (payload) { @@ -92,28 +104,22 @@ define(function (require) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_loaded); - // always get project and experiment views - // NOTE: even if project is not persisted we may have static default views on the project/experiment - var projectView = window.Project.getView(); + // apply persisted views if any for experiment var experimentView = null; if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ experimentView = window.Project.getActiveExperiment().getView(); } - - // apply persisted views - GEPPETTO.ViewController.applyView(projectView, experimentView); + GEPPETTO.ViewController.applyView(undefined, experimentView); // local storage views if(!Project.persisted && GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ - // get project and experiment views from local storage if project is not persisted - var localProjectView = localStorage.getItem("{0}_view".format(Project.getId())); + // get experiment view from local storage if project is not persisted var localExperimentView = null; if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined) { localExperimentView = localStorage.getItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId())); } - - // apply local storage views - GEPPETTO.ViewController.applyView(localProjectView, localExperimentView); + // apply local experiment view + GEPPETTO.ViewController.applyView(undefined, localExperimentView); } // after applying views, run script if any From 17664c0e9834131660f049f4d7cf81315d423f64 Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 28 Mar 2017 19:10:01 +0100 Subject: [PATCH 033/339] le bug fix - server doesn't like null values for numerical fields --- src/main/webapp/js/geppettoProject/ExperimentsController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index 6f60b9983..3d8d81356 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -203,7 +203,7 @@ define(function (require) { // go to server to persist only if experiment is persisted if(Project.persisted){ var parameters = {}; - var experimentId = activeExperiment ? Project.getActiveExperiment().getId() : null; + var experimentId = activeExperiment ? Project.getActiveExperiment().getId() : -1; parameters["experimentId"] = experimentId; parameters["projectId"] = Project.getId(); parameters["view"] = JSON.stringify(view); From 4a2fd9d80deba938d57b68c4b827ec75b0d05e34 Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 28 Mar 2017 19:20:58 +0100 Subject: [PATCH 034/339] apply both project and experiment view after loading experiment in roder to cascade default project view to experiment --- src/main/webapp/js/communication/MessageHandler.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index eda06cf87..4b1878313 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -104,22 +104,24 @@ define(function (require) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_loaded); - // apply persisted views if any for experiment + // apply persisted views if any for both project and experiment + var projectView = window.Project.getView(); var experimentView = null; if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ experimentView = window.Project.getActiveExperiment().getView(); } - GEPPETTO.ViewController.applyView(undefined, experimentView); + GEPPETTO.ViewController.applyView(projectView, experimentView); // local storage views if(!Project.persisted && GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ - // get experiment view from local storage if project is not persisted + // get project and experiment view from local storage if project is not persisted + var localProjectView = localStorage.getItem("{0}_view".format(Project.getId())); var localExperimentView = null; if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined) { localExperimentView = localStorage.getItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId())); } // apply local experiment view - GEPPETTO.ViewController.applyView(undefined, localExperimentView); + GEPPETTO.ViewController.applyView(localProjectView, localExperimentView); } // after applying views, run script if any From 0ed987a3ce4147cccc59062a6c0a33edb7450d4c Mon Sep 17 00:00:00 2001 From: johnidol Date: Thu, 30 Mar 2017 15:25:55 +0100 Subject: [PATCH 035/339] fixes for views and local storage --- .../ComponentsInitialization.js | 2 ++ .../js/common/GEPPETTO.ViewController.js | 22 ++++++++++++ .../webapp/js/communication/MessageHandler.js | 35 ++----------------- src/main/webapp/webpack.config.js | 2 +- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index ad856e2b2..0c959a76e 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -1,5 +1,7 @@ define(function (require) { return function (GEPPETTO) { + // Enable local storage + G.enableLocalStorage(true); //Logo initialization GEPPETTO.ComponentFactory.addComponent('LOGO', {logo: 'gpt-gpt_logo'}, document.getElementById("geppettologo")); diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index 0bef5f403..c0cf873e2 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -8,6 +8,28 @@ define(function(require) GEPPETTO.ViewController = { monitorInterval: undefined, + resolveViews: function(){ + // apply persisted views if any for both project and experiment + var projectView = window.Project.getView(); + var experimentView = null; + if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ + experimentView = window.Project.getActiveExperiment().getView(); + } + this.applyView(projectView, experimentView); + + // local storage views + if(!Project.persisted && GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ + // get project and experiment view from local storage if project is not persisted + var localProjectView = JSON.parse(localStorage.getItem("{0}_view".format(Project.getId()))); + var localExperimentView = null; + if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined) { + localExperimentView = JSON.parse(localStorage.getItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId()))); + } + // apply local experiment view + this.applyView(localProjectView, localExperimentView); + } + }, + /** * Applies initial view state for project / experiment and sets up monitor */ diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 4b1878313..2c09a9d98 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -48,18 +48,7 @@ define(function (require) { messageHandler[messageTypes.PROJECT_LOADED] = function (payload) { GEPPETTO.SimulationHandler.loadProject(payload); - - // apply persisted project view if any - var projectView = window.Project.getView(); - GEPPETTO.ViewController.applyView(projectView, undefined); - - // local storage views - if(!Project.persisted && GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ - // get project and experiment views from local storage if project is not persisted - var localProjectView = localStorage.getItem("{0}_view".format(Project.getId())); - // apply local project views - GEPPETTO.ViewController.applyView(localProjectView, undefined); - } + GEPPETTO.ViewController.resolveViews(); }; messageHandler[messageTypes.MODEL_LOADED] = function (payload) { @@ -101,28 +90,9 @@ define(function (require) { messageHandler[messageTypes.EXPERIMENT_LOADED] = function (payload) { GEPPETTO.SimulationHandler.loadExperiment(payload); - GEPPETTO.trigger(GEPPETTO.Events.Experiment_loaded); - // apply persisted views if any for both project and experiment - var projectView = window.Project.getView(); - var experimentView = null; - if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined){ - experimentView = window.Project.getActiveExperiment().getView(); - } - GEPPETTO.ViewController.applyView(projectView, experimentView); - - // local storage views - if(!Project.persisted && GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ - // get project and experiment view from local storage if project is not persisted - var localProjectView = localStorage.getItem("{0}_view".format(Project.getId())); - var localExperimentView = null; - if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined) { - localExperimentView = localStorage.getItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId())); - } - // apply local experiment view - GEPPETTO.ViewController.applyView(localProjectView, localExperimentView); - } + GEPPETTO.ViewController.resolveViews(); // after applying views, run script if any if(window.Project.getActiveExperiment()!=null && window.Project.getActiveExperiment()!=undefined){ @@ -233,7 +203,6 @@ define(function (require) { messageHandler[messageTypes.UPDATE_MODEL_TREE] = function (payload) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_updated); GEPPETTO.Console.log("The model parameters were successfully updated."); - }; //received supported outputs from server diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index ca5b4b34d..fad209158 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -15,7 +15,7 @@ var getCLIParameter=function(param){ } } return ""; -} +}; console.log("\nThe arguments passed to HtmlWebpackPlugin are:\n"); From d7da8833876e8c5e0301babfcad4a5bf797c39eb Mon Sep 17 00:00:00 2001 From: johnidol Date: Thu, 30 Mar 2017 15:28:33 +0100 Subject: [PATCH 036/339] mistakes were made --- .../extensions/geppetto-default/ComponentsInitialization.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index 0c959a76e..d30b219dc 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -1,8 +1,5 @@ define(function (require) { return function (GEPPETTO) { - // Enable local storage - G.enableLocalStorage(true); - //Logo initialization GEPPETTO.ComponentFactory.addComponent('LOGO', {logo: 'gpt-gpt_logo'}, document.getElementById("geppettologo")); From a5cc9250ae835ffbd5c8b6b93ac9fd5047f3a083 Mon Sep 17 00:00:00 2001 From: johnidol Date: Thu, 30 Mar 2017 18:17:06 +0100 Subject: [PATCH 037/339] Use GEPPETTO.UserController.persistence to detect if we have the potential for persistence instead of Project.persisted --- .../ComponentsInitialization.js | 3 ++ .../js/common/GEPPETTO.ViewController.js | 8 ++++-- .../geppettoProject/ExperimentsController.js | 28 +------------------ 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index d30b219dc..bd2114201 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -1,5 +1,8 @@ define(function (require) { return function (GEPPETTO) { + // Enable local storage + G.enableLocalStorage(true); + //Logo initialization GEPPETTO.ComponentFactory.addComponent('LOGO', {logo: 'gpt-gpt_logo'}, document.getElementById("geppettologo")); diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index c0cf873e2..fc9b87c92 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -18,11 +18,15 @@ define(function(require) this.applyView(projectView, experimentView); // local storage views - if(!Project.persisted && GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ + var useLocalStorage = GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined"); + if ( + (!Project.persisted && GEPPETTO.UserController.persistence && useLocalStorage) || + (!GEPPETTO.UserController.persistence && useLocalStorage) + ) { // get project and experiment view from local storage if project is not persisted var localProjectView = JSON.parse(localStorage.getItem("{0}_view".format(Project.getId()))); var localExperimentView = null; - if(window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined) { + if (window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined) { localExperimentView = JSON.parse(localStorage.getItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId()))); } // apply local experiment view diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index 3d8d81356..b2dd4adfb 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -137,35 +137,9 @@ define(function (require) { parameters["experimentId"] = experiment.id; parameters["projectId"] = experiment.getParent().getId(); - /* ATTEMPT TO NOT REMOVE THE WIDGETS OPTIONALLY WHEN SWTICHING EXPERIMENT - * To work we need to mark the old widget as inactive so that we don't try to update them - if($("div.ui-widget").length>0){ - GEPPETTO.FE.inputDialog( - "Do you want to remove the existing widgets?", - "You are loading a different experiment, do you want to retain the existing widgets? Note that the underlining data will cease existing as you are switching to a different experiment.", - "Remove widgets", - function(){ - GEPPETTO.trigger('show_spinner', GEPPETTO.Resources.LOADING_EXPERIMENT); - GEPPETTO.WidgetsListener.update(GEPPETTO.WidgetsListener.WIDGET_EVENT_TYPE.DELETE); - GEPPETTO.MessageSocket.send("load_experiment", parameters); - }, - "Keep widgets", - function(){ - GEPPETTO.trigger('show_spinner', GEPPETTO.Resources.LOADING_EXPERIMENT); - GEPPETTO.MessageSocket.send("load_experiment", parameters); - } - ); - } - else{ - GEPPETTO.trigger('show_spinner', GEPPETTO.Resources.LOADING_EXPERIMENT); - GEPPETTO.WidgetsListener.update(GEPPETTO.WidgetsListener.WIDGET_EVENT_TYPE.DELETE); - GEPPETTO.MessageSocket.send("load_experiment", parameters); - }*/ - GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.LOADING_EXPERIMENT); GEPPETTO.WidgetsListener.update(GEPPETTO.WidgetsListener.WIDGET_EVENT_TYPE.DELETE); GEPPETTO.MessageSocket.send("load_experiment", parameters); - }, /** @@ -201,7 +175,7 @@ define(function (require) { setView: function (view) { var activeExperiment = (window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined); // go to server to persist only if experiment is persisted - if(Project.persisted){ + if(Project.persisted && GEPPETTO.UserController.persistence){ var parameters = {}; var experimentId = activeExperiment ? Project.getActiveExperiment().getId() : -1; parameters["experimentId"] = experimentId; From e08189f7e3b37e00cf7240cf092671ab100e1e94 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Fri, 31 Mar 2017 10:20:19 -0700 Subject: [PATCH 038/339] work in progress for downloading project --- .../controllers/ConnectionHandler.java | 23 ++++ .../controllers/WebsocketConnection.java | 9 ++ .../frontend/messages/InboundMessages.java | 1 + .../frontend/messages/OutboundMessages.java | 1 + .../messages/TransportMessageFactory.java | 3 + .../webapp/js/common/GEPPETTO.Resources.js | 2 + .../webapp/js/communication/MessageHandler.js | 5 + .../downloadProject/DownloadProjectButton.js | 124 ++++++++++++++++++ .../js/geppettoProject/model/ProjectNode.js | 15 +++ 9 files changed, 183 insertions(+) create mode 100644 src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index 373efd5b5..d126810da 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -1394,4 +1394,27 @@ public void experimentError(String titleMessage, String logMessage, Exception ex websocketConnection.sendMessage(null, OutboundMessages.ERROR_RUNNING_EXPERIMENT, jsonError); } + + public void downloadProject(String requestID, long projectId) { + try + { + IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); + + if(geppettoProject != null) + { + + geppettoManager.downloadProject(geppettoProject); + websocketConnection.sendMessage(requestID, OutboundMessages.DOWNLOAD_PROJECT,null); + } + else + { + error(null, "Error persisting project " + projectId + "."); + } + } + catch(GeppettoExecutionException | GeppettoAccessException e) + { + error(e, "Error persisting project"); + } + + } } diff --git a/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java b/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java index 2aeeeed1a..731871765 100644 --- a/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java +++ b/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java @@ -229,6 +229,15 @@ protected void onTextMessage(CharBuffer message) throws IOException connectionHandler.makeProjectPublic(requestID, projectId,isPublic); break; } + case DOWNLOAD_PROJECT: + { + parameters = new Gson().fromJson(gmsg.data, new TypeToken>() + { + }.getType()); + projectId = Long.parseLong(parameters.get("projectId")); + connectionHandler.downloadProject(requestID, projectId); + break; + } case SAVE_PROJECT_PROPERTIES: { ReceivedObject receivedObject = new Gson().fromJson(gmsg.data, ReceivedObject.class); diff --git a/src/main/java/org/geppetto/frontend/messages/InboundMessages.java b/src/main/java/org/geppetto/frontend/messages/InboundMessages.java index b937f1571..748446a76 100644 --- a/src/main/java/org/geppetto/frontend/messages/InboundMessages.java +++ b/src/main/java/org/geppetto/frontend/messages/InboundMessages.java @@ -18,6 +18,7 @@ public enum InboundMessages { SAVE_PROJECT_PROPERTIES("save_project_properties"), PERSIST_PROJECT("persist_project"), MAKE_PROJECT_PUBLIC("make_project_public"), + DOWNLOAD_PROJECT("download_project"), //EXPERIMENT MESSAGES NEW_EXPERIMENT("new_experiment"), diff --git a/src/main/java/org/geppetto/frontend/messages/OutboundMessages.java b/src/main/java/org/geppetto/frontend/messages/OutboundMessages.java index a8b621863..02c10aff0 100644 --- a/src/main/java/org/geppetto/frontend/messages/OutboundMessages.java +++ b/src/main/java/org/geppetto/frontend/messages/OutboundMessages.java @@ -41,6 +41,7 @@ public enum OutboundMessages { DELETE_EXPERIMENT("experiment_deleted"), GET_EXPERIMENT_STATE("get_experiment_state"), PROJECT_PERSISTED("project_persisted"), + DOWNLOAD_PROJECT("download_project"), PROJECT_PROPS_SAVED("project_props_saved"), PROJECT_MADE_PUBLIC("project_made_public"), EXPERIMENT_PROPS_SAVED("experiment_props_saved"), diff --git a/src/main/java/org/geppetto/frontend/messages/TransportMessageFactory.java b/src/main/java/org/geppetto/frontend/messages/TransportMessageFactory.java index 6f2a62388..6e6a2a412 100644 --- a/src/main/java/org/geppetto/frontend/messages/TransportMessageFactory.java +++ b/src/main/java/org/geppetto/frontend/messages/TransportMessageFactory.java @@ -83,6 +83,9 @@ public static GeppettoTransportMessage getTransportMessage(String requestID, Out case DOWNLOAD_MODEL: params.add(new SimpleEntry("update", (update!=null) ? update : EMPTY_STRING)); break; + case DOWNLOAD_PROJECT: + params.add(new SimpleEntry("update", (update!=null) ? update : EMPTY_STRING)); + break; case GET_EXPERIMENT_STATE: params.add(new SimpleEntry("update", (update!=null) ? update : EMPTY_STRING)); break; diff --git a/src/main/webapp/js/common/GEPPETTO.Resources.js b/src/main/webapp/js/common/GEPPETTO.Resources.js index 6fc91eb79..073af4cdd 100644 --- a/src/main/webapp/js/common/GEPPETTO.Resources.js +++ b/src/main/webapp/js/common/GEPPETTO.Resources.js @@ -305,6 +305,8 @@ define(function (require) { RETRIEVING_VISUALIZATION_TREE: 'Visualization tree: ', DOWNLOADING_MODEL: 'Downloading model as ', + + DOWNLOADING_PROJECT: 'Downloading Project', ERROR_DOWNLOADING_MODEL: "Error downloading model", diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 56b963074..9558ac905 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -10,6 +10,7 @@ define(function (require) { EXPERIMENT_UPDATE: "experiment_update", SIMULATION_CONFIGURATION: "project_configuration", PROJECT_LOADED: "project_loaded", + DOWNLOAD_PROJECT : "download_project", MODEL_LOADED: "geppetto_model_loaded", PROJECT_PROPS_SAVED: "project_props_saved", EXPERIMENT_PROPS_SAVED: "experiment_props_saved", @@ -240,6 +241,10 @@ define(function (require) { messageHandler[messageTypes.DOWNLOAD_MODEL] = function (payload) { GEPPETTO.Console.log("Model downloaded succesfully"); }; + + messageHandler[messageTypes.DOWNLOAD_PROJECT] = function (payload) { + GEPPETTO.Console.log("Project downloaded succesfully"); + }; messageHandler[messageTypes.RESULTS_UPLOADED] = function (payload) { GEPPETTO.Console.log("Results uploaded succesfully"); diff --git a/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js b/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js new file mode 100644 index 000000000..16cce9006 --- /dev/null +++ b/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js @@ -0,0 +1,124 @@ +/******************************************************************************* + * + * Copyright (c) 2011, 2016 OpenWorm. http://openworm.org + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the MIT License which accompanies this + * distribution, and is available at http://opensource.org/licenses/MIT + * + * Contributors: OpenWorm - http://openworm.org/people.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ******************************************************************************/ + +define(function(require) { + + var React = require('react'); + var GEPPETTO = require('geppetto'); + + $.widget.bridge('uitooltip', $.ui.tooltip); + + var downloadProjectComp = React.createClass({ + attachTooltip: function(){ + var self = this; + $('.DownloadProjectButton').uitooltip({ + position: { my: "right center", at : "left-25 center"}, + tooltipClass: "tooltip-persist", + show: { + effect: "slide", + direction: "right", + delay: 200 + }, + hide: { + effect: "slide", + direction: "right", + delay: 200 + }, + content: function () { + return self.state.tooltipLabel; + }, + }); + }, + + getInitialState: function() { + return { + disableButton : false, + tooltipLabel : "Click here to download this project!", + icon: "fa fa-download" + }; + }, + + componentDidMount: function() { + + var self = this; + + GEPPETTO.on(GEPPETTO.Events.Project_downloaded, function(){ + self.setState({disableButton: false}); + // update contents of what's displayed on tooltip + $('.DownloadProjectButton').uitooltip({content: "The project was downloaded!", + position: { my: "right center", at : "left center"}}); + $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); + self.setState({disableButton: true}); + }); + + GEPPETTO.on('spin_persist', function() { + self.setState({icon:"fa fa-download fa-spin"}); + }.bind($(".downloadProjectButton"))); + + GEPPETTO.on('stop_spin_persist', function() { + self.setState({icon:"fa fa-download"}); + }.bind($(".downloadProjectButton"))); + + + self.attachTooltip(); + + if(window.Project!=undefined){ + this.setState(this.evaluateState()); + } + }, + + evaluateState:function(){ + return {disableButton:window.Project.persisted || !GEPPETTO.UserController.hasPermission(GEPPETTO.Resources.WRITE_PROJECT)}; + }, + + clickEvent : function(){ + var self = this; + // update contents of what's displayed on tooltip + $('.DownloadProjectButton').uitooltip({content: "The project is getting downloaded..."}); + $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); + self.setState({disableButton: true}); + GEPPETTO.Console.executeCommand("Project.download();"); + GEPPETTO.trigger("spin_persist"); + }, + + render: function () { + return ( +
    + +
    + ); + } + }); + + return downloadProjectComp; + +}); \ No newline at end of file diff --git a/src/main/webapp/js/geppettoProject/model/ProjectNode.js b/src/main/webapp/js/geppettoProject/model/ProjectNode.js index 3f09da2b3..a16ec193a 100644 --- a/src/main/webapp/js/geppettoProject/model/ProjectNode.js +++ b/src/main/webapp/js/geppettoProject/model/ProjectNode.js @@ -346,6 +346,21 @@ define(['backbone'], function (require) { } }, + /** + * Download this project. + * + * @command ProjectNode.downloadModel(format) + * * @param {String} name - File format to download + */ + downloadProject : function(path, format) { + var parameters = {}; + parameters["projectId"] = this.getId(); + GEPPETTO.MessageSocket.send("download_project", parameters); + + var formatMessage = (format=="")?"default format":format; + return GEPPETTO.Resources.DOWNLOADING_PROJECT + formatMessage; + }, + /** * Print out formatted node */ From 5b215d2dcb8faacad12626d9827248da7f225f6c Mon Sep 17 00:00:00 2001 From: jrmartin Date: Fri, 31 Mar 2017 11:06:19 -0700 Subject: [PATCH 039/339] a bit more work on downloading project --- .../extensions/geppetto-default/ComponentsInitialization.js | 3 +++ src/main/webapp/js/communication/MessageHandler.js | 1 + src/main/webapp/js/components/ComponentFactory.js | 3 ++- src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js | 1 + src/main/webapp/js/pages/geppetto/geppetto.ejs | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index ad856e2b2..4439fdcdf 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -37,6 +37,9 @@ define(function (require) { //Save initialization GEPPETTO.ComponentFactory.addComponent('SAVECONTROL', {}, document.getElementById("SaveButton")); + + //Download Project Button initialization + GEPPETTO.ComponentFactory.addComponent('DownloadProjectButton', {}, document.getElementById("DownloadProjectButton")); //Simulation controls initialization GEPPETTO.ComponentFactory.addComponent('SIMULATIONCONTROLS', {}, document.getElementById("sim-toolbar")); diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 9558ac905..918fb2254 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -243,6 +243,7 @@ define(function (require) { }; messageHandler[messageTypes.DOWNLOAD_PROJECT] = function (payload) { + GEPPETTO.trigger(GEPPETTO.Events.Project_downloaded); GEPPETTO.Console.log("Project downloaded succesfully"); }; diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index d9be7d004..7806550d1 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -62,7 +62,8 @@ define(function (require) { 'PYTHONCONSOLE': 'interface/pythonConsole/PythonConsole', 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', - 'RAISEDBUTTON': 'controls/RaisedButton' + 'RAISEDBUTTON': 'controls/RaisedButton', + 'DownloadProjectButton':'interface/downloadProject/DownloadProjectButton' } diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js index 45e399c11..81626a1dc 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js @@ -20,6 +20,7 @@ define(function (require) { Experiment_over: "experiment:over", Project_loading: "project:loading", Project_loaded: "project:loaded", + Project_downloaded : "project:downloaded", Model_loaded: "model:loaded", Experiment_loaded: "experiment:loaded", ModelTree_populated: "experiment:modeltreepopulated", diff --git a/src/main/webapp/js/pages/geppetto/geppetto.ejs b/src/main/webapp/js/pages/geppetto/geppetto.ejs index 648f2c4c7..3e839009f 100644 --- a/src/main/webapp/js/pages/geppetto/geppetto.ejs +++ b/src/main/webapp/js/pages/geppetto/geppetto.ejs @@ -29,6 +29,7 @@
    +
    From 015698245b7af8cb6abde10cea2a8d6e89a9fafd Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 3 Apr 2017 12:51:56 +0100 Subject: [PATCH 040/339] Widgets can be created as stateless from G.addWidget, stateless widgets are not persisted to views --- .../js/common/GEPPETTO.ViewController.js | 8 +++++-- .../webapp/js/components/widgets/Widget.js | 3 ++- .../js/components/widgets/WidgetFactory.js | 23 ++++++++++--------- .../controllers/ButtonBarController.js | 11 +++++++-- .../controllers/ConnectivityController.js | 12 +++++++--- .../plot/controllers/PlotsController.js | 11 +++++++-- .../popup/controllers/PopupController.js | 11 +++++++-- .../controllers/StackViewerController.js | 11 +++++++-- .../TreeVisualiserControllerDAT.js | 14 +++++------ .../VariableVisualiserController.js | 11 +++++++-- src/main/webapp/js/pages/geppetto/G.js | 13 ++++++++--- 11 files changed, 91 insertions(+), 37 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index fc9b87c92..0badea37c 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -88,8 +88,12 @@ define(function(require) var viewState = { views: {} }; for(var c in components){ - // call getView API if the method is exposed - if(typeof components[c].getView == 'function'){ + // call getView API if the method is exposed and the component is not stateless + if( + typeof components[c].getView == 'function' && + components[c].stateless != undefined && + !components[c].stateless + ) { // build object literal with view state for all the widgets/components viewState.views[c] = components[c].getView(); } diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index 4244719f7..4a1acbef5 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -37,6 +37,7 @@ define(function (require) { maximize : false, collapsed : false, widgetType: null, + stateless: false, defaultSize : function(){return {height: 300, width: 350}}, defaultPosition : function(){return {left: "50%", top: "50%"}}, @@ -56,6 +57,7 @@ define(function (require) { this.contextMenu = new GEPPETTO.ContextMenuView(); this.historyMenu = new GEPPETTO.ContextMenuView(); this.widgetType = options.widgetType; + this.stateless = (options.stateless != undefined) ? options.stateless : false; this.registeredEvents = []; var self = this; @@ -619,7 +621,6 @@ define(function (require) { //add help button this.addHelpButton(); - }, /** diff --git a/src/main/webapp/js/components/widgets/WidgetFactory.js b/src/main/webapp/js/components/widgets/WidgetFactory.js index cd323b6f1..271c004bb 100644 --- a/src/main/webapp/js/components/widgets/WidgetFactory.js +++ b/src/main/webapp/js/components/widgets/WidgetFactory.js @@ -52,41 +52,42 @@ define(function (require) { //WIDGETNAMEController: null /** - * Adds widget to Geppetto + * Adds widgets to Geppetto * - * @param {GEPPETTO.Widgets} - * widgetType - Widget to add to Geppetto + * @param widgetType - what type of widget gets added + * @param isStateless - boolean that controls if the widget is stateless and persisted in views or not (false by default) + * @returns {*} */ - addWidget: function (widgetType) { + addWidget: function (widgetType, isStateless) { var widget = null; switch (widgetType) { //create plotting widget case GEPPETTO.Widgets.PLOT: - widget = this.getController(GEPPETTO.Widgets.PLOT).addPlotWidget(); + widget = this.getController(GEPPETTO.Widgets.PLOT).addPlotWidget(isStateless); break; //create popup widget case GEPPETTO.Widgets.POPUP: - widget = this.getController(GEPPETTO.Widgets.POPUP).addPopupWidget(); + widget = this.getController(GEPPETTO.Widgets.POPUP).addPopupWidget(isStateless); break; //create tree visualiser DAT widget case GEPPETTO.Widgets.TREEVISUALISERDAT: - widget = this.getController(GEPPETTO.Widgets.TREEVISUALISERDAT).addTreeVisualiserDATWidget(); + widget = this.getController(GEPPETTO.Widgets.TREEVISUALISERDAT).addTreeVisualiserDATWidget(isStateless); break; //create variable visualiser widget case GEPPETTO.Widgets.VARIABLEVISUALISER: - widget = this.getController(GEPPETTO.Widgets.VARIABLEVISUALISER).addVariableVisualiserWidget(); + widget = this.getController(GEPPETTO.Widgets.VARIABLEVISUALISER).addVariableVisualiserWidget(isStateless); break; //create connectivity widget case GEPPETTO.Widgets.CONNECTIVITY: - widget = this.getController(GEPPETTO.Widgets.CONNECTIVITY).addConnectivityWidget(); + widget = this.getController(GEPPETTO.Widgets.CONNECTIVITY).addConnectivityWidget(isStateless); break; //create button bar case GEPPETTO.Widgets.BUTTONBAR: - widget = this.getController(GEPPETTO.Widgets.BUTTONBAR).addButtonBarWidget(); + widget = this.getController(GEPPETTO.Widgets.BUTTONBAR).addButtonBarWidget(isStateless); break; //create stack viewer case GEPPETTO.Widgets.STACKVIEWER: - widget = this.getController(GEPPETTO.Widgets.STACKVIEWER).addStackViewerWidget(); + widget = this.getController(GEPPETTO.Widgets.STACKVIEWER).addStackViewerWidget(isStateless); break; //Use as template for new widgets //create WIDGETNAME diff --git a/src/main/webapp/js/components/widgets/buttonBar/controllers/ButtonBarController.js b/src/main/webapp/js/components/widgets/buttonBar/controllers/ButtonBarController.js index 3d35cd6b9..2fd1f9663 100644 --- a/src/main/webapp/js/components/widgets/buttonBar/controllers/ButtonBarController.js +++ b/src/main/webapp/js/components/widgets/buttonBar/controllers/ButtonBarController.js @@ -52,11 +52,18 @@ define(function (require) { /** * Creates new button bar widget */ - addButtonBarWidget: function () { + addButtonBarWidget: function (isStateless) { + if(isStateless == undefined){ + isStateless = false; + } + //look for a name and id for the new widget var id = this.getAvailableWidgetId("ButtonBar", this.widgets); var name = id; - var vv = window[name] = new BuBar({id: id, name: name, visible: true, widgetType: GEPPETTO.Widgets.BUTTONBAR}); + var vv = window[name] = new BuBar({ + id: id, name: name, visible: true, + widgetType: GEPPETTO.Widgets.BUTTONBAR, stateless: isStateless + }); vv.help = function () { return GEPPETTO.Console.getObjectCommands(id); }; diff --git a/src/main/webapp/js/components/widgets/connectivity/controllers/ConnectivityController.js b/src/main/webapp/js/components/widgets/connectivity/controllers/ConnectivityController.js index 66632f2e1..17028dbfb 100644 --- a/src/main/webapp/js/components/widgets/connectivity/controllers/ConnectivityController.js +++ b/src/main/webapp/js/components/widgets/connectivity/controllers/ConnectivityController.js @@ -30,14 +30,20 @@ define(function (require) { /** * Adds a new Connectivity Widget to Geppetto */ - addConnectivityWidget: function () { + addConnectivityWidget: function (isStateless) { + if(isStateless == undefined){ + isStateless = false; + } + //look for a name and id for the new widget var id = this.getAvailableWidgetId("Connectivity", this.widgets); var name = id; - //create tree visualiser widget - var cnt = window[name] = new Connectivity({id: id, name: name, visible: false, width: 500, height: 500, controller: this, widgetType: GEPPETTO.Widgets.CONNECTIVITY}); + var cnt = window[name] = new Connectivity({ + id: id, name: name, visible: false, width: 500, height: 500, controller: this, + widgetType: GEPPETTO.Widgets.CONNECTIVITY, stateless: isStateless + }); //create help command for connw cnt.help = function () { diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index 4b00a2034..8149885e7 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -22,14 +22,21 @@ define(function (require) { /** * Creates plotting widget */ - addPlotWidget: function () { + addPlotWidget: function (isStateless) { + if(isStateless == undefined){ + isStateless = false; + } //look for a name and id for the new widget var id = this.getAvailableWidgetId("Plot", this.widgets); var name = id; //create plotting widget - var p = window[name] = new Plot({id: id, name: name, visible: true, widgetType: GEPPETTO.Widgets.PLOT}); + var p = window[name] = new Plot({ + id: id, name: name, visible: true, + widgetType: GEPPETTO.Widgets.PLOT, + stateless: isStateless + }); p.setController(this); //create help command for plot diff --git a/src/main/webapp/js/components/widgets/popup/controllers/PopupController.js b/src/main/webapp/js/components/widgets/popup/controllers/PopupController.js index c6fbadaaf..e16752d14 100644 --- a/src/main/webapp/js/components/widgets/popup/controllers/PopupController.js +++ b/src/main/webapp/js/components/widgets/popup/controllers/PopupController.js @@ -24,13 +24,20 @@ define(function (require) { /** * Creates popup widget */ - addPopupWidget: function () { + addPopupWidget: function (isStateless) { + if(isStateless == undefined){ + isStateless = false; + } + //look for a name and id for the new widget var id = this.getAvailableWidgetId("Popup", this.widgets); var name = id; //create popup widget - var p = window[name] = new Popup({id: id, name: name, visible: true, controller: this, widgetType: GEPPETTO.Widgets.POPUP}); + var p = window[name] = new Popup({ + id: id, name: name, visible: true, controller: this, + widgetType: GEPPETTO.Widgets.POPUP, stateless: isStateless + }); p.setController(this); p.setSize(394,490); //create help command for plot diff --git a/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js b/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js index dff7e081d..9e445bf15 100644 --- a/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js +++ b/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js @@ -53,11 +53,18 @@ define(function (require) { /** * Creates new stack viewer widget */ - addStackViewerWidget: function () { + addStackViewerWidget: function (isStateless) { + if(isStateless == undefined){ + isStateless = false; + } + //look for a name and id for the new widget var id = this.getAvailableWidgetId("StackViewer", this.widgets); var name = id; - var vv = window[name] = new Stack({id: id, name: name, visible: true, widgetType: GEPPETTO.Widgets.STACKVIEWER}); + var vv = window[name] = new Stack({ + id: id, name: name, visible: true, + widgetType: GEPPETTO.Widgets.STACKVIEWER, stateless: isStateless + }); vv.help = function () { return GEPPETTO.Console.getObjectCommands(id); }; diff --git a/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js b/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js index d58e7956c..6de888141 100644 --- a/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js +++ b/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js @@ -23,19 +23,19 @@ define(function (require) { /** * Adds a new TreeVisualizerDAT Widget to Geppetto */ - addTreeVisualiserDATWidget: function () { + addTreeVisualiserDATWidget: function (isStateless) { + if(isStateless == undefined){ + isStateless = false; + } + //look for a name and id for the new widget var id = this.getAvailableWidgetId("TreeVisualiserDAT", this.widgets); var name = id; // create tree visualiser widget var tvdat = window[name] = new TreeVisualiserDAT({ - id: id, - name: name, - visible: true, - width: 260, - height: 350, - widgetType: GEPPETTO.Widgets.TREEVISUALISERDAT + id: id, name: name, visible: true, width: 260, height: 350, + widgetType: GEPPETTO.Widgets.TREEVISUALISERDAT, stateless: isStateless }); // create help command for plot tvdat.help = function () { diff --git a/src/main/webapp/js/components/widgets/variablevisualiser/controllers/VariableVisualiserController.js b/src/main/webapp/js/components/widgets/variablevisualiser/controllers/VariableVisualiserController.js index 9fbdf771e..76e7cb9cc 100644 --- a/src/main/webapp/js/components/widgets/variablevisualiser/controllers/VariableVisualiserController.js +++ b/src/main/webapp/js/components/widgets/variablevisualiser/controllers/VariableVisualiserController.js @@ -52,11 +52,18 @@ define(function (require) { /** * Creates new variable visualiser widget */ - addVariableVisualiserWidget: function () { + addVariableVisualiserWidget: function (isStateless) { + if(isStateless == undefined){ + isStateless = false; + } + //look for a name and id for the new widget var id = this.getAvailableWidgetId("VarVis", this.widgets); var name = id; - var vv = window[name] = new VarVis({id: id, name: name, visible: true, widgetType: GEPPETTO.Widgets.VARIABLEVISUALISER}); + var vv = window[name] = new VarVis({ + id: id, name: name, visible: true, + widgetType: GEPPETTO.Widgets.VARIABLEVISUALISER, stateless: isStateless + }); vv.help = function () { return GEPPETTO.Console.getObjectCommands(id); }; diff --git a/src/main/webapp/js/pages/geppetto/G.js b/src/main/webapp/js/pages/geppetto/G.js index fae8882f6..968bfe2b8 100644 --- a/src/main/webapp/js/pages/geppetto/G.js +++ b/src/main/webapp/js/pages/geppetto/G.js @@ -48,9 +48,16 @@ define(function (require) { isBrightnessFunctionSet: function() { return this.brightnessFunctionSet; }, - - addWidget: function (type) { - var newWidget = GEPPETTO.WidgetFactory.addWidget(type); + + /** + * Adds widgets to Geppetto + * + * @param type + * @param isStateless + * @returns {*} + */ + addWidget: function (type, isStateless) { + var newWidget = GEPPETTO.WidgetFactory.addWidget(type, isStateless); return newWidget; }, From 729ee62404c68cb58740b1942a19dd7dea7d3eb2 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 3 Apr 2017 09:19:46 -0700 Subject: [PATCH 041/339] saving work in progress --- .../controllers/ConnectionHandler.java | 20 +++++++++++++++---- .../downloadProject/DownloadProjectButton.js | 16 ++++----------- .../js/geppettoProject/model/ProjectNode.js | 2 +- .../controls/simulation-controls.less | 11 ++++++++++ 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index d126810da..fd650b693 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -2,6 +2,7 @@ import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; @@ -1400,20 +1401,31 @@ public void downloadProject(String requestID, long projectId) { { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); - if(geppettoProject != null) + // Convert model + URL url = geppettoManager.downloadProject(geppettoProject); + + if(url != null) { + // Zip folder + Zipper zipper = new Zipper(PathConfiguration.getProjectPath(Scope.CONNECTION, projectId)); + Path path = zipper.getZipFromFile(url); - geppettoManager.downloadProject(geppettoProject); + // Send zip file to the client + websocketConnection.sendBinaryMessage(requestID, path); websocketConnection.sendMessage(requestID, OutboundMessages.DOWNLOAD_PROJECT,null); } else { - error(null, "Error persisting project " + projectId + "."); + error(new GeppettoExecutionException("Results of type not found in the current experiment"), "Error downloading project"); } } catch(GeppettoExecutionException | GeppettoAccessException e) { - error(e, "Error persisting project"); + error(e, "Error downloading project"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); } } diff --git a/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js b/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js index 16cce9006..e6fc18fed 100644 --- a/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js +++ b/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js @@ -77,26 +77,18 @@ define(function(require) { self.setState({disableButton: true}); }); - GEPPETTO.on('spin_persist', function() { + GEPPETTO.on('spin_download', function() { self.setState({icon:"fa fa-download fa-spin"}); }.bind($(".downloadProjectButton"))); - GEPPETTO.on('stop_spin_persist', function() { + GEPPETTO.on('stop_spin_download', function() { self.setState({icon:"fa fa-download"}); }.bind($(".downloadProjectButton"))); self.attachTooltip(); - - if(window.Project!=undefined){ - this.setState(this.evaluateState()); - } }, - evaluateState:function(){ - return {disableButton:window.Project.persisted || !GEPPETTO.UserController.hasPermission(GEPPETTO.Resources.WRITE_PROJECT)}; - }, - clickEvent : function(){ var self = this; // update contents of what's displayed on tooltip @@ -104,14 +96,14 @@ define(function(require) { $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); self.setState({disableButton: true}); GEPPETTO.Console.executeCommand("Project.download();"); - GEPPETTO.trigger("spin_persist"); + GEPPETTO.trigger("spin_download"); }, render: function () { return (
    diff --git a/src/main/webapp/js/geppettoProject/model/ProjectNode.js b/src/main/webapp/js/geppettoProject/model/ProjectNode.js index a16ec193a..cf51d6f4b 100644 --- a/src/main/webapp/js/geppettoProject/model/ProjectNode.js +++ b/src/main/webapp/js/geppettoProject/model/ProjectNode.js @@ -352,7 +352,7 @@ define(['backbone'], function (require) { * @command ProjectNode.downloadModel(format) * * @param {String} name - File format to download */ - downloadProject : function(path, format) { + download : function(path, format) { var parameters = {}; parameters["projectId"] = this.getId(); GEPPETTO.MessageSocket.send("download_project", parameters); diff --git a/src/main/webapp/style/less/components/controls/simulation-controls.less b/src/main/webapp/style/less/components/controls/simulation-controls.less index b00cbea99..121ab8a8c 100644 --- a/src/main/webapp/style/less/components/controls/simulation-controls.less +++ b/src/main/webapp/style/less/components/controls/simulation-controls.less @@ -24,6 +24,17 @@ color:@orange; } +.DownloadProjectButton{ + position: fixed; + top: 5px; + right: 569px; + height: 27px; +} + +.DownloadProjectButton:disabled{ + background:0; + color:@orange; +} .tooltip-persist { height: 25px; From 98aaaf07d7280ae6ad10d5d84ed5e2ec35df481c Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 3 Apr 2017 09:54:48 -0700 Subject: [PATCH 042/339] error check --- .../interface/downloadProject/DownloadProjectButton.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js b/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js index e6fc18fed..6bf6c2128 100644 --- a/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js +++ b/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js @@ -74,7 +74,14 @@ define(function(require) { $('.DownloadProjectButton').uitooltip({content: "The project was downloaded!", position: { my: "right center", at : "left center"}}); $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); - self.setState({disableButton: true}); + }); + + GEPPETTO.on("geppetto:error", function(){ + // update contents of what's displayed on tooltip + $('.DownloadProjectButton').uitooltip({content: "The project had errors during download!", + position: { my: "right center", at : "left center"}}); + $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); + self.setState({icon:"fa fa-download"}); }); GEPPETTO.on('spin_download', function() { From 2b338bceaf4a684bc049e2b17e2bf7a66e6d28ca Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 3 Apr 2017 20:04:03 +0100 Subject: [PATCH 043/339] typo maligno --- src/main/webapp/js/common/GEPPETTO.Utility.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/common/GEPPETTO.Utility.js b/src/main/webapp/js/common/GEPPETTO.Utility.js index 6563a705d..fc81dad50 100644 --- a/src/main/webapp/js/common/GEPPETTO.Utility.js +++ b/src/main/webapp/js/common/GEPPETTO.Utility.js @@ -174,7 +174,7 @@ define(function (require) { }; // inject saveData into utility, need to do it this way because it's adding things to the DOM - // and returning the function with clousre on the DOM elements + // and returning the function with closure on the DOM elements GEPPETTO.Utility.saveData = (function () { var a = document.createElement("a"); document.body.appendChild(a); From 08e35f4706aa8eda941734d3b8608beeb0dd6d90 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 3 Apr 2017 20:04:36 +0100 Subject: [PATCH 044/339] formatting from hell --- src/main/webapp/js/common/GEPPETTO.Utility.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.Utility.js b/src/main/webapp/js/common/GEPPETTO.Utility.js index fc81dad50..1a1273e66 100644 --- a/src/main/webapp/js/common/GEPPETTO.Utility.js +++ b/src/main/webapp/js/common/GEPPETTO.Utility.js @@ -208,10 +208,7 @@ define(function (require) { String.prototype.format = function() { var args = arguments; return this.replace(/{(\d+)}/g, function(match, number) { - return typeof args[number] != 'undefined' - ? args[number] - : match - ; + return typeof args[number] != 'undefined' ? args[number] : match; }); }; } From dd7c1ef6451e052167fcdd03154d4839cfa7ed90 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 4 Apr 2017 11:54:07 +0100 Subject: [PATCH 045/339] Bug fixing and changes to allow to run quick experiments. --- .../webapp/js/communication/MessageHandler.js | 226 +++++++++--------- .../experimentsTable/ExperimentsTable.js | 15 +- .../geppettoProject/model/ExperimentNode.js | 140 +++++------ .../js/pages/geppetto/GEPPETTO.Events.js | 1 + 4 files changed, 190 insertions(+), 192 deletions(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 56b963074..72317dcef 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -1,9 +1,8 @@ - /** * Handles incoming messages associated with Simulation */ -define(function (require) { - return function (GEPPETTO) { +define(function(require) { + return function(GEPPETTO) { var messageTypes = { @@ -40,99 +39,98 @@ define(function (require) { UPDATE_MODEL_TREE: "update_model_tree", DOWNLOAD_MODEL: "download_model", DOWNLOAD_RESULTS: "download_results", - ERROR_RUNNING_EXPERIMENT : "error_running_experiment", - PROJECT_MADE_PUBLIC : "project_made_public" + ERROR_RUNNING_EXPERIMENT: "error_running_experiment", + PROJECT_MADE_PUBLIC: "project_made_public" }; var messageHandler = {}; - messageHandler[messageTypes.PROJECT_LOADED] = function (payload) { + messageHandler[messageTypes.PROJECT_LOADED] = function(payload) { GEPPETTO.SimulationHandler.loadProject(payload); }; - messageHandler[messageTypes.MODEL_LOADED] = function (payload) { + messageHandler[messageTypes.MODEL_LOADED] = function(payload) { GEPPETTO.SimulationHandler.loadModel(payload); }; - messageHandler[messageTypes.EXPERIMENT_CREATED] = function (payload) { + messageHandler[messageTypes.EXPERIMENT_CREATED] = function(payload) { var newExperiment = GEPPETTO.SimulationHandler.createExperiment(payload); }; - - messageHandler[messageTypes.ERROR_RUNNING_EXPERIMENT] = function (payload) { - var error = JSON.parse(payload.error_running_experiment); - var experiments = window.Project.getExperiments(); - var experimentID = error.id; - - //changing status in matched experiment - for (var e in experiments) { - if (experiments[e].getId() == experimentID) { - experiments[e].setDetails(error); - break; - } - } - + + messageHandler[messageTypes.ERROR_RUNNING_EXPERIMENT] = function(payload) { + var error = JSON.parse(payload.error_running_experiment); + var experiments = window.Project.getExperiments(); + var experimentID = error.id; + + //changing status in matched experiment + for (var e in experiments) { + if (experiments[e].getId() == experimentID) { + experiments[e].setDetails(error); + break; + } + } + GEPPETTO.trigger('geppetto:error', error.msg); GEPPETTO.FE.errorDialog(GEPPETTO.Resources.ERROR, error.message, error.code, error.exception); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; - - messageHandler[messageTypes.EXPERIMENT_LOADING] = function (payload) { + + messageHandler[messageTypes.EXPERIMENT_LOADING] = function(payload) { GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.LOADING_EXPERIMENT); }; - - messageHandler[messageTypes.PROJECT_MADE_PUBLIC] = function (payload) { + + messageHandler[messageTypes.PROJECT_MADE_PUBLIC] = function(payload) { var data = JSON.parse(payload.update); window.Project.isPublicProject = data.isPublic; GEPPETTO.trigger(GEPPETTO.Events.Project_made_public); console.log("Project was made public"); }; - messageHandler[messageTypes.EXPERIMENT_LOADED] = function (payload) { + messageHandler[messageTypes.EXPERIMENT_LOADED] = function(payload) { GEPPETTO.SimulationHandler.loadExperiment(payload); GEPPETTO.trigger(GEPPETTO.Events.Experiment_loaded); - - if(window.Project.getActiveExperiment()!=null || undefined){ - if (window.Project.getActiveExperiment().getScript() != undefined) { - G.runScript(window.Project.getActiveExperiment().getScript()); - } + + if (window.Project.getActiveExperiment() != null || undefined) { + if (window.Project.getActiveExperiment().getScript() != undefined) { + G.runScript(window.Project.getActiveExperiment().getScript()); + } } }; - messageHandler[messageTypes.VARIABLE_FETCHED] = function (payload) { - GEPPETTO.trigger('spin_logo'); + messageHandler[messageTypes.VARIABLE_FETCHED] = function(payload) { + GEPPETTO.trigger('spin_logo'); GEPPETTO.SimulationHandler.addVariableToModel(payload); GEPPETTO.trigger('stop_spin_logo'); }; - messageHandler[messageTypes.IMPORT_TYPE_RESOLVED] = function (payload) { - GEPPETTO.trigger('spin_logo'); - GEPPETTO.SimulationHandler.swapResolvedType(payload); + messageHandler[messageTypes.IMPORT_TYPE_RESOLVED] = function(payload) { + GEPPETTO.trigger('spin_logo'); + GEPPETTO.SimulationHandler.swapResolvedType(payload); GEPPETTO.trigger('stop_spin_logo'); }; - - messageHandler[messageTypes.IMPORT_VALUE_RESOLVED] = function (payload) { + + messageHandler[messageTypes.IMPORT_VALUE_RESOLVED] = function(payload) { GEPPETTO.SimulationHandler.swapResolvedValue(payload); GEPPETTO.trigger('stop_spin_logo'); }; - - messageHandler[messageTypes.GET_EXPERIMENT_STATE] = function (payload) { + + messageHandler[messageTypes.GET_EXPERIMENT_STATE] = function(payload) { var experimentState = JSON.parse(payload.update); var experiment = window.Project.getActiveExperiment(); - - if(experimentState.projectId==window.Project.getId() && experimentState.experimentId==experiment.getId()){ - //if we fetched data for the current project/experiment - GEPPETTO.ExperimentsController.updateExperiment(experiment, experimentState); - } - else{ - GEPPETTO.ExperimentsController.addExternalExperimentState(experimentState); + + if (experimentState.projectId == window.Project.getId() && experimentState.experimentId == experiment.getId()) { + //if we fetched data for the current project/experiment + GEPPETTO.ExperimentsController.updateExperiment(experiment, experimentState); + } else { + GEPPETTO.ExperimentsController.addExternalExperimentState(experimentState); } - + GEPPETTO.trigger("stop_spin_logo"); }; - messageHandler[messageTypes.EXPERIMENT_STATUS] = function (payload) { + messageHandler[messageTypes.EXPERIMENT_STATUS] = function(payload) { var experimentStatus = JSON.parse(payload.update); var experiments = window.Project.getExperiments(); @@ -145,19 +143,17 @@ define(function (require) { for (var e in experiments) { if (experiments[e].getId() == experimentID) { if (experiments[e].getStatus() != status) { - if (window.Project.getActiveExperiment() != null || undefined) { + if (window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined) { if (window.Project.getActiveExperiment().getId() == experimentID) { if (experiments[e].getStatus() == GEPPETTO.Resources.ExperimentStatus.RUNNING && status == GEPPETTO.Resources.ExperimentStatus.COMPLETED) { - experiments[e].setDetails(""); + experiments[e].setDetails(""); experiments[e].setStatus(status); GEPPETTO.trigger(GEPPETTO.Events.Experiment_completed, experimentID); - } - else if (status == GEPPETTO.Resources.ExperimentStatus.ERROR) { + } else if (status == GEPPETTO.Resources.ExperimentStatus.ERROR) { experiments[e].setStatus(status); GEPPETTO.trigger(GEPPETTO.Events.Experiment_failed, experimentID); - } - else if (experiments[e].getStatus() == GEPPETTO.Resources.ExperimentStatus.DESIGN && + } else if (experiments[e].getStatus() == GEPPETTO.Resources.ExperimentStatus.DESIGN && status == GEPPETTO.Resources.ExperimentStatus.RUNNING) { experiments[e].setStatus(status); GEPPETTO.trigger(GEPPETTO.Events.Experiment_running, experimentID); @@ -171,86 +167,86 @@ define(function (require) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_status_check); }; - messageHandler[messageTypes.PROJECT_PERSISTED] = function (payload) { + messageHandler[messageTypes.PROJECT_PERSISTED] = function(payload) { GEPPETTO.SimulationHandler.persistProject(payload); GEPPETTO.trigger("stop_spin_persist"); }; - messageHandler[messageTypes.PROJECT_CONFIGURATION] = function (payload) { + messageHandler[messageTypes.PROJECT_CONFIGURATION] = function(payload) { GEPPETTO.trigger('project:configloaded', payload.configuration); }; - messageHandler[messageTypes.EXPERIMENT_DELETED] = function (payload) { + messageHandler[messageTypes.EXPERIMENT_DELETED] = function(payload) { GEPPETTO.SimulationHandler.deleteExperiment(payload); }; - messageHandler[messageTypes.WATCHED_VARIABLES_SET] = function (payload) { + messageHandler[messageTypes.WATCHED_VARIABLES_SET] = function(payload) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_updated); GEPPETTO.Console.log("The list of variables to watch was successfully updated."); }; //handles the case where service doesn't support feature and shows message - messageHandler[messageTypes.NO_FEATURE] = function () { + messageHandler[messageTypes.NO_FEATURE] = function() { //Updates the simulation controls visibility GEPPETTO.FE.infoDialog(GEPPETTO.Resources.NO_FEATURE, payload.message); }; //received model tree from server - messageHandler[messageTypes.UPDATE_MODEL_TREE] = function (payload) { + messageHandler[messageTypes.UPDATE_MODEL_TREE] = function(payload) { GEPPETTO.trigger(GEPPETTO.Events.Experiment_updated); GEPPETTO.Console.log("The model parameters were successfully updated."); }; //received supported outputs from server - messageHandler[messageTypes.GET_SUPPORTED_OUTPUTS] = function (payload) { + messageHandler[messageTypes.GET_SUPPORTED_OUTPUTS] = function(payload) { var supportedOutputs = JSON.parse(payload.get_supported_outputs); GEPPETTO.Console.log(supportedOutputs); }; - messageHandler[messageTypes.PROJECT_PROPS_SAVED] = function (payload) { + messageHandler[messageTypes.PROJECT_PROPS_SAVED] = function(payload) { GEPPETTO.Console.log("Project saved succesfully"); }; - - messageHandler[messageTypes.EXPERIMENT_PROPS_SAVED] = function (payload) { + + messageHandler[messageTypes.EXPERIMENT_PROPS_SAVED] = function(payload) { GEPPETTO.Console.log("Experiment saved succesfully"); var data = JSON.parse(payload.update); var experiment = window.Project.getExperimentById(data.id); - + //Updates status. Used for when experiment failed, and user modified the parameters //right after, the status changes back to DESIGN from ERROR - if(experiment.getStatus() != data.status){ + if (experiment.getStatus() != data.status) { experiment.setStatus(data.status); } }; - messageHandler[messageTypes.DROPBOX_LINKED] = function (payload) { + messageHandler[messageTypes.DROPBOX_LINKED] = function(payload) { GEPPETTO.Console.log("Dropbox linked successfully"); }; - messageHandler[messageTypes.DROPBOX_UNLINKED] = function (payload) { + messageHandler[messageTypes.DROPBOX_UNLINKED] = function(payload) { GEPPETTO.Console.log("Dropbox unlinked succesfully"); }; - messageHandler[messageTypes.DOWNLOAD_RESULTS] = function (payload) { + messageHandler[messageTypes.DOWNLOAD_RESULTS] = function(payload) { GEPPETTO.Console.log("Results downloaded succesfully"); }; - messageHandler[messageTypes.DOWNLOAD_MODEL] = function (payload) { + messageHandler[messageTypes.DOWNLOAD_MODEL] = function(payload) { GEPPETTO.Console.log("Model downloaded succesfully"); }; - messageHandler[messageTypes.RESULTS_UPLOADED] = function (payload) { + messageHandler[messageTypes.RESULTS_UPLOADED] = function(payload) { GEPPETTO.Console.log("Results uploaded succesfully"); }; - messageHandler[messageTypes.MODEL_UPLOADED] = function (payload) { + messageHandler[messageTypes.MODEL_UPLOADED] = function(payload) { GEPPETTO.Console.log("Model uploaded succesfully"); }; GEPPETTO.SimulationHandler = { - onMessage: function (parsedServerMessage) { + onMessage: function(parsedServerMessage) { // parsed message has a type and data fields - data contains the payload of the message // Switch based on parsed incoming message type if (messageHandler.hasOwnProperty(parsedServerMessage.type)) { @@ -258,15 +254,15 @@ define(function (require) { } }, - persistProject: function (payload) { + persistProject: function(payload) { var message = JSON.parse(payload.update); var projectID = message.projectID; var activeExperimentID = message.activeExperimentID; window.Project.id = parseInt(projectID); - if(window.Project.getActiveExperiment()!=null || undefined){ - var oldActiveExperiment = window.Project.getActiveExperiment().id; - window.Project.getActiveExperiment().id = parseInt(activeExperimentID); + if (window.Project.getActiveExperiment() != null || undefined) { + var oldActiveExperiment = window.Project.getActiveExperiment().id; + window.Project.getActiveExperiment().id = parseInt(activeExperimentID); } window.Project.persisted = true; @@ -274,7 +270,7 @@ define(function (require) { GEPPETTO.Console.log("The project has been persisted [id=" + projectID + "]."); }, - loadProject: function (payload) { + loadProject: function(payload) { // we remove anything from any previous loaded project if there was one GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.LOADING_PROJECT); if (Project) { @@ -287,13 +283,13 @@ define(function (require) { var readOnly = update.isReadOnly; window.Project = GEPPETTO.ProjectFactory.createProjectNode(project, persisted); window.Project.readOnly = update.isReadOnly; - + GEPPETTO.Init.initEventListeners(); GEPPETTO.trigger(GEPPETTO.Events.Project_loaded); GEPPETTO.Console.log(GEPPETTO.Resources.PROJECT_LOADED); }, - loadModel: function (payload) { + loadModel: function(payload) { console.time(GEPPETTO.Resources.PARSING_MODEL); GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.PARSING_MODEL); @@ -343,7 +339,7 @@ define(function (require) { * @param variableId * @param datasourceId */ - fetchVariable: function (variableId, datasourceId, callback) { + fetchVariable: function(variableId, datasourceId, callback) { if (!window.Model.hasOwnProperty(variableId)) { var params = {}; params["projectId"] = Project.getId(); @@ -366,7 +362,7 @@ define(function (require) { * * @param payload */ - addVariableToModel: function (payload) { + addVariableToModel: function(payload) { var rawModel = JSON.parse(payload.variable_fetched); console.time(GEPPETTO.Resources.ADDING_VARIABLE); @@ -393,16 +389,16 @@ define(function (require) { * * @param typePath */ - resolveImportType: function (typePaths, callback) { - if(typeof typePaths == "string"){ - typePaths=[typePaths]; + resolveImportType: function(typePaths, callback) { + if (typeof typePaths == "string") { + typePaths = [typePaths]; } var params = {}; params["projectId"] = Project.getId(); // replace client naming first occurrence - the server doesn't know about it - var paths=[]; - for(var i=0;i} - List of watched variables for given name */ - getWatchedVariables: function (asObjs, time) { + getWatchedVariables: function(asObjs, time) { if (asObjs === undefined) { asObjs = false; } @@ -356,9 +350,9 @@ define(['backbone'], function (require) { return watchedVariables; }, - - getSetParameters : function(){ - return this.setParameters; + + getSetParameters: function() { + return this.setParameters; }, /** @@ -366,12 +360,11 @@ define(['backbone'], function (require) { * * @command ExperimentNode.downloadResults(recording) */ - downloadResults: function (aspectPath, format) { + downloadResults: function(aspectPath, format) { if (this.downloadPermission && this.login) { if (this == window.Project.getActiveExperiment()) { if (this.status == GEPPETTO.Resources.ExperimentStatus.COMPLETED) { - var parameters = - {}; + var parameters = {}; parameters["format"] = format; parameters["aspectPath"] = aspectPath; parameters["experimentId"] = this.id; @@ -403,21 +396,20 @@ define(['backbone'], function (require) { * @command ExperimentNode.clone() * @returns {ExperimentNode} Creates a new ExperimentNode */ - clone: function () { + clone: function(callback) { if (this.writePermission && this.getParent().persisted && this.login && !this.getParent().isReadOnly()) { var parameters = {}; parameters["projectId"] = this.getParent().getId(); parameters["experimentId"] = this.id; - GEPPETTO.MessageSocket.send("clone_experiment", parameters); + GEPPETTO.MessageSocket.send("clone_experiment", parameters, callback); } else { return GEPPETTO.Utility.persistedAndWriteMessage(this); } }, - deleteExperiment: function () { + deleteExperiment: function() { if (this.writePermission && this.getParent().persisted && this.login) { - var parameters = - {}; + var parameters = {}; parameters["experimentId"] = this.id; parameters["projectId"] = this.getParent().getId(); GEPPETTO.MessageSocket.send("delete_experiment", parameters); @@ -428,12 +420,11 @@ define(['backbone'], function (require) { } }, - uploadModel: function (aspectPath, format) { + uploadModel: function(aspectPath, format) { if (this.writePermission && this.getParent().persisted && this.login) { if (this == window.Project.getActiveExperiment()) { if (this.status == GEPPETTO.Resources.ExperimentStatus.COMPLETED) { - var parameters = - {}; + var parameters = {}; parameters["format"] = format; parameters["aspectPath"] = aspectPath; parameters["experimentId"] = this.id; @@ -452,12 +443,11 @@ define(['backbone'], function (require) { } }, - uploadResults: function (aspectPath, format) { + uploadResults: function(aspectPath, format) { if (this.writePermission && this.getParent().persisted && this.login) { if (this == window.Project.getActiveExperiment()) { if (this.status == GEPPETTO.Resources.ExperimentStatus.COMPLETED) { - var parameters = - {}; + var parameters = {}; parameters["format"] = format; parameters["aspectPath"] = aspectPath; parameters["experimentId"] = this.id; @@ -479,13 +469,13 @@ define(['backbone'], function (require) { /** * Print out formatted node */ - print: function () { + print: function() { return "Name : " + this.name + "\n" + " Id: " + this.id + "\n"; }, - addSimulatorConfiguration: function (aspect, simulatorConfiguration) { + addSimulatorConfiguration: function(aspect, simulatorConfiguration) { this.simulatorConfigurations[aspect] = simulatorConfiguration; } }); -}); +}); \ No newline at end of file diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js index 45e399c11..1a9918ff5 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js @@ -35,6 +35,7 @@ define(function (require) { Experiment_failed: "experiment:failed", Experiment_update: "experiment:update", Experiment_updated: "experiment:updated", + Experiment_renamed: "experiment:renamed", Experiment_deleted: "experiment_deleted", Experiment_active: "experiment_active", Experiment_created:"experiment:created", From 1eba821b3ec8ac02356a86bf7044c45781e5685c Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 4 Apr 2017 15:43:04 +0100 Subject: [PATCH 046/339] save/set button bar to/from view state --- .../js/components/widgets/popup/Popup.js | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/webapp/js/components/widgets/popup/Popup.js b/src/main/webapp/js/components/widgets/popup/Popup.js index 2609c70e0..e612bfdbb 100644 --- a/src/main/webapp/js/components/widgets/popup/Popup.js +++ b/src/main/webapp/js/components/widgets/popup/Popup.js @@ -357,7 +357,9 @@ define(function (require) { baseView.dataType = (typeof this.data == "string") ? "string" : "object"; baseView.data = this.data; baseView.componentSpecific = { - customHandlers: this.customHandlers + customHandlers: this.customHandlers, + buttonBarControls: this.buttonBarControls, + buttonBarConfig: this.buttonBarConfig }; return baseView; @@ -378,13 +380,20 @@ define(function (require) { } // set component specific stuff, only custom handlers for popup widget - if(view.componentSpecific != undefined && view.componentSpecific.customHandlers != undefined){ - for(var i=0; i Date: Tue, 4 Apr 2017 19:26:47 +0100 Subject: [PATCH 047/339] plot viewstate, work in progress --- .../webapp/js/components/widgets/plot/Plot.js | 78 ++++++++++++------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 8556b6fd8..3603461ea 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -34,7 +34,8 @@ define(function (require) { plotOptions : null, reIndexUpdate : 0, updateRedraw : 3, - functionNode: false, + isFunctionNode: false, + isXYData: false, xaxisAutoRange : false, yaxisAutoRange : false, imageTypes : [], @@ -239,6 +240,10 @@ define(function (require) { * @param {Object} options - options for the plotting widget, if null uses default */ plotData: function (data, options) { + // set flags for function node and xy both to false + this.isFunctionNode = false; + this.isXYData = false; + var validVariable = eval(data); if(validVariable == null || undefined){ return "Can't plot undefined variable"; @@ -373,8 +378,7 @@ define(function (require) { this.plotOptions.height = this.plotElement.height(); this.plotOptions.width = this.plotElement.width(); } - //resizes plot right after creation, needed for d3 to resize - //to parent's widht and height + //resizes plot right after creation, needed for d3 to resize to parent's width and height Plotly.relayout(this.plotDiv,this.plotOptions); } }, @@ -413,7 +417,7 @@ define(function (require) { * Downloads a zip with the plotting data */ downloadPlotData : function(){ - if(!this.functionNode){ + if(!this.isFunctionNode){ var data = new Array(); var xCopied = false; @@ -560,7 +564,7 @@ define(function (require) { * Updates the plot widget with new data */ updateDataSet: function (step, playAll) { - if(!this.functionNode){ + if(!this.isFunctionNode){ /*Clears the data of the plot widget if the initialized flag *has not be set to true, which means arrays are populated but not yet plot*/ if(!this.initialized){ @@ -764,7 +768,7 @@ define(function (require) { }, clean: function (playAll) { - if(!this.functionNode){ + if(!this.isFunctionNode){ this.plotOptions.playAll = playAll; this.plotOptions.margin.r = 10; this.cleanDataSets(); @@ -810,8 +814,9 @@ define(function (require) { * @param {Node} functionNode - Function Node to be displayed */ plotFunctionNode: function (functionNode) { - - this.functionNode = true; + // set flags + this.isFunctionNode = true; + this.isXYData = false; //Check there is metada information to plot if (functionNode.getInitialValues()[0].value.dynamics.functionPlot != null) { @@ -837,7 +842,7 @@ define(function (require) { var YAxisLabel = plotMetadata["yAxisLabel"]; //Generate options from metadata information - options = { + var options = { xaxis: {min: initialValue, max: finalValue, show: true, axisLabel: XAxisLabel}, yaxis: {axisLabel: YAxisLabel}, legendText: plotTitle @@ -950,6 +955,10 @@ define(function (require) { } } + // set flags + this.isXYData = true; + this.isFunctionNode = false; + this.controller.addToHistory("Plot "+dataY.getInstancePath()+"/"+dataX.getInstancePath(),"plotXYData",[dataY,dataX,options],this.getId()); var timeSeriesData = this.getTimeSeriesData(dataY, dataX); @@ -998,14 +1007,24 @@ define(function (require) { getView: function(){ var baseView = Widget.View.prototype.getView.call(this); - // add options, data-type and data field - // baseView.options = this.options; - // TODO: handle case of function node, data function and x,y data - baseView.dataType = 'object'; - baseView.data = this.datasets.map(function(item){ - // build array of paths - return item.name; - }); + // add options, whatever they are + baseView.options = this.options; + + // handle case of function node, data function and x,y data + if(this.isFunctionNode){ + baseView.dataType = 'function'; + // TODO: store data + } else if (this.isXYData){ + baseView.dataType = 'xyData'; + // TODO: store data + } else { + // default case, simple plot with variables plots based on instance paths + baseView.dataType = 'object'; + baseView.data = this.datasets.map(function(item){ + // build array of paths + return item.name; + }); + } return baseView; }, @@ -1018,16 +1037,21 @@ define(function (require) { this.setOptions(view.options); } - // set data + options based on data-type field - for (var index in view.data) { - var path = view.data[index]; - // TODO: project and experiment id could be any project and any experiment - this.controller.plotStateVariable( - Project.getId(), - Project.getActiveExperiment().getId(), - path, - this - ) + if(view.dataType == 'function'){ + // TODO pull function out of data attribute + } else if(view.dataType == 'xyData'){ + // TODO pull xy data out of data attribute + } else if(view.dataType == 'object'){ + for (var index in view.data) { + var path = view.data[index]; + // TODO: project and experiment id could be any project and any experiment + this.controller.plotStateVariable( + Project.getId(), + Project.getActiveExperiment().getId(), + path, + this + ) + } } } From 05ad7658ea4e949941fa447fb865b235ff2ac2f9 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 5 Apr 2017 07:05:25 -0700 Subject: [PATCH 048/339] gets rid of react component for download project button, replacing it using general button --- .../controllers/ConnectionHandler.java | 26 ++-- .../ComponentsInitialization.js | 19 ++- .../webapp/js/components/ComponentFactory.js | 2 +- .../js/components/controls/mixins/Button.js | 3 +- .../downloadProject/DownloadProjectButton.js | 123 ------------------ 5 files changed, 33 insertions(+), 140 deletions(-) delete mode 100644 src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index fd650b693..71159acfe 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -5,6 +5,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; +import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Path; import java.util.ArrayList; @@ -1400,32 +1401,29 @@ public void downloadProject(String requestID, long projectId) { try { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); - + // Convert model - URL url = geppettoManager.downloadProject(geppettoProject); - if(url != null) + URL url = URLReader.getURL("/projects/C302"); + + File file = new File(url.getFile()); + + if(file != null) { // Zip folder - Zipper zipper = new Zipper(PathConfiguration.getProjectPath(Scope.CONNECTION, projectId)); - Path path = zipper.getZipFromFile(url); + Zipper zipper = new Zipper(file.getAbsolutePath()); + Path path = zipper.getZipFromDirectory(file); // Send zip file to the client websocketConnection.sendBinaryMessage(requestID, path); websocketConnection.sendMessage(requestID, OutboundMessages.DOWNLOAD_PROJECT,null); } - else - { - error(new GeppettoExecutionException("Results of type not found in the current experiment"), "Error downloading project"); - } + } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + catch (FileNotFoundException e) { error(e, "Error downloading project"); - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { - e.printStackTrace(); + error(e, "Error downloading project"); } } diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index 4439fdcdf..a5b07ab03 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -35,8 +35,25 @@ define(function (require) { //Home button initialization GEPPETTO.ComponentFactory.addComponent('HOME', {}, document.getElementById("HomeButton")); + var toggleClickHandler = function(){ + $('.DownloadProjectButton').uitooltip({content: "The project is getting downloaded..."}); + $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); + GEPPETTO.Console.executeCommand("Project.download();"); + GEPPETTO.trigger("spin_download"); + }; + + var configuration = { + id: "DownloadProjectButton", + onClick : toggleClickHandler, + tooltipPosition : { my: "right center", at : "left-10 center"}, + icon : "fa fa-download", + className : "btn DownloadProjectButton pull-right", + disabled : false, + hidden : false + }; + //Save initialization - GEPPETTO.ComponentFactory.addComponent('SAVECONTROL', {}, document.getElementById("SaveButton")); + GEPPETTO.ComponentFactory.addComponent('BUTTON', {}, document.getElementById("SaveButton")); //Download Project Button initialization GEPPETTO.ComponentFactory.addComponent('DownloadProjectButton', {}, document.getElementById("DownloadProjectButton")); diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 7806550d1..4007b24cd 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -63,7 +63,7 @@ define(function (require) { 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', 'RAISEDBUTTON': 'controls/RaisedButton', - 'DownloadProjectButton':'interface/downloadProject/DownloadProjectButton' + 'BUTTON':'controls/mixins/Button' } diff --git a/src/main/webapp/js/components/controls/mixins/Button.js b/src/main/webapp/js/components/controls/mixins/Button.js index 3c97466a6..e35bf501e 100644 --- a/src/main/webapp/js/components/controls/mixins/Button.js +++ b/src/main/webapp/js/components/controls/mixins/Button.js @@ -17,7 +17,8 @@ define(function (require) { className: 'btn ' + this.props.className + ((this.props.hidden === true) ? ' hiddenElement' : ''), 'data-toggle': this.props['data-toggle'], onClick: this.props.onClick, - disabled: this.props.disabled + disabled: this.props.disabled, + icon : this.props.icon }, React.DOM.i({className: this.props.icon}), " " + this.props.label) ); } diff --git a/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js b/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js deleted file mode 100644 index 6bf6c2128..000000000 --- a/src/main/webapp/js/components/interface/downloadProject/DownloadProjectButton.js +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - * - * Copyright (c) 2011, 2016 OpenWorm. http://openworm.org - * - * All rights reserved. This program and the accompanying materials are made - * available under the terms of the MIT License which accompanies this - * distribution, and is available at http://opensource.org/licenses/MIT - * - * Contributors: OpenWorm - http://openworm.org/people.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - ******************************************************************************/ - -define(function(require) { - - var React = require('react'); - var GEPPETTO = require('geppetto'); - - $.widget.bridge('uitooltip', $.ui.tooltip); - - var downloadProjectComp = React.createClass({ - attachTooltip: function(){ - var self = this; - $('.DownloadProjectButton').uitooltip({ - position: { my: "right center", at : "left-25 center"}, - tooltipClass: "tooltip-persist", - show: { - effect: "slide", - direction: "right", - delay: 200 - }, - hide: { - effect: "slide", - direction: "right", - delay: 200 - }, - content: function () { - return self.state.tooltipLabel; - }, - }); - }, - - getInitialState: function() { - return { - disableButton : false, - tooltipLabel : "Click here to download this project!", - icon: "fa fa-download" - }; - }, - - componentDidMount: function() { - - var self = this; - - GEPPETTO.on(GEPPETTO.Events.Project_downloaded, function(){ - self.setState({disableButton: false}); - // update contents of what's displayed on tooltip - $('.DownloadProjectButton').uitooltip({content: "The project was downloaded!", - position: { my: "right center", at : "left center"}}); - $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); - }); - - GEPPETTO.on("geppetto:error", function(){ - // update contents of what's displayed on tooltip - $('.DownloadProjectButton').uitooltip({content: "The project had errors during download!", - position: { my: "right center", at : "left center"}}); - $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); - self.setState({icon:"fa fa-download"}); - }); - - GEPPETTO.on('spin_download', function() { - self.setState({icon:"fa fa-download fa-spin"}); - }.bind($(".downloadProjectButton"))); - - GEPPETTO.on('stop_spin_download', function() { - self.setState({icon:"fa fa-download"}); - }.bind($(".downloadProjectButton"))); - - - self.attachTooltip(); - }, - - clickEvent : function(){ - var self = this; - // update contents of what's displayed on tooltip - $('.DownloadProjectButton').uitooltip({content: "The project is getting downloaded..."}); - $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); - self.setState({disableButton: true}); - GEPPETTO.Console.executeCommand("Project.download();"); - GEPPETTO.trigger("spin_download"); - }, - - render: function () { - return ( -
    - -
    - ); - } - }); - - return downloadProjectComp; - -}); \ No newline at end of file From acbb0daaebe0931f534a92faced6d9acd321facf Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 5 Apr 2017 17:05:48 +0100 Subject: [PATCH 049/339] initial version for high res viewer working with google moon images --- .../webapp/js/components/ComponentFactory.js | 3 +- .../interface/highResViewer/HighResViewer.css | 3 + .../interface/highResViewer/HighResViewer.js | 96 + .../interface/highResViewer/atlas.js | 2052 +++++++++++++++++ src/main/webapp/package.json | 1 + src/main/webapp/webpack.config.js | 2 +- 6 files changed, 2155 insertions(+), 2 deletions(-) create mode 100644 src/main/webapp/js/components/interface/highResViewer/HighResViewer.css create mode 100644 src/main/webapp/js/components/interface/highResViewer/HighResViewer.js create mode 100644 src/main/webapp/js/components/interface/highResViewer/atlas.js diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 59d2b0f75..5855a8dfc 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -64,7 +64,8 @@ define(function (require) { 'PYTHONCONSOLE': 'interface/pythonConsole/PythonConsole', 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', - 'RAISEDBUTTON': 'controls/RaisedButton' + 'RAISEDBUTTON': 'controls/RaisedButton', + 'HIGHRESVIEWER': 'interface/highResViewer/HighResViewer' //'WIDGETCONTAINER': 'widgets/WidgetContainer' } diff --git a/src/main/webapp/js/components/interface/highResViewer/HighResViewer.css b/src/main/webapp/js/components/interface/highResViewer/HighResViewer.css new file mode 100644 index 000000000..a35e97dd7 --- /dev/null +++ b/src/main/webapp/js/components/interface/highResViewer/HighResViewer.css @@ -0,0 +1,3 @@ +#highResViewer { + height: 100%; +} \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js b/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js new file mode 100644 index 000000000..799cd5321 --- /dev/null +++ b/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js @@ -0,0 +1,96 @@ +define(function (require) { + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/interface/highResViewer/HighResViewer.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + var React = require('react'); + + // var GoogleMapLib = require('react-google-maps'); + // var GoogleMap = GoogleMapLib.GoogleMap; + + var GoogleMapsLoader = require('google-maps'); // only for common js environments + + + + var highResViewerComponent = React.createClass({ + + + + + componentDidMount: function () { + GoogleMapsLoader.KEY = 'AIzaSyAtAf8S4uU54ZogtLqbzc8pvQI6phGDL1Q'; + + GoogleMapsLoader.load(function(google) { + + var map = new google.maps.Map(document.getElementById('highResViewer'), { + center: {lat: 0, lng: 0}, + zoom: 1, + streetViewControl: false, + mapTypeControlOptions: { + mapTypeIds: ['moon'] + } + }); + + var moonMapType = new google.maps.ImageMapType({ + // Normalizes the coords that tiles repeat across the x axis (horizontally) + // like the standard Google map tiles. + getNormalizedCoord: function(coord, zoom) { + var y = coord.y; + var x = coord.x; + + // tile range in one direction range is dependent on zoom level + // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc + var tileRange = 1 << zoom; + + // don't repeat across y-axis (vertically) + if (y < 0 || y >= tileRange) { + return null; + } + + // repeat across x-axis + if (x < 0 || x >= tileRange) { + x = (x % tileRange + tileRange) % tileRange; + } + + return {x: x, y: y}; + }, + + getTileUrl: function(coord, zoom) { + var normalizedCoord = this.getNormalizedCoord(coord, zoom); + if (!normalizedCoord) { + return null; + } + var bound = Math.pow(2, zoom); + return '//mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' + + '/' + zoom + '/' + normalizedCoord.x + '/' + + (bound - normalizedCoord.y - 1) + '.jpg'; + }, + tileSize: new google.maps.Size(256, 256), + maxZoom: 9, + minZoom: 0, + radius: 1738000, + name: 'Moon' + }); + + map.mapTypes.set('moon', moonMapType); + map.setMapTypeId('moon'); + + }); + + GoogleMapsLoader.onLoad(function(google) { + console.log('I just loaded google maps api'); + }); + }, + + render: function () { + return ( +
    +
    + ) + } + }); + return highResViewerComponent; +}); diff --git a/src/main/webapp/js/components/interface/highResViewer/atlas.js b/src/main/webapp/js/components/interface/highResViewer/atlas.js new file mode 100644 index 000000000..3a0655b4a --- /dev/null +++ b/src/main/webapp/js/components/interface/highResViewer/atlas.js @@ -0,0 +1,2052 @@ + +function LoadTabContent(ind, callback) { + if (ind == 2) { + $("#tabs-1").load('./lib/tab_1_content.html', callback); + } + if (ind > 0) { + for (i = 3; i <= ind; i++) { + if ($("#tabs-" + i).html() == '') { + $("#tabs-" + i).load('./lib/tab_' + i + '_content.html', callback); + } + } + } else { + for (i = 3; i <= 4; i++) { + if ($(window).height() + $(this).scrollTop() > $("#tabs-" + i).offset().top && $("#tabs-" + i).html() == '') { + $("#tabs-" + i).load('./lib/tab_' + i + '_content.html', callback); + } + } + } + if (ind == 2) { // after moving to the correct location update the map + resizeMapDiv(); + updateMap(); + } +} + +$(function () { + /*$("#tabs").tabs();*/ + $("#tabs-3").tabs(); + $("#tabs-3").tabs("select", "3"); + $("#tabs-3").tabs("select", "3"); +}); + + +$(function () { + var sub_nav = $('#sub-nav'); + pos = sub_nav.offset(); + + //main_nav_height = parseFloat($("#selector").css("margin-top")); + sloc = findPosC(document.getElementById('left-container')); + //contloc = findPosC(document.getElementById('selector')); + //selector_height = /*parseFloat($("#selector").css("height"));*/ sloc[1] - contloc [1] + + topNav_height = (parseFloat($("top-banner").css("height")) + parseFloat($("#primary").css("height"))); + + $(window).scroll(function () { + /* dynamic load tab content to save initial loading time */ + LoadTabContent(0); + + var cont = $('#cont').offset().left; + var left = $('#left-box').css({ 'left': (cont - $(window).scrollLeft() + 20) + 'px' }); + $('#banner').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); + $('#primary').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); + $('#left-container').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); + $('#left-box').css({ 'left': (cont - $(window).scrollLeft() + 20) + 'px' }); + // $('.sub-nav-fixed').css({'left': (cont - $(window).scrollLeft() + 384) + 'px' }); + // $('.sub-nav-default').css({'left': 0 + 'px' }); + + /* leave the sub-nav as fixed on the top for Design for a width of 768px */ + if (parseInt($(".container").css("width")) > 420) { + if ($(this).scrollTop() + topNav_height > sloc[1] && sub_nav.hasClass('sub-nav-default')) { + sub_nav.fadeOut('fast', function () { + $(this).removeClass('sub-nav-default').addClass('sub-nav-fixed').fadeIn('fast'); + }); + $("#left-box").fadeOut('fast', function () { + $(this).removeClass('left-box-default').addClass('left-box-fixed').fadeIn('fast'); + }); + $("#selector").fadeOut('fast', function () { + $(this).addClass('selector-scroll').fadeIn('fast'); + }); + } else if ($(this).scrollTop() + topNav_height <= sloc[1] && sub_nav.hasClass('sub-nav-fixed')) { + sub_nav.fadeOut('fast', function () { + $(this).removeClass('sub-nav-fixed').addClass('sub-nav-default').fadeIn('fast'); + $("#sub-nav").css("left:0px !important"); + }); + $("#left-box").fadeOut('fast', function () { + $(this).removeClass('left-box-fixed').addClass('left-box-default').fadeIn('fast'); + }); + $("#selector").fadeOut('fast', function () { + $(this).removeClass('selector-scroll').fadeIn('fast'); + }); + } + } + + for (i = 4; i > 0; i--) { + if ($(this).scrollTop() >= $("#tabs-" + i).offset().top - 130) { + $('#sub-nav .ui-state-active').removeClass('ui-state-active'); + $('#sub-nav .ui-tabs-selected').removeClass('ui-tabs-selected'); + $("#tabs-" + i + "-link").addClass('ui-state-active ui-tabs-selected'); + break; + } + } + + }); +}); + +$(window).resize(function () { + var cont = $('#cont').offset().left; + var left = $('#left-box').css({ 'left': (cont - $(window).scrollLeft() + 20) + 'px' }); + $('#banner').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); + $('#primary').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); + $('#left-container').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); + $('#left-box').css({ 'left': (cont - $(window).scrollLeft() + 20) + 'px' }); + // $('.sub-nav-fixed').css({'left': (cont - $(window).scrollLeft() + 384) + 'px' }); + // $('.sub-nav-default').css({'left': 0 + 'px' }); + + resizeMRIDiv(); + resizeMapDiv(); + resizeSegDiv(); + resizeMapDiv2(); + resizeBlockfaceDiv(); + resizeComparisonViewerDiv(); + resizeInfoViewerGoogleBrain(); + resizeInfoViewerSegmentation(); + resizeInfoViewerMRI(); + resizeInfoViewerModels(); + resizeInfoViewerBlockface(); + resizeInfoViewerWalkthrough(); + if (parseInt($(".container").css("width")) < 768) { + if ($("#sub-nav").hasClass('sub-nav-fixed')) { + $("#sub-nav").removeClass('sub-nav-fixed').addClass('sub-nav-default'); + } + if ($("#left-box").hasClass('left-box-fixed')) { + $("#left-box").removeClass('left-box-fixed').addClass('left-box-default'); + } + } + + if ($(".six.columns").width() == 268 || $(".six.columns").width() == 300) { + document.getElementById("label").size = 25; + } else if ($(".six.columns").width() == 420) { + document.getElementById("label").size = 42; + } else { + document.getElementById("label").size = 36; + } + $(".ui-autocomplete").css("left", ($("html").width() - $("#cont").width()) / 2); + $(".ui-autocomplete").css("width", parseInt($("#left-container").css("width")) + (parseInt($("#left-container").css("margin-left")) - 5) * 2); + +}); + +$('.sub-nav-tab').live('click', function (event) { + + /*...prevent the default behaviour...*/ + event.preventDefault(); + + var tabInd = $(this).parent().attr('id').match(/[0-9]/); + // (Hauke) uncommented the loading as it did not load the google map portion correctly + // This change destroys the load by usage feature! + //LoadTabContent(tabInd); + + // var divId = $(this).parent().attr("id").replace("-link",''); + var divId = "tabs-" + tabInd; + var offsetTop = $("#" + divId).offset().top - 104; + window.scroll(0, offsetTop); + + /* ...remove the tab_link_selected class from current active link... */ + $('#sub-nav .ui-state-active').removeClass('ui-state-active'); + $('#sub-nav .ui-tabs-selected').removeClass('ui-tabs-selected'); + + /* ...and add it to the new active link */ + $(this).parent().addClass('ui-state-active ui-tabs-selected'); +}); + + +function centerToTab(number) { + var divId = "tabs-" + number; + var offsetTop = $("#" + divId).offset().top - 104; + jQuery('html, body').animate({ scrollTop: offsetTop }, 1000); + // window.scroll(0,offsetTop); +} + +$(function () { + $("#label").autocomplete({ + source: availableLabel, + delay: 0, + open: function (event, ui) { + $(this).autocomplete("widget").css({ + "left": ($("html").width() - $("#cont").width()) / 2, + "width": parseInt($("#left-container").css("width")) + (parseInt($("#left-container").css("margin-left")) - 5) * 2 + }); + } + }); +}); + + + +var search_result_mycarousel_itemList = []; + +var searchFunc = function (e) { + if (typeof user_1279 !== "undefined" && user_1279 == "1") { + return; // do nothing if user is not logged in + } + + search_result_mycarousel_itemList = []; + jQuery('#mycarousel').jcarousel('scroll', 0); + $(".brain_jcarousel").html('
      '); + + var search_str = $("#label").val().toLowerCase().trim().replace(/ /g, '_'); + // alert('search now for ' + search_str); + $("#mycarousel img").removeClass("found"); + $(window).scrollTop(0); + $("#left-box").removeClass('left-box-fixed').addClass('left-box-default').fadeIn('fast'); + $("#sub-nav").removeClass('sub-nav-fixed').addClass('sub-nav-default').fadeIn('fast'); + if ($("#label").val() == '') { + //return false; + current_itemList = mycarousel_itemList; + + load_carousel(mycarousel_itemList); + + } else { + slice_label.filter(function (sl) { + if (sl.label.toLowerCase() == search_str) { + //$("img[alt=\"Section "+sl.slice_num+"\"]").addClass('found'); + if (mycarousel_itemList_pic_num.indexOf(parseInt(sl.slice_num)) >= 0) { + var len = sl.slice_num.toString().length; + var img_num = ''; + for (i = 0; i < 4 - len; i++) { + img_num += '0'; + } + img_num += sl.slice_num; + search_result_mycarousel_itemList.push({ url: "./data/Blockface/TINY/2500_FINAL_HM_Blockface_" + img_num + "_MEDIUM_TINY.jpg", title: "Section " + sl.slice_num }); + + } + + } + }); + + load_carousel(search_result_mycarousel_itemList); + + } + if (e.which == 13) { + $(".ui-autocomplete").css("display", "none"); + } +}; +$("#label").live("blur", function () { + var default_value = "Search by structure or slice number"; //$(this).attr("rel"); + if ($(this).val() == "") { + $(this).val(default_value); + } +}).live("focus", function () { + var default_value = "Search by structure or slice number"; //$(this).attr("rel"); + if ($(this).val() == default_value) { + $(this).val(""); + } +}); +var addFoundBorder = function (e) { + if (current_itemList.length != mycarousel_itemList.length) { + $("#mycarousel img").addClass("found"); + } +} + +$('#search').live('click', searchFunc); +$('#search-icon-small').live('click', searchFunc); +//$(".jcarousel-next").live('click',searchFunc); +//$(".jcarousel-prev").live('click',searchFunc); +$(".ui-menu-item").live('click', searchFunc); +$("#search-box #label").live('keyup', function (e) { + if (e.which == 13) { + $(".ui-autocomplete").css("display", "none"); + $("#search-icon-small").trigger('click'); + } + + if ($("#search-box #label").val() == '') { + current_itemList = mycarousel_itemList; + jQuery('#mycarousel').jcarousel('scroll', 0); + $(".brain_jcarousel").html('
        '); + load_carousel(mycarousel_itemList); + } + else if ($(".ui-autocomplete-input").val() == 'Search by structure or slice number') { + $(".ui-autocomplete-input").val(''); + } + else { + // $("#search-icon-small").trigger('click'); + } +}); +$(".jcarousel-next").live('click', addFoundBorder); +$(".jcarousel-prev").live('click', addFoundBorder); + + +$("#reset-buttons").live('click', function () { + // $("#label").val(''); + $(".ui-autocomplete-input").val("Search by structure or slice number"); + $("#mycarousel img").removeClass("found"); + jQuery('#mycarousel').jcarousel('scroll', 0); + $(".brain_jcarousel").html('
          '); + load_carousel(mycarousel_itemList); +}); + +$('#g-right li img').live('click', function (event) { + if (typeof user_1279 !== "undefined" && user_1279 == "1") { + alert('This functionality is only available for users with a valid account. Go to thedigitalbrainlibrary.org/hm_web to sign-up.'); + return; // do nothing + } + + var n = $(this).attr("imageNumber"); + $('#g-right li img').removeClass('active'); + $(this).addClass('active'); + + // get real image number + var sr = jQuery(this).attr('src'); + // extract the number from the string + var idS = sr.split('_'); + var idSS = idS[4]; + var id = parseFloat(idSS).toFixed(0); + + SliceNumber = addZeros(id, 4); + update(id); +}); + +function findPosC(obj) { + var curleft = curtop = 0; + if (obj.offsetParent) { + do { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + } while (obj = obj.offsetParent); + return [curleft, curtop]; + } +} + +function putSliceNrAsThal(id) { + // update the mm location in the side view + var thal = -(id - 955) * 70 / 1000.0; + var pos = -1.27 * thal - 17; + var talpos = (thal / 0.97).toFixed(2); + jQuery('#slice-location-thal').html('' + thal + ' mm'); + jQuery('#slice-location-thal').css('left', pos + 'px'); + jQuery('.Talar').html('' + thal + ' mm (' + talpos + ' mm)'); +} + +function ACPCpos(id) { + var y = (49 / 2304 * (id - 55) - 316).toFixed(4); + jQuery('#AC-PC').css('top', y + 'px'); +} + +// update everything +function update(id, _maplocation, _mapZoom) { + SliceNumber = addZeros(id, 4); + putSliceNrAsThal(id); + ACPCpos(id); + updateBlockface(); + current_slice_path = 'https://s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/HM/Slice_' + addZeros(id, 4) + '/Result'; + if (typeof _maplocation != "undefined" && typeof _mapZoom != "undefined") + updateMap(SliceNumber, _maplocation, _mapZoom); + else + updateMap(); + mpr1.infoOverlayEnabled = true; + mpr1.setLockPosition(false, false, false); + pos = mpr1.getPosition(); + mrId = parseInt(id / 2400 * 511); + mpr1.setPosition(pos[0], pos[1], mrId); + mpr1.infoOverlayEnabled = false; + mpr1.setLockPosition(false, false, true); + mpr1.scoutLineColor = "rgb(127,29,49)"; + resizeMRIDiv(); + resizeMapDiv(); + resizeSegDiv(); + var medium_src = './data/Blockface/MEDIUM/2500_FINAL_HM_Blockface_' + SliceNumber + '_MEDIUM.jpg'; + medium_src = 'getBlockface.php?h=' + SliceNumber; + //medium_src = medium_src.replace("TINY","MEDIUM"); + $("#left-box img").attr("src", medium_src); + + $("#left-box #img-title").html("SECTION NUMBER " + SliceNumber); + + //This loads the label file. + var lab = new Image(); + lab.onload = function () { + can = document.getElementById('labeldisplay').getContext('2d'); + can.drawImage(lab, 0, 0, 521, 457); + //$('#type').text('ready'); + } + + lab.src = "/hm_web/data/Segmented/" + seglabel; + + //Shows labels on hover over blockface + $('#blockface_label').mousemove(function (e) { + bfloc = findPosC(this); + cX = e.clientX; + cY = e.pageY; + bX = bfloc[0]; + bY = bfloc[1]; + sY = parseFloat(jQuery('#segmented').css('top')); + sX = parseFloat(jQuery('#segmented').css('left')); + var mouseX = -sX + cX - bfloc[0] + 0.5; + var mouseY = -sY + cY - bfloc[1] + 10; + var can = document.getElementById('labeldisplay').getContext('2d'); + var imageData = can.getImageData(mouseX, mouseY, 1, 1); + var labelValue = imageData.data; + $('#type').text(nameForKey(labelValue[0])[0].replace(/_/g, ' ')// + ' ' + labelValue[0] + ' ' + mouseX + ' ' + mouseY + ' ' + sX + ' ' + sY + ); + }); +} + +function resizeMapDiv() { + //Resize the height of the div containing the map. + //Do not call any map methods here as the resize is called before the map is created. + var d = document.getElementById("map"); + var offsetTop = 0; + var w = jQuery('#tabs-1').width(); + var w1 = w * .974; + var h = w * .917; h = h.toFixed(0); + jQuery('#map').css({ 'width': w1 + "px" }); + jQuery('#map').css({ 'height': h + "px" }); + jQuery('#tabs-1').css({ 'height': h + "px" }); + if (h >= 0) { + jQuery('#map').css({ 'height': h + "px" }); + } + for (var elem = d; elem != null; elem = elem.offsetParent) { + offsetTop += elem.offsetTop; + } + jQuery('#open-info-viewer-googlebrain').css('top', -jQuery('#map').height() - 37); + jQuery('#open-info-viewer-googlebrain').css('left', jQuery('#map').width() - 17); + jQuery('#open-info-viewer-googlebrain').fadeIn('slow'); + + jQuery('#open-map-overlay').css('top', -jQuery('#map').height() - 33); + jQuery('#open-map-overlay').css('left', jQuery('#map').width() - 18); + jQuery('#open-map-overlay').fadeIn('slow'); + + jQuery('#open-add-hotspot-location').css('top', -jQuery('#map').height() - 29); + jQuery('#open-add-hotspot-location').css('left', jQuery('#map').width() - 18); + jQuery('#open-add-hotspot-location').fadeIn('slow'); + + jQuery('#open-add-hotspot-notice-location').css('top', -jQuery('#map').height() - 25); + jQuery('#open-add-hotspot-notice-location').css('left', jQuery('#map').width() - 18); + jQuery('#open-add-hotspot-notice-location').fadeIn('slow'); + + jQuery('#open-comparison-viewer').css('top', -jQuery('#map').height() - 22); + jQuery('#open-comparison-viewer').css('left', jQuery('#map').width() - 17); + //jQuery('#open-comparison-viewer').fadeIn('slow'); + +} + +function resizeMapDiv2() { + //Resize the height of the div containing the map. + //Do not call any map methods here as the resize is called before the map is created. + var d = document.getElementById("map2"); + var offsetTop = 0; + var w = jQuery('#map-overlay').width(); + var h = jQuery('#map-overlay').height(); + jQuery('#map2').width(w - 12); + if (h >= 0) { + //jQuery('#map2').css({'height': (h-20)+"px"}); + jQuery('#map2').height(h - 12); + } + var w2 = jQuery(window).width(); + jQuery('#map-overlay').css('left', (w2 - w) / 2.0); + for (var elem = d; elem != null; elem = elem.offsetParent) { + offsetTop += elem.offsetTop; + } +} + +function resizeComparisonViewerDiv() { + var api = jQuery('#comparison-viewer').data("overlay"); + if (typeof api === "undefined") + return; + + var w = jQuery('#comparison-viewer').width(); + var h = jQuery('#comparison-viewer').height(); + jQuery('#comparison-viewer-div').width(w); + if (h >= 0) { + jQuery('#comparison-viewer-div').height(h); + } + jQuery('#comparison-viewer-right').css('width', w / 2 - 2); + jQuery('#comparison-viewer-left').css('width', w / 2 - 2); + jQuery('#map3').width(w / 2 - 2); + jQuery('#map4').width(w / 2 - 2); + jQuery('#map3').height(h - 2); + jQuery('#map4').height(h - 2); + var w2 = jQuery(window).width(); + jQuery('#comparison-viewer').css('left', (w2 - w) / 2.0); +} + +function resizeBlockfaceDiv() { + var api = jQuery('#blockface-overlay').data("overlay"); + if (typeof api === "undefined") + return; + + var w = jQuery('#blockface-overlay').width(); + var h = jQuery('#blockface-overlay').height(); + jQuery('#blockface-overlay-div').width(w - 12); + if (h >= 0) { + jQuery('#blockface-overlay-div').height(h - 12); + } + jQuery('#blockface-overlay-div').children().remove(); + + //jQuery('#blockface-overlay-div').css('background', 'url(./data/Blockface/LARGE/HM_Blockface_' + SliceNumber + '.jpg) center center no-repeat'); + //var url = 'url(getBlockface.php?h='+SliceNumber + '&type=large) center center no-repeat'; + var url = 'url(getBlockface.php?h=' + SliceNumber + '&type=large) center center no-repeat'; + jQuery('#blockface-overlay-div').css('background', url); + if (w < h) { + jQuery('#blockface-overlay-div').css('background-size', (w - 12) + 'px auto'); + // jQuery('#blockface-overlay-div').append("
          "); + } else { + jQuery('#blockface-overlay-div').css('background-size', 'auto ' + (h - 12) + 'px'); + var w2 = jQuery(window).width(); + jQuery('#blockface-overlay').css('left', (w2 - w) / 2.0); + } + +} + +function resizeMRIDiv() { + var d = document.getElementById("tabs-3-1"); + var e = document.getElementById("tabs-3-2"); + var f = document.getElementById("tabs-3-3"); + var g = document.getElementById("win0"); + var h = document.getElementById("win1"); + var i = document.getElementById("win2"); + var w = jQuery('#tabs-1').width(); + var x = w * .978; x = x.toFixed(0); + var y = x * .83; y = y.toFixed(0); + var z = y * .1; z = z.toFixed(0); + d.style.width = e.style.width = f.style.width = d.style.height = e.style.height = f.style.height = x + "px"; + g.style.width = h.style.width = i.style.width = g.style.height = h.style.height = i.style.height = x + "px"; + + + // g.style.width=h.style.width=i.style.width=y+"px"; + // g.style.height=h.style.height=i.style.height=x*.8+"px"; + jQuery('#window0').attr('width', y); + jQuery('#window1').attr('width', y); + jQuery('#window2').attr('width', y); + jQuery('#window0').attr('height', y); + jQuery('#window1').attr('height', y); + jQuery('#window2').attr('height', y); + jQuery('#window0').css({ 'margin-left': z + 'px' }); + jQuery('#window1').css({ 'margin-left': z + 'px' }); + jQuery('#window2').css({ 'margin-left': z + 'px' }); + jQuery('#window0').css({ 'margin-bottom': z + 'px' }); + jQuery('#window1').css({ 'margin-bottom': z + 'px' }); + jQuery('#window2').css({ 'margin-bottom': z + 'px' }); + mpr1.update(); + mpr2.update(); + mpr3.update(); + + jQuery('#open-info-viewer-MRI').css('top', -jQuery('#win2').height() - 35); + jQuery('#open-info-viewer-MRI').css('left', jQuery('#win2').width() + 4); + jQuery('#open-info-viewer-MRI').fadeIn('slow'); +} +function resizeSegDiv() { + //Resize the height of the div containing the map. + //Do not call any map methods here as the resize is called before the map is created. + var w = jQuery('#tabs-1').width(); + var x = w * .977; x = x.toFixed(0); + var y = x * .88 - 18; y = y.toFixed(0); + jQuery('#blockface_label').css({ 'width': x + "px" }); + jQuery('#blockface_label').css({ 'height': y + "px" }); + + jQuery('#open-info-viewer-segmentation').css('top', -jQuery('#blockface_label').height() - 74); + jQuery('#open-info-viewer-segmentation').css('left', jQuery('#blockface_label').width() + 1); + jQuery('#open-info-viewer-segmentation').fadeIn('slow'); + +} + +function resizeInfoViewerGoogleBrain() { + var w = jQuery(window).width(); + var x = w * .5; x = x.toFixed(0); + var y = x * .5; y = y.toFixed(0); + jQuery('#info-viewer-googlebrain').css({ 'width': x + "px" }); + // var y=jQuery('#info-viewer-googlebrain-content').height(); y= y.toFixed(0); + jQuery('#info-viewer-googlebrain').css({ 'height': y + "px" }); + + // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); + jQuery('#info-viewer-googlebrain').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); + // jQuery('#info-viewer-googlebrain').fadeIn('slow'); + +} + +function resizeInfoViewerSegmentation() { + var w = jQuery(window).width(); + var x = w * .5; x = x.toFixed(0); + var y = x * .5; y = y.toFixed(0); + jQuery('#info-viewer-segmentation').css({ 'width': x + "px" }); + // var y=jQuery('#info-viewer-segmentation-content').height(); y= y.toFixed(0); + jQuery('#info-viewer-segmentation').css({ 'height': y + "px" }); + + // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); + jQuery('#info-viewer-segmentation').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); + // jQuery('#info-viewer-googlebrain').fadeIn('slow'); + +} +function resizeInfoViewerMRI() { + var w = jQuery(window).width(); + var x = w * .5; x = x.toFixed(0); + var y = x * .5; y = y.toFixed(0); + jQuery('#info-viewer-MRI').css({ 'width': x + "px" }); + // var y=jQuery('#info-viewer-MRI-content').height(); y= y.toFixed(0); + jQuery('#info-viewer-MRI').css({ 'height': y + "px" }); + + // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); + jQuery('#info-viewer-MRI').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); + // jQuery('#info-viewer-googlebrain').fadeIn('slow'); + +} +function resizeInfoViewerModels() { + var w = jQuery(window).width(); + var x = w * .5; x = x.toFixed(0); + var y = x * .5; y = y.toFixed(0); + jQuery('#info-viewer-models').css({ 'width': x + "px" }); + // var y=jQuery('#info-viewer-models-content').height(); y= y.toFixed(0); + jQuery('#info-viewer-models').css({ 'height': y + "px" }); + + // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); + jQuery('#info-viewer-models').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); + // jQuery('#info-viewer-googlebrain').fadeIn('slow'); + +} +function resizeInfoViewerBlockface() { + var w = jQuery(window).width(); + var x = w * .5; x = x.toFixed(0); + var y = x * .5; y = y.toFixed(0); + jQuery('#info-viewer-blockface').css({ 'width': x + "px" }); + // var y=jQuery('#info-viewer-blockface-content').height(); y= y.toFixed(0); + jQuery('#info-viewer-blockface').css({ 'height': y + "px" }); + + // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); + // console.log('margin is:' + (parseFloat(w)-x)/2.0); + // console.log('w is: ' + w + ' x is: ' + x); + jQuery('#info-viewer-blockface').css('left', (parseFloat(w) - x) / 2.0); + // jQuery('#info-viewer-googlebrain').fadeIn('slow'); + +} +function resizeInfoViewerWalkthrough() { + var w = jQuery(window).width(); + var x = w * .5; x = x.toFixed(0); + var y = x * .5; y = y.toFixed(0); + jQuery('#info-viewer-walkthrough').css({ 'width': x + "px" }); + // var y=jQuery('#info-viewer-walkthrough-content').height(); y= y.toFixed(0); + jQuery('#info-viewer-walkthrough').css({ 'height': y + "px" }); + + // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); + jQuery('#info-viewer-walkthrough').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); + // jQuery('#info-viewer-googlebrain').fadeIn('slow'); + +} + +function updateBlockface() { + bfname = "2500_FINAL_HM_Blockface_" + SliceNumber + "_MEDIUM.jpg"; + segname = "colorwash_snapshot_" + SliceNumber + ".jpg"; + seglabel = "grayscale_snapshot_" + SliceNumber + ".png"; + talairach = "grid.png"; + $('#Blockface').attr('src', './data/Blockface/MEDIUM/"+bfname+"'); + // $('.Blockface').css('background-repeat', "no-repeat"); + $('.BlockfaceLabel').css('background', "url('/hm_web/data/Segmented/" + segname + "')"); + $('.BlockfaceLabel').css('background-repeat', "no-repeat"); + $('#section').text('Section ' + SliceNumber); +} + +function showMe(it, box) { + // var vis = (box.checked) ? "block" : "none"; + document.getElementById(it).style.display = vis; +} + +//load label names +function nameForKey(num) { + switch (num) { + case 0: { return [""]; } + case 1: { return ["Hippocampus"]; } + case 2: { return ["Putamen"]; } + case 3: { return ["Superior_Frontal_Gyrus"]; } + case 4: { return ["Caudate"]; } + case 5: { return [""]; } + case 6: { return [""]; } + case 7: { return ["Cingulate_Gyrus"]; } + case 8: { return ["Paracentral_Lobule"]; } + case 9: { return ["Precentral_Gyrus"]; } + case 10: { return ["Postcentral_Gyrus"]; } + case 11: { return ["Parietal_Operculum"]; } + case 12: { return ["Frontal_Operculum"]; } + case 13: { return ["Insular_Gyrus"]; } + case 14: { return ["Claustrum"]; } + case 15: { return ["Planum_Polare"]; } + case 16: { return ["Transverse_Temporal_Gyrus"]; } + case 17: { return ["Planum_Temporale"]; } + case 18: { return ["Superior_Temporal_Gyrus"]; } + case 19: { return ["Middle_Temporal_Gyrus"]; } + case 20: { return ["Inferior_Temporal_Gyrus"]; } + case 21: { return ["Fusiform_Gyrus"]; } + case 22: { return ["Parahippocampal_Gyrus"]; } + case 23: { return ["Entorhinal_Cortex"]; } + case 24: { return ["Amygdala"]; } + case 25: { return ["Optic_Tract"]; } + case 26: { return ["Internal_Globus_Pallidus"]; } + case 27: { return ["Stria_Terminalis"]; } + case 28: { return ["Thalamus"]; } + case 29: { return ["Zona_Incerta"]; } + case 30: { return ["Substantia_Nigra"]; } + case 31: { return ["Subthalamic_Nucleus"]; } + case 32: { return ["Supramarginal_Gyrus"]; } + case 33: { return ["Lateral_Geniculate_Nucleus"]; } + case 34: { return ["Medial_Geniculate_Nucleus"]; } + case 35: { return [""]; } + case 36: { return ["Red_Nucleus"]; } + case 37: { return [""]; } + case 38: { return ["Inferior_Colliculus"]; } + case 39: { return [""]; } + case 40: { return [""]; } + case 41: { return [""]; } + case 42: { return ["Periaqueductal_Gray"]; } + case 43: { return [""]; } + case 44: { return ["Pineal_Gland"]; } + case 45: { return ["Hippocampal_Commissure"]; } + case 46: { return ["Precuneus_Gyrus"]; } + case 47: { return ["Lingual_Gyrus"]; } + case 48: { return [""]; } + case 49: { return ["Superior_Parietal_Lobule"]; } + case 50: { return ["Occipital_Gyrus"]; } + case 51: { return ["Angular_Gyrus"]; } + case 52: { return ["Striate_Area"]; } + case 53: { return ["Parietooccipital_Cortex"]; } + case 54: { return ["Temporooccipital_Cortex"]; } + case 55: { return ["Hypothalamus"]; } + case 56: { return ["Mammillary_Bodies"]; } + case 57: { return ["Oculomotor_Nerve"]; } + case 58: { return [""]; } + case 59: { return ["Middle_Frontal_Gyrus"]; } + case 60: { return ["Ambiens_Gyrus"]; } + case 61: { return ["Basal_Nucleus"]; } + case 62: { return ["Piriform_Cortex"]; } + case 63: { return ["Accumbens_Nucleus"]; } + case 64: { return ["Paraterminal_Gyrus"]; } + case 65: { return ["Inferior_Frontal_Gyrus_Opercular"]; } + case 66: { return ["Fundus_Region"]; } + case 67: { return ["Straight_Gyrus"]; } + case 68: { return ["Posteromedial_Orbital_Lobule"]; } + case 69: { return ["Area_Piriformis_Insulae"]; } + case 70: { return ["Posterior_Orbital_Gyrus"]; } + case 71: { return ["Inferior_Frontal_Gyrus_Triangular"]; } + case 72: { return ["Basal_Operculum"]; } + case 73: { return ["Medial_Orbital_Gyrus"]; } + case 74: { return ["Inferior_Rostal_Gyrus"]; } + case 75: { return ["Subcallosal_Gyrus"]; } + case 76: { return ["Inferior_Frontal_Gyrus_Orbital"]; } + case 77: { return ["Lateral_Orbital_Gyrus"]; } + case 78: { return ["Middle_Frontopolar_Gyrus"]; } + case 79: { return ["Inferior_Frontopolar_Gyrus"]; } + case 80: { return ["Frontomarginal_Gyrus"]; } + case 81: { return ["Superior_Rostral_Gyrus"]; } + case 82: { return ["Intermediate_Orbital_Gyrus"]; } + case 83: { return ["Declive"]; } + case 84: { return ["Central_Lobule"]; } + case 85: { return ["Folium"]; } + case 86: { return ["Lingula"]; } + case 87: { return ["Tuber"]; } + case 88: { return ["Culmen"]; } + case 89: { return ["Uvula"]; } + case 90: { return ["Nodulus"]; } + case 91: { return ["Pyramis"]; } + case 92: { return ["Ala_of_the_Central_Lobule"]; } + case 93: { return ["Anterior_Quadrangular_Lobule"]; } + case 94: { return ["Posterior_Quadrangular_Lobule"]; } + case 95: { return ["Superior_Semilunar_Lobule"]; } + case 96: { return ["Inferior_Semilunar_Lobule"]; } + case 97: { return ["Gracile_Lobule"]; } + case 98: { return ["Biventral_Lobule"]; } + case 99: { return ["Tonsilla"]; } + case 100: { return [""]; } + case 101: { return ["Flocculus"]; } + case 102: { return ["White_Matter"]; } + case 103: { return [""]; } + case 104: { return [""]; } + case 105: { return ["Optic_Chiasm"]; } + case 106: { return ["Subcallosal_Area"]; } + case 107: { return ["Olfactory_Area"]; } + case 108: { return ["Perirhinal_Cortex"]; } + case 109: { return [""]; } + case 110: { return ["Bed_Nucleus_of_the_Stria"]; } + case 111: { return ["External_Globus_Pallidus"]; } + case 112: { return ["Anterior_Commissure"]; } + case 113: { return [""]; } + case 114: { return [""]; } + case 115: { return [""]; } + case 116: { return [""]; } + case 117: { return [""]; } + case 118: { return [""]; } + case 119: { return [""]; } + case 120: { return [""]; } + case 121: { return [""]; } + case 122: { return ["Cerebral_Peduncle"]; } + case 123: { return [""]; } + case 124: { return [""]; } + case 125: { return ["Third_Ventricle"]; } + case 126: { return [""]; } + case 127: { return ["Fornix"]; } + case 128: { return [""]; } + case 129: { return ["Internal_Capsule"]; } + case 130: { return ["Lateral_Ventricle"]; } + case 131: { return ["Striatal_Cell_Bridges"]; } + case 132: { return ["Pons"]; } + case 133: { return ["Superior_Cerebellar_Peduncle"]; } + case 134: { return [""]; } + case 135: { return [""]; } + case 136: { return [""]; } + case 137: { return [""]; } + case 138: { return [""]; } + case 139: { return [""]; } + case 140: { return ["Fimbria"]; } + case 141: { return [""]; } + case 142: { return ["Posterior_Commissure"]; } + case 143: { return ["Cerebral_Aqueduct"]; } + case 144: { return [""]; } + case 145: { return ["Optic_Radiation"]; } + case 146: { return ["Superior_Colliculus"]; } + case 147: { return ["Indusium_Griseum"]; } + case 148: { return ["Area_Orbitoinsularis"]; } + case 149: { return ["Fourth_Ventricle"]; } + case 150: { return ["Inferior_Olivary_Nucleus"]; } + case 151: { return ["Cortex"]; } + case 152: { return ["Lesion"]; } + case 153: { return ["Olfactory_Tract"]; } + case 154: { return ["Anterior_Olfactory_Nucleus"]; } + case 155: { return ["Septal_Nuclei"]; } + case 156: { return ["Simple_Lobule"]; } + case 157: { return ["Dentate_Nucleus"]; } + case 158: { return ["Middle_Cerebellar_Peduncle"]; } + case 159: { return [""]; } + case 160: { return ["Removed_MTL_Guess"]; } + case 161: { return ["Surgical_Clip"]; } + default: { return [" "]; } + } +} + +var cX = 0; var cY = 0; var rX = 0; var rY = 0; + +function UpdateCursorPosition(e) { cX = e.pageX; cY = e.pageY; } +function UpdateCursorPositionDocAll(e) { cX = event.clientX; cY = event.clientY; } + +if (document.all) { document.onmousemove = UpdateCursorPositionDocAll; } +else { document.onmousemove = UpdateCursorPosition; } + +function AssignPosition(d) { + if (self.pageYOffset) { + rX = self.pageXOffset; + rY = self.pageYOffset; + } + else if (document.documentElement && document.documentElement.scrollTop) { + rX = document.documentElement.scrollLeft; + rY = document.documentElement.scrollTop; + } + else if (document.body) { + rX = document.body.scrollLeft; + rY = document.body.scrollTop; + } + if (document.all) { + cX += rX; + cY += rY; + } + // d.style.left = (cX+10) + "px"; + // d.style.top = (cY+10) + "px"; +} + +function HideContent(d) { + if (d.length < 1) { return; } + document.getElementById(d).style.display = "none"; +} +function ShowContent(d) { + if (d.length < 1) { return; } + var dd = document.getElementById(d); + dd.style.display = "block"; + // bfloc = findPosC(document.getElementById('blockface_nolabel')) + // var mouseX = e.pageX - bfloc[0]; + // var mouseY = e.pageY - bfloc[1]; +} +function ReverseContentDisplay(d) { + if (d.length < 1) { return; } + var dd = document.getElementById(d); + AssignPosition(dd); + if (dd.style.display == "none") { dd.style.display = "block"; } + else { dd.style.display = "none"; } +} + +var locationBySlice = {}; +locationBySlice['0199'] = { centerLat: 44.43137917475613, centerLng: -35.5078125 }; +locationBySlice['0235'] = { centerLat: 42.38852556705306, centerLng: -34.1015625 }; +locationBySlice['0271'] = { centerLat: 28.726182356515025, centerLng: -17.2265625 }; +locationBySlice['0307'] = { centerLat: 39.195599922646224, centerLng: -21.4453125 }; +locationBySlice['0343'] = { centerLat: 40.27696080353267, centerLng: -5.9765625 }; +locationBySlice['0379'] = { centerLat: 71.2291387369125, centerLng: -90.3515625 }; +locationBySlice['0415'] = { centerLat: 71.67642594903772, centerLng: -93.8671875 }; +locationBySlice['0433'] = { centerLat: 65.85538134683627, centerLng: -93.1640625 }; +locationBySlice['0487'] = { centerLat: 65.85538134683627, centerLng: -93.1640625 }; +locationBySlice['0523'] = { centerLat: 65.85538134683627, centerLng: -90.3515625 }; +locationBySlice['0559'] = { centerLat: 65.85538134683627, centerLng: -87.5390625 }; +locationBySlice['0595'] = { centerLat: 65.85538134683627, centerLng: -84.0234375 }; +locationBySlice['0631'] = { centerLat: 65.85538134683627, centerLng: -87.5390625 }; +locationBySlice['0649'] = { centerLat: 65.85538134683627, centerLng: -88.2421875 }; +locationBySlice['0667'] = { centerLat: 65.85538134683627, centerLng: -82.6171875 }; +locationBySlice['0703'] = { centerLat: 65.85538134683627, centerLng: -86.1328125 }; +locationBySlice['0721'] = { centerLat: 65.85538134683627, centerLng: -88.2421875 }; +locationBySlice['0739'] = { centerLat: 65.85538134683627, centerLng: -85.4296875 }; +locationBySlice['0775'] = { centerLat: 65.85538134683627, centerLng: -79.8046875 }; +locationBySlice['0793'] = { centerLat: 65.85538134683627, centerLng: -80.5078125 }; +locationBySlice['0811'] = { centerLat: 65.85538134683627, centerLng: -85.4296875 }; +locationBySlice['0847'] = { centerLat: 65.85538134683627, centerLng: -75.5859375 }; +locationBySlice['0883'] = { centerLat: 65.85538134683627, centerLng: -76.9921875 }; +locationBySlice['0901'] = { centerLat: 65.85538134683627, centerLng: -72.7734375 }; +locationBySlice['0919'] = { centerLat: 65.85538134683627, centerLng: -74.8828125 }; +locationBySlice['0955'] = { centerLat: 65.85538134683627, centerLng: -72.7734375 }; +locationBySlice['0973'] = { centerLat: 65.85538134683627, centerLng: -77.6953125 }; +locationBySlice['0991'] = { centerLat: 65.85538134683627, centerLng: -74.8828125 }; +locationBySlice['1027'] = { centerLat: 65.85538134683627, centerLng: -69.2578125 }; +locationBySlice['1063'] = { centerLat: 65.85538134683627, centerLng: -63.6328125 }; +locationBySlice['1099'] = { centerLat: 65.85538134683627, centerLng: -64.3359375 }; +locationBySlice['1135'] = { centerLat: 65.85538134683627, centerLng: -63.6328125 }; +locationBySlice['1171'] = { centerLat: 65.85538134683627, centerLng: -60.8203125 }; +locationBySlice['1207'] = { centerLat: 65.85538134683627, centerLng: -58.0078125 }; +locationBySlice['1855'] = { centerLat: 65.85538134683627, centerLng: -72.7734375 }; +locationBySlice['1891'] = { centerLat: 65.85538134683627, centerLng: -72.7734375 }; +locationBySlice['1927'] = { centerLat: 65.85538134683627, centerLng: -76.9921875 }; +locationBySlice['1963'] = { centerLat: 65.85538134683627, centerLng: -78.3984375 }; +locationBySlice['1999'] = { centerLat: 65.85538134683627, centerLng: -86.1328125 }; +locationBySlice['2035'] = { centerLat: 24.963092337069753, centerLng: -5.9765625 }; +locationBySlice['2071'] = { centerLat: 22.38760541276085, centerLng: -3.1640625 }; +locationBySlice['2107'] = { centerLat: 27.48579875372881, centerLng: -5.9765625 }; +locationBySlice['2143'] = { centerLat: 36.982317556959366, centerLng: -21.4453125 }; +locationBySlice['2179'] = { centerLat: 45.426939316192815, centerLng: -22.8515625 }; +locationBySlice['2215'] = { centerLat: 56.045621879849854, centerLng: -34.1015625 }; +locationBySlice['2251'] = { centerLat: 59.77682973372056, centerLng: -39.7265625 }; +locationBySlice['2287'] = { centerLat: 60.47722221435968, centerLng: -52.3828125 }; +locationBySlice['2359'] = { centerLat: 36.982317556959366, centerLng: 1.0546875 }; + +function tileURL(a, b) { + // pervent wrap around + if (a.y < 0 || a.y >= (1 << b)) { + return null; + } + if (a.x < 0 || a.x >= (1 << b)) { + return null; + } + var c = Math.pow(2, b); + var d = a.x; + var e = a.y; + var f = "t"; + for (var g = 0; g < b; g++) { + c = c / 2; + if (e < c) { + if (d < c) { f += "q" } + else { f += "r"; d -= c } + } else { + if (d < c) { f += "t"; e -= c } + else { f += "s"; d -= c; e -= c } + } + } + subdirs = 3; + tmp = ""; + if (f.length >= subdirs) { // subdivide into sub-directories + for (i = 0; i < subdirs; i++) { + tmp += f.charAt(i) + "/"; + } + } + tmp += f; + return current_slice_path + "/" + tmp + ".jpg"; +} +function tileURL2(a, b) { + // pervent wrap around + if (a.y < 0 || a.y >= (1 << b)) { + return null; + } + if (a.x < 0 || a.x >= (1 << b)) { + return null; + } + var c = Math.pow(2, b); + var d = a.x; + var e = a.y; + var f = "t"; + for (var g = 0; g < b; g++) { + c = c / 2; + if (e < c) { + if (d < c) { f += "q" } + else { f += "r"; d -= c } + } else { + if (d < c) { f += "t"; e -= c } + else { f += "s"; d -= c; e -= c } + } + } + subdirs = 3; + tmp = ""; + if (f.length >= subdirs) { // subdivide into sub-directories + for (i = 0; i < subdirs; i++) { + tmp += f.charAt(i) + "/"; + } + } + tmp += f; + return alternate_slice_path + "/" + tmp + ".jpg"; +} + +function updateMap(_SliceNumber, _maplocation, _zoom) { + var maxZoom = 11; + var initialZoom = 1; + + // change zoom level based on size of region + + if (typeof _zoom != "undefined") + initialZoom = _zoom; + + var customMapOptions = { + getTileUrl: tileURL, + isPng: false, + maxZoom: maxZoom, + minZoom: 1, + tileSize: new google.maps.Size(256, 256), + radius: 1738000, + name: "HM", + streetViewControl: false + }; + if (typeof SliceNumber !== "undefined" + && (SliceNumber < 379 || SliceNumber > 1999)) { + initialZoom = 0; + maxZoom = 10; + var customMapOptions = { + getTileUrl: tileURL, + isPng: false, + maxZoom: maxZoom, + minZoom: 0, + tileSize: new google.maps.Size(256, 256), + radius: 1738000, + name: "HM", + streetViewControl: false + }; + } + if (typeof SliceNumber !== "undefined" + && (SliceNumber < 91 || SliceNumber > 2359)) { + initialZoom = -1; + maxZoom = 9; + var customMapOptions = { + getTileUrl: tileURL, + isPng: false, + maxZoom: maxZoom, + minZoom: -1, + tileSize: new google.maps.Size(256, 256), + radius: 1738000, + name: "HM", + streetViewControl: false + }; + } + var customMapType = new google.maps.ImageMapType(customMapOptions); + + var centreLat = 66.70383915858723; + var centreLon = -48.1640625; + if (typeof SliceNumber !== "undefined" && + typeof locationBySlice[SliceNumber] !== "undefined") { + centreLat = locationBySlice[SliceNumber].centerLat; + centreLon = locationBySlice[SliceNumber].centerLng; + } + if (typeof _maplocation != "undefined") { + centreLat = _maplocation[0]; + centreLon = _maplocation[1]; + } + + var mycenter = new google.maps.LatLng(centreLat, centreLon); + var myOptions = { + center: mycenter, + zoom: initialZoom, + mapTypeControlOptions: { + mapTypeIds: ['map'] + }, + mapTypeId: 'map', + backgroundColor: "#FFFFFF", + zoomControl: true, + scrollwheel: true, + streetViewControl: false, + draggableCursor: 'crosshair', + overviewMapControl: true, + overviewMapControlOptions: { + position: google.maps.ControlPosition.RIGHT_TOP + }, + zoomControlOptions: { + style: google.maps.ZoomControlStyle.DEFAULT + } + }; + map = new google.maps.Map(document.getElementById("map"), myOptions); + map.mapTypes.set('HM', customMapType); + map.setMapTypeId('HM'); + //map.enableScrollWheelZoom(); + //map.enableContinuousZoom(); + + google.maps.event.addListener(map, "bounds_changed", function (event) { + // sO.draw(); + // line drawn on screen is 100px == 200micron + var mz = customMapOptions.maxZoom; + var st = document.getElementById('scaleText'); + // if (typeof str === "undefined") + // return; + var zoomDif = (mz + 1) - map.getZoom(); + var dif = 20 / zoomDif; // 20x is our max zoom, added factor of 2.0 + var siz1 = (50 * 0.37 * Math.pow(2, zoomDif - 1)); + var unit1 = 'µm '; + if (siz1 > 1000) { + siz1 = (siz1 / 1000.0).toFixed(1); + unit1 = 'mm '; + } + var siz2 = (20 / Math.pow(2, zoomDif - 1)).toFixed(2); + st.innerHTML = siz1 + unit1 //+ "[" + siz2 + "x" + "]"; + jQuery('.gmnoprint a').html('Google Terms of Use'); + }); + + + google.maps.event.addListener(map, 'mousewheel DOMMouseScroll', function (e) { + if (!e) { + e = window.event + } + if (e.preventDefault) { + e.preventDefault() + } + e.returnValue = false; + }); + + // switch on comparison viewer if slice has a comparison slice available + if (typeof SliceNumber !== "undefined") { + var found = false; + for (var i = 0; i < alternate_pairings.length; i++) { + if (alternate_pairings[i][0] == SliceNumber) { + jQuery('#open-comparison-viewer').fadeIn('slow'); + found = true; + break; + } + } + if (found == false) + jQuery('#open-comparison-viewer').fadeOut(); + } +} + +/*Pairing for comparison viewer*/ +var alternate_slice_path = "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/GoogleBrain/HM/Slice_1207/Result"; +var alternate_pairings = [["1243", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0009/Slice_1234/Result"], +["1171", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0015/Slice_1162/Result"], +["0811", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0001/Slice_0718/Result"], +["1315", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0009/Slice_1306/Result"], +["0739", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0009/Slice_0730/Result"], +["0955", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0009/Slice_0947/Result"], +["1027", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0015/Slice_0910/Result"] +]; + +if (user_name == "jannese") { + alternate_pairings.push(["1207", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-ME-0028/Slice_0671/Result"]); +} + +var shift_3_to_4 = [0, 0]; + +// Comparison viewer update +function updateMap34() { + + alternate_slice_path = "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/RM/Slice_1234/Result"; + for (var i = 0; i < alternate_pairings.length; i++) { + if (SliceNumber == alternate_pairings[i][0]) { + alternate_slice_path = alternate_pairings[i][1]; + break; + } + } + var customMapOptions = { + getTileUrl: tileURL, + isPng: false, + maxZoom: 11, + minZoom: 0, + tileSize: new google.maps.Size(256, 256), + radius: 1738000, + name: "HM", + streetViewControl: false + }; + var customMapType = new google.maps.ImageMapType(customMapOptions); + var customMapOptions2 = { + getTileUrl: tileURL2, + isPng: false, + maxZoom: 11, + minZoom: 0, + tileSize: new google.maps.Size(256, 256), + radius: 1738000, + name: "HM", + streetViewControl: false + }; + var customMapType2 = new google.maps.ImageMapType(customMapOptions2); + + initialZoom = 1; + var centreLat = 66.70383915858723; + var centreLon = -48.1640625; + if (typeof SliceNumber !== "undefined" && + typeof locationBySlice[SliceNumber] !== "undefined") { + centreLat = locationBySlice[SliceNumber].centerLat; + centreLon = locationBySlice[SliceNumber].centerLng; + } + + var mycenter = new google.maps.LatLng(centreLat, centreLon); + var myOptions = { + center: mycenter, + zoom: initialZoom, + mapTypeControlOptions: { + mapTypeIds: ['map3'] + }, + mapTypeId: 'map3', + backgroundColor: "#FFFFFF", + draggableCursor: 'crosshair', + zoomControl: true, + scrollwheel: true, + streetViewControl: false, + overviewMapControl: true, + overviewMapControlOptions: { + opened: true, + position: google.maps.ControlPosition.RIGHT_TOP + }, + zoomControlOptions: { + style: google.maps.ZoomControlStyle.DEFAULT + } + }; + map3 = new google.maps.Map(document.getElementById("map3"), myOptions); + map3.mapTypes.set('HM', customMapType2); + map3.setMapTypeId('HM'); + map4 = new google.maps.Map(document.getElementById("map4"), myOptions); + map4.mapTypes.set('HM', customMapType); + map4.setMapTypeId('HM'); + //map.enableScrollWheelZoom(); + //map.enableContinuousZoom(); + + google.maps.event.addListener(map4, "zoom_changed", function (event) { + var zoom = map4.getZoom(); + map3.setZoom(zoom); + }); + google.maps.event.addListener(map4, "center_changed", function (event) { + var center = map4.getCenter(); + var newPos = new google.maps.LatLng(center.lat() - shift_3_to_4[0], center.lng() - shift_3_to_4[1]); + + map3.setCenter(newPos); // keep the old shift + }); + + google.maps.event.addListener(map3, "drag", function () { + var cent3 = map3.getCenter(); + var cent4 = map4.getCenter(); + shift_3_to_4 = [cent4.lat() - cent3.lat(), cent4.lng() - cent3.lng()]; + }); + + google.maps.event.addListener(map3, "bounds_changed", function (event) { + + // sO.draw(); + // line drawn on screen is 100px == 200micron + var mz = customMapOptions.maxZoom; + var st = document.getElementById('scaleText2'); + var zoomDif = (mz + 1) - map3.getZoom(); + var dif = 20 / zoomDif; // 20x is our max zoom, added factor of 2.0 + var siz1 = (50 * 0.37 * Math.pow(2, zoomDif - 1)); + var unit1 = 'µm '; + if (siz1 > 1000) { + siz1 = (siz1 / 1000.0).toFixed(1); + unit1 = 'mm '; + } + var siz2 = (20 / Math.pow(2, zoomDif - 1)).toFixed(2); + st.innerHTML = siz1 + unit1 //+ "[" + siz2 + "x" + "]" + ; + //st.innerHTML = siz1 + unit1 + siz2 + "x "; + + // st.innerHTML = (100 * 0.37*2.0 * Math.pow(2, zoomDif - 1)) + 'µm ' + (20 / Math.pow(2, zoomDif-1)).toFixed(1) + "x "; + jQuery('.gmnoprint a').html('Google Terms of Use'); + }); + + + google.maps.event.addListener(map3, 'mousewheel DOMMouseScroll', function (e) { + if (!e) { + e = window.event + } + if (e.preventDefault) { + e.preventDefault() + } + e.returnValue = false; + }); + google.maps.event.addListener(map4, "bounds_changed", function (event) { + // sO.draw(); + // line drawn on screen is 100px == 200micron + var mz = customMapOptions.maxZoom; + var st = document.getElementById('scaleText2'); + var zoomDif = (mz + 1) - map3.getZoom(); + var dif = 20 / zoomDif; // 20x is our max zoom, added factor of 2.0 + var siz1 = (50 * 0.37 * Math.pow(2, zoomDif - 1)); + var unit1 = 'µm '; + if (siz1 > 1000) { + siz1 = (siz1 / 1000.0).toFixed(1); + unit1 = 'mm '; + } + var siz2 = (20 / Math.pow(2, zoomDif - 1)).toFixed(2); + st.innerHTML = siz1 + unit1 //+ "[" + siz2 + "x" + "]" + ; + //st.innerHTML = siz1 + unit1 + siz2 + "x "; + + // st.innerHTML = (100 * 0.37*2.0 * Math.pow(2, zoomDif - 1)) + 'µm ' + (20 / Math.pow(2, zoomDif-1)).toFixed(1) + "x "; + jQuery('.gmnoprint a').html('Google Terms of Use'); + }); + + + google.maps.event.addListener(map4, 'mousewheel DOMMouseScroll', function (e) { + if (!e) { + e = window.event + } + if (e.preventDefault) { + e.preventDefault() + } + e.returnValue = false; + }); +} + +function updateMap2() { + var customMapOptions = { + getTileUrl: tileURL, + isPng: false, + maxZoom: 11, + minZoom: 0, + tileSize: new google.maps.Size(256, 256), + radius: 1738000, + name: "HM", + streetViewControl: false + }; + var customMapType = new google.maps.ImageMapType(customMapOptions); + + initialZoom = 1; + var centreLat = 66.70383915858723; + var centreLon = -48.1640625; + if (typeof SliceNumber !== "undefined" && + typeof locationBySlice[SliceNumber] !== "undefined") { + centreLat = locationBySlice[SliceNumber].centerLat; + centreLon = locationBySlice[SliceNumber].centerLng; + } + + var mycenter = new google.maps.LatLng(centreLat, centreLon); + var myOptions = { + center: mycenter, + zoom: initialZoom, + mapTypeControlOptions: { + mapTypeIds: ['map2'] + }, + mapTypeId: 'map2', + backgroundColor: "#FFFFFF", + draggableCursor: 'crosshair', + zoomControl: true, + scrollwheel: true, + streetViewControl: false, + overviewMapControl: true, + overviewMapControlOptions: { + opened: true, + position: google.maps.ControlPosition.RIGHT_TOP + }, + zoomControlOptions: { + style: google.maps.ZoomControlStyle.DEFAULT + } + }; + map2 = new google.maps.Map(document.getElementById("map2"), myOptions); + map2.mapTypes.set('HM', customMapType); + map2.setMapTypeId('HM'); + //map.enableScrollWheelZoom(); + //map.enableContinuousZoom(); + + google.maps.event.addListener(map2, "bounds_changed", function (event) { + // sO.draw(); + // line drawn on screen is 100px == 200micron + var mz = customMapOptions.maxZoom; + var st = document.getElementById('scaleText2'); + var zoomDif = (mz + 1) - map2.getZoom(); + var dif = 20 / zoomDif; // 20x is our max zoom, added factor of 2.0 + var siz1 = (50 * 0.37 * Math.pow(2, zoomDif - 1)); + var unit1 = 'µm '; + if (siz1 > 1000) { + siz1 = (siz1 / 1000.0).toFixed(1); + unit1 = 'mm '; + } + var siz2 = (20 / Math.pow(2, zoomDif - 1)).toFixed(2); + st.innerHTML = siz1 + unit1 //+ "[" + siz2 + "x" + "]" + ; + //st.innerHTML = siz1 + unit1 + siz2 + "x "; + + // st.innerHTML = (100 * 0.37*2.0 * Math.pow(2, zoomDif - 1)) + 'µm ' + (20 / Math.pow(2, zoomDif-1)).toFixed(1) + "x "; + jQuery('.gmnoprint a').html('Google Terms of Use'); + }); + + + google.maps.event.addListener(map2, 'mousewheel DOMMouseScroll', function (e) { + if (!e) { + e = window.event + } + if (e.preventDefault) { + e.preventDefault() + } + e.returnValue = false; + }); +} + +var current_slice_path = "/hm_web/GoogleBrain/Subjects/HM/Slice_1207/Result"; +if (typeof user_1279 !== "undefined" && user_1279 == "1") { + current_slice_path = "/hm_web/GoogleBrain/Subjects/HM/Slice_1279/Result"; +} + +$(document).ready(function () { + + + $.extend($.fn.disableTextSelect = function () { + return this.each(function () { + if ($.browser.mozilla) {//Firefox + $(this).css('MozUserSelect', 'none'); + } else if ($.browser.msie) {//IE + $(this).bind('selectstart', function () { return false; }); + } else {//Opera, etc. + $(this).mousedown(function () { return false; }); + } + }); + }); + $('.noSelect').disableTextSelect();//No text selection on elements with a class of 'noSelect' + + + // if the user presses the next slide button we should go to the next slide + jQuery('#walkthrough-scroll-next').click(function () { + // what is the currently displayed div? + var thisDiv = 0; + var thisLocation = jQuery('.walkthrough').scrollTop(); + var slides = jQuery('.walkthrough .slide'); + var minDist, location, winnerPos; + var first = true; + var locations = new Array(); + jQuery.each(slides, function (index, value) { + //var pos = jQuery(value).offset().top; + var pos = thisLocation + jQuery(value).position().top; + var pos2 = jQuery(value).position().top; + var pos3 = jQuery('.walkthrough')[0].scrollHeight; + locations[index] = pos; + if (first) { + first = false; + winnerPos = pos; + minDist = Math.abs(pos - thisLocation); + thisDiv = index; + } + if (Math.abs(pos - thisLocation) < minDist) { + minDist = Math.abs(pos - thisLocation); + thisDiv = index; + winnerPos = pos; + } + }); + + // scroll to the next + var nextDiv = thisDiv + 1; + if (nextDiv > locations.length - 1) { + nextDiv = 0; + } + jQuery('.walkthrough').animate({ scrollTop: locations[nextDiv] }, 'slow'); + + }); + jQuery('#walkthrough-scroll-prev').click(function () { + // what is the currently displayed div? + var thisDiv = 0; + var thisLocation = jQuery('.walkthrough').scrollTop(); + var slides = jQuery('.walkthrough .slide'); + var minDist, location, winnerPos; + var first = true; + var locations = new Array(); + jQuery.each(slides, function (index, value) { + //var pos = jQuery(value).offset().top; + var pos = thisLocation + jQuery(value).position().top; + var pos2 = jQuery(value).position().top; + var pos3 = jQuery('.walkthrough')[0].scrollHeight; + locations[index] = pos; + if (first) { + first = false; + winnerPos = pos; + minDist = Math.abs(pos - thisLocation); + thisDiv = index; + } + if (Math.abs(pos - thisLocation) < minDist) { + minDist = Math.abs(pos - thisLocation); + thisDiv = index; + winnerPos = pos; + } + }); + + // scroll to the next + var nextDiv = thisDiv - 1; + if (nextDiv < 0) { + nextDiv = locations.length - 1; + } + jQuery('.walkthrough').animate({ scrollTop: locations[nextDiv] }, 'slow'); + + }); + + //$('#rad-sub-nav li a').last().css('background-image', 'none'); + //$('#sub-nav li a').last().css('background-image', 'none'); + jQuery('#model-interface').buttonset(); + + for (var i = 0; i < jQuery('#model-interface').children().length / 2.0; i++) { // set all buttons to off + jQuery('#model-' + i).attr('checked', false).button("refresh"); + } + AEROTWIST.A3.Sample.finishedLoading = function () { + //jQuery('#model-5').trigger('change'); + }; + + jQuery('#model-interface :checkbox').change(function () { + var buttonChanged = jQuery(this).attr('id'); + // If we pressed something other than "BRAIN SURFACE" make sure that + // "BRAIN SURFACE" is off. + //if (buttonChanged != "model-5") { + // jQuery('#model-5').attr('checked', false); + // jQuery('#model-interface').buttonset('refresh'); + //} + + for (var i = 0; i < AEROTWIST.A3.Sample.geometries.length; i++) { + // jQuery('#model-'+i).attr('checked',false).button("refresh"); + if (jQuery('#model-' + i).attr('checked') + && AEROTWIST.A3.Sample.geometries[i] == null) { + var b = i; // make a local copy, don't reference the parents i + AEROTWIST.A3.Sample.loadThis(b, function () { + AEROTWIST.A3.Sample.geometries[b].visible = jQuery('#model-' + b).attr('checked'); + }); + + } else { + if (AEROTWIST.A3.Sample.geometries[i] !== null) + AEROTWIST.A3.Sample.geometries[i].visible = jQuery('#model-' + i).attr('checked'); + } + } + }); + //jQuery('#model-5').attr('checked',true).button("refresh"); + //jQuery('#model-6').attr('checked',true).button("refresh"); + + jQuery('#open-info-viewer-models').css('top', -jQuery('#model_container').height() + 1); + jQuery('#open-info-viewer-models').css('left', jQuery('#model_container').width() - 18); + jQuery('#open-info-viewer-models').fadeIn('slow'); + + jQuery('#open-info-viewer-blockface').fadeIn('slow'); + + jQuery('#open-info-viewer-walkthrough').css('top', -jQuery('#guided').height() + 194); + jQuery('#open-info-viewer-walkthrough').css('left', jQuery('#guided').width() - 18); + jQuery('#open-info-viewer-walkthrough').fadeIn('slow'); + + jQuery('#open-blockface-overlay').fadeIn('slow'); + + jQuery('#open-add-hotspot-location').live('click', function () { + jQuery('#add-hotspot-location').live('onLoad', function () { + jQuery('#add-hotspot-location').css('width', 300); + jQuery('#add-hotspot-location').css('height', 100); + }); + jQuery('#add-hotspot-location').dialog({ + modal: true, + title: "Add location to the Histological Slide Box", + closeOnClick: false, + effect: 'apple', + buttons: { + "Cancel": function () { + jQuery('#add-hotspot-location').dialog('close'); + return false; + }, + "Submit": function () { + var location = map.getCenter(); + var zoom = map.getZoom(); + var numTiles = 1 << map.getZoom(); + var projection = map.getProjection(); + var worldCoordinate = projection.fromLatLngToPoint(location); + var pixelCoordinate = new google.maps.Point( + worldCoordinate.x * numTiles, + worldCoordinate.y * numTiles); + var tileCoordinate = new google.maps.Point( + Math.floor(pixelCoordinate.x / 256), + Math.floor(pixelCoordinate.y / 256)); + var tile = tileURL(tileCoordinate, zoom); + // submit the data to hotspot + jQuery.ajax({ + type: "POST", + url: '/hm_web/data/hotspots/process.php?callback=success', + data: { + user: user_name == "" ? "anonymous" : user_name, + add: jQuery('#hotspot-location-title').val(), + body: jQuery('#hotspot-location-body').val().replace(/\n/g, '
          '), + location: "[" + SliceNumber + "," + location.lat() + "," + location.lng() + "," + zoom + "," + tile + "]" + }, + dataType: 'jsonp', + context: document.body, + success: function (data) { // this is very strange, we should use success here, but it does not work... + jQuery('#add-hotspot-location').dialog('close'); + + // show a dialog that adding the hotspot was successful + jQuery('#add-hotspot-location-success').dialog({ + modal: true, + title: "Added hotspot location", + closeOnClick: false, + effect: 'apply', + load: true, + buttons: { + "OK": function () { jQuery('#add-hotspot-location-success').dialog('close'); } + } + }); + } + }); + return false; + } + }, + load: true + }); + }); + + jQuery('#open-add-hotspot-notice-location').live('click', function () { + jQuery('#add-hotspot-notice-location').live('onLoad', function () { + jQuery('#add-hotspot-notice-location').css('width', 300); + jQuery('#add-hotspot-notice-location').css('height', 100); + }); + jQuery('#add-hotspot-notice-location').dialog({ + modal: true, + title: "REPORT LOCATION", + closeOnClick: false, + effect: 'apple', + buttons: { + "Cancel": function () { + jQuery('#add-hotspot-notice-location').dialog('close'); + return false; + }, + "Submit": function () { + var location = map.getCenter(); + var zoom = map.getZoom(); + var numTiles = 1 << map.getZoom(); + var projection = map.getProjection(); + var worldCoordinate = projection.fromLatLngToPoint(location); + var pixelCoordinate = new google.maps.Point( + worldCoordinate.x * numTiles, + worldCoordinate.y * numTiles); + var tileCoordinate = new google.maps.Point( + Math.floor(pixelCoordinate.x / 256), + Math.floor(pixelCoordinate.y / 256)); + var tile = tileURL(tileCoordinate, zoom); + // submit the data to hotspot + jQuery.ajax({ + type: "POST", + url: '/hm_web/data/hotspots-notice/process.php?callback=success', + data: { + user: user_name == "" ? "anonymous" : user_name, + add: jQuery('#hotspot-notice-location-title').val(), + body: jQuery('#hotspot-notice-location-body').val().replace(/\n/g, '
          '), + location: "[" + SliceNumber + "," + location.lat() + "," + location.lng() + "," + zoom + "," + tile + "]" + }, + dataType: 'jsonp', + context: document.body, + success: function (data) { // this is very strange, we should use success here, but it does not work... + jQuery('#add-hotspot-notice-location').dialog('close'); + // show a dialog that adding the hotspot was successful + jQuery('#add-hotspot-notice-location-success').dialog({ + modal: true, + title: "Problem reported successfully", + closeOnClick: false, + effect: 'apply', + load: true, + buttons: { + "OK": function () { jQuery('#add-hotspot-notice-location-success').dialog('close'); } + } + }); + } + }); + return false; + } + }, + load: true + }); + }); + + jQuery('#open-comparison-viewer').live('click', function () { + setTimeout("resizeComparisonViewerDiv();", 200); + jQuery('#comparison-viewer').live('onLoad', function () { + updateMap34(); + resizeComparisonViewerDiv(); + }); + jQuery('#comparison-viewer').overlay({ + closeOnClick: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.5 + } + }); + var api = jQuery('#comparison-viewer').data("overlay"); + api.onBeforeLoad = function () { + updateMap34(); + resizeComparisonViewerDiv(); + }; + api.load(); + }); + + jQuery('#open-blockface-overlay').click(function () { + jQuery('#blockface-overlay').overlay({ + closeOnClick: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.5 + } + }); + var api = jQuery('#blockface-overlay').data("overlay"); + api.onBeforeLoad = function () { + resizeBlockfaceDiv(); + }; + api.load(); + }); + + + /* based on the screen size, load minimal tab content to save initial loading time */ + LoadTabContent(2, function () { + jQuery('#open-map-overlay').click(function () { + setTimeout("resizeMapDiv2();", 200); + jQuery('#map-overlay').overlay({ + closeOnClick: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.5 + } + }); + var api = jQuery('#map-overlay').data("overlay"); + jQuery('#map-overlay').on('onLoad', function () { + updateMap2(); + resizeMapDiv2(); + map2.setCenter(map.getCenter()); + map2.setZoom(map.getZoom() + 1); + }); + api.load(); + }); + jQuery('#open-info-viewer-googlebrain').on('click', function () { + jQuery('#info-viewer-googlebrain').overlay({ + oneInstance: false, + closeOnClick: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.5 + } + }); + var api = jQuery('#info-viewer-googlebrain').data("overlay"); + api.onBeforeLoad = function () { + resizeInfoViewerGoogleBrain(); + }; + api.load(); + jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up + }); + + }); + + + jQuery('#open-info-viewer-segmentation').on('click', function () { + jQuery('#info-viewer-segmentation').overlay({ + oneInstance: false, + closeOnClick: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.5 + } + }); + var api = jQuery('#info-viewer-segmentation').data("overlay"); + api.onBeforeLoad = function () { + resizeInfoViewerSegmentation(); + }; + api.load(); + jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up + }); + + jQuery('#open-info-viewer-MRI').on('click', function () { + jQuery('#info-viewer-MRI').overlay({ + oneInstance: false, + closeOnClick: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.5 + } + }); + var api = jQuery('#info-viewer-MRI').data("overlay"); + api.onBeforeLoad = function () { + resizeInfoViewerMRI(); + }; + api.load(); + jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up + }); + + jQuery('#open-info-viewer-models').on('click', function () { + jQuery('#info-viewer-models').overlay({ + closeOnClick: false, + oneInstance: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.9 + } + }); + var api = jQuery('#info-viewer-models').data("overlay"); + api.onBeforeLoad = function () { + resizeInfoViewerModels(); + return false; + }; + api.load(); + jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up + }); + + jQuery('#open-info-viewer-blockface').on('click', function () { + jQuery('#info-viewer-blockface').overlay({ + closeOnClick: false, + oneInstance: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.5 + } + }); + var api = jQuery('#info-viewer-blockface').data("overlay"); + api.onBeforeLoad = function () { + resizeInfoViewerBlockface(); + }; + api.load(); + jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up + }); + + jQuery('#open-info-viewer-walkthrough').on('click', function () { + jQuery('#info-viewer-walkthrough').overlay({ + closeOnClick: false, + oneInstance: false, + effect: 'apple', + mask: { + color: '#fff', + loadSpeed: 200, + opacity: 0.5 + } + }); + var api = jQuery('#info-viewer-walkthrough').data("overlay"); + api.onBeforeLoad = function () { + resizeInfoViewerWalkthrough(); + }; + api.load(); + jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up + }); + + mpr1 = new mpr(0); + mpr1.scoutLineColor = "rgb(127,29,49)"; + mpr1.setVoxelSize([1, 1, 1]); + mpr1.setFlipSliceDirections([false, false, false]); + mpr1.bindWindow("#window0", [0, 1, 0]); + mpr1.bindWindow("#window1", [0, 0, 1]); // make sure that cross-hair is syncronized + mpr1.bindWindow("#window2", [1, 0, 0]); // make sure that cross-hair is syncronized + mpr1.setCacheSize(3); // speed up display by caching 1 image only + mpr1.setPatientInformation("H.M.", "March 2009", "T1", "none"); + mpr1.setDataPath('data/JPEGS/T1'); + mpr1.infoOverlayEnabled = false; + mpr1.setLockPosition(false, false, true); + mpr2 = new mpr(1); + mpr2.scoutLineColor = "rgb(127,29,49)"; + mpr2.setVoxelSize([1, 1, 1]); + mpr2.setFlipSliceDirections([false, false, false]); + //mpr2.bindWindow("#window1", [0,0,1]); + mpr2.setCacheSize(3); // speed up display by caching 1 image only + mpr2.setPatientInformation("H. M.", "2012/02/02", "T1", "none"); + mpr2.setDataPath('data/JPEGS/T1'); + mpr3 = new mpr(2); + mpr3.scoutLineColor = "rgb(127,29,49)"; + mpr3.setVoxelSize([1, 1, 1]); + mpr3.setFlipSliceDirections([false, false, false]); + //mpr3.bindWindow("#window2", [1,0,0]); + mpr3.setCacheSize(3); // speed up display by caching 1 image only + mpr3.setPatientInformation("H. M.", "2012/02/02", "T1", "none"); + mpr3.setDataPath('data/JPEGS/T1'); + + SliceNumber = "1207"; + if (typeof user_1279 !== "undefined" && user_1279 == "1") { + SliceNumber = "1279"; + } + + if (typeof slice != "undefined" && slice != "") { + SliceNumber = slice; + } + if (typeof maplocation != "undefined" && maplocation != "") { + initialMapLocation = maplocation; + } + if (typeof mapZoom != "undefined" && mapZoom != "") { + initialMapZoom = mapZoom; + } + resizeMRIDiv(); + if (typeof maplocation != "undefined" && + typeof mapZoom != "undefined" && + typeof slice != "undefined") + updateMap(SliceNumber, initialMapLocation, initialMapZoom); + else + updateMap(); + resizeMapDiv(); + $('#Blockface').attr("src", "/hm_web/data/Blockface/MEDIUM/2500_FINAL_HM_Blockface_" + SliceNumber + "_MEDIUM.jpg"); + // $('.Blockface').css('background-repeat', "no-repeat"); + $('#section').text('Section ' + SliceNumber); + $("#tabs-1 #id").css("height", $('#tabs-1').width() * .917); + $(".ui-autocomplete-input").val("Search by structure or slice number"); + /* + $('#search').live('click', searchFunc); + $('#search-icon-small').live('click', searchFunc); + $(".jcarousel-next").live('click',searchFunc); + $(".jcarousel-prev").live('click',searchFunc); + $(".ui-menu-item").live('click',searchFunc); + $("#search-box #label").live('keyup',function(){ + if($("#ui-complete-input").val()=='Search by Brain Region') + $("#ui-complete-input").val(''); + searchFunc; + }); + */ + if ($(".six.columns").width() == 268 || $(".six.columns").width() == 300) { + document.getElementById("label").size = 25; + } else if ($(".six.columns").width() == 420) { + document.getElementById("label").size = 42; + } else { + document.getElementById("label").size = 36; + } + + + //This loads the label file. + var lab = new Image(); + lab.onload = function () { + can = document.getElementById('labeldisplay').getContext('2d'); + can.drawImage(lab, 0, 0, 521, 457); + //$('#type').text('ready'); + } + + //This loads the colored labels. + var img = new Image(); + lab.src = "/hm_web/data/Segmented/grayscale_snapshot_" + SliceNumber + ".png"; + //Shows labels on hover over blockface + // $('#blockface_label').mousemove(function(e) { + // bfloc = findPosC(this); + // cX = e.clientX; + // cY = e.pageY; + // bX = bfloc[0]; + // bY = bfloc[1]; + // var mouseX = cX - bfloc[0]; + // var mouseY = cY - bfloc[1]; + // var can = document.getElementById('labeldisplay').getContext('2d'); + // var imageData = can.getImageData(mouseX, mouseY, 1, 1); + // var labelValue = imageData.data; + // $('#type').text(nameForKey(labelValue[0])[0].replace(/_/g,' ')+ ' ' + mouseX + ' ' + mouseY + // ); + // }); + jQuery('.slide').click(function () { + if (typeof user_1279 !== "undefined" && user_1279 == "1") { + alert('This functionality is only available for users with a valid account. Go to thedigitalbrainlibrary.org/hm_web to sign-up.'); + return; // do nothing if not logged in + } + + var slice = jQuery(this).attr('mapLocationSlice'); + var lat = jQuery(this).attr('mapLocationLat'); + var lng = jQuery(this).attr('mapLocationLng'); + var tab = jQuery(this).attr('tab'); + var mprlocation1 = jQuery(this).attr('mprLocation1'); + var mprlocation2 = jQuery(this).attr('mprLocation2'); + var mprlocation3 = jQuery(this).attr('mprLocation3'); + if (typeof slice === "undefined") + return; + var zoom = parseFloat(jQuery(this).attr('mapLocationZoom')); + if (SliceNumber != slice) + update(slice); + map.setCenter(new google.maps.LatLng(lat, lng)); + map.setZoom(zoom); + centerToTab(parseFloat(tab).toFixed(0)); + if (typeof mprlocation1 !== "undefined" && + typeof mprlocation2 !== "undefined" && + typeof mprlocation3 !== "undefined") { + mpr1.setPosition(parseInt(mprlocation1), + parseInt(mprlocation2), + parseInt(mprlocation3)); + } + + //jQuery(this).parent().animate({ scrollTop: jQuery(this).offset().top }, 1000); + }); + if (typeof maplocation != "undefined") // in case we have an initial map location + setTimeout("update( \"" + SliceNumber + "\", [" + initialMapLocation[0] + "," + initialMapLocation[1] + "], " + initialMapZoom + " );", 1000); + else + setTimeout("update( \"" + SliceNumber + "\" );", 1000); // display the first image + +}); + + + + + + +var lastID = 0; +function mycarousel_itemLoadCallback(carousel, state) { + for (var i = carousel.first; i <= carousel.last; i++) { + if (carousel.has(i * 2 - 1)) { + // continue; + } + + if (i > Math.ceil(current_itemList.length / 2)) { + break; + } + + //carousel.add(i, mycarousel_getItemHTML(mycarousel_itemList[i-1])); + carousel.add(i, mycarousel_getItemHTML(i * 2 - 2)) + } + jQuery('.jcarousel-item img').hover(function () { + var sr = jQuery(this).attr('src'); + // extract the number from the string + var idS = sr.split('_'); + var idSS = idS[4]; + var id = parseFloat(idSS).toFixed(0); + if (id != lastID) { + jQuery('#g-left-img').attr('src', './data/KeyFrames/line' + addZeros(id, 4) + '.png').fadeIn('fast'); + lastID = id; + } + // update the mm location in the side view + putSliceNrAsThal(id); + }); +}; + +/** + * Item html creation helper. + */ +function mycarousel_getItemHTML(I) { + //return '' + item.url + ''; + item_0 = current_itemList[I]; + itemHTML = '' + item_0.title + ''; + if (current_itemList.length - 1 > I) { + item_1 = current_itemList[I + 1]; + itemHTML += '' + item_1.title + ''; + } + return itemHTML; +}; + +function addZeros(num, size) { + var s = num + ""; + while (s.length < size) s = "0" + s; + return s; +} + +jQuery(document).ready(function () { + load_carousel(mycarousel_itemList); +}); + +var current_itemList; +var j_scroll; + +function load_carousel(itemList) { + if (itemList.length == 0) { + itemList = mycarousel_itemList; + } + current_itemList = itemList; + j_scroll = Math.floor((parseInt($("#g-right").css("width")) - 28) / 102); + jQuery('#mycarousel').jcarousel({ + size: Math.ceil(itemList.length / 2), + scroll: j_scroll, + vertical: false, + rows: 2, + itemLoadCallback: { onBeforeAnimation: mycarousel_itemLoadCallback }, + }); + if (current_itemList.length != mycarousel_itemList.length) { + $("#mycarousel img").addClass("found"); + } + else { + // jQuery('#mycarousel').jcarousel('scroll',1); + } +}; +function newPopup(url) { + popupWindow = window.open( + url, 'popUpWindow', 'height=740,width=800,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=yes,menubar=no,location=no,directories=no,status=yes') +}; + diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index dbe43287a..b5bafe12f 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -34,6 +34,7 @@ "express-ws": "^2.0.0", "file-loader": "^0.9.0", "file-saver": "^1.3.3", + "google-maps": "^3.2.1", "griddle-react": "^0.6.1", "handlebars": "^4.0.5", "html-webpack-plugin": "^2.28.0", diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index 19dd333a0..7bb5f1ca5 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -149,7 +149,7 @@ module.exports = { loader: "json-loader" }, { - test: /\.(py|png|svg|gif|css|jpg|md|hbs)$/, + test: /\.(py|png|svg|gif|css|jpg|md|hbs|dcm)$/, loader: 'ignore-loader' }, { test: /\.css$/, From ab95037dfbb5f1ce9c221fc5f44557a4b788e3b0 Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 5 Apr 2017 18:04:31 +0100 Subject: [PATCH 050/339] button bar reuses init logic from base class --- src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js b/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js index a22b160aa..bbcb9ddaa 100644 --- a/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js +++ b/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js @@ -46,7 +46,6 @@ define(function (require) { return Widget.View.extend({ variable: null, - options: null, /** * Initialises button bar @@ -55,9 +54,7 @@ define(function (require) { * options - Object with options for the widget */ initialize: function (options) { - this.id = options.id; - this.name = options.name; - this.options = options; + Widget.View.prototype.initialize.call(this, options); this.render(); From b6a289a0637e8d4af73ab4f42c67a3dbff63e5f3 Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 5 Apr 2017 18:56:01 +0100 Subject: [PATCH 051/339] get/set button bar view state --- .../components/widgets/buttonBar/ButtonBar.js | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js b/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js index bbcb9ddaa..faf82aa34 100644 --- a/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js +++ b/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js @@ -46,6 +46,8 @@ define(function (require) { return Widget.View.extend({ variable: null, + barName: "", + barBody: {}, /** * Initialises button bar @@ -163,6 +165,9 @@ define(function (require) { }, renderBar: function (name, barObject) { + this.barName = name; + this.barBody = barObject; + this.setName(name); this.setBody(this.BootstrapMenuMaker.generateToolbar(barObject)); $(function () { @@ -232,6 +237,34 @@ define(function (require) { */ setBody: function (content) { this.innerButtonBarContainer.html(content); + }, + + getView: function(){ + var baseView = Widget.View.prototype.getView.call(this); + + // add options, whatever they are + baseView.options = this.options; + + baseView.componentSpecific = {}; + + // component specific stuff + baseView.componentSpecific.barName = this.barName; + baseView.componentSpecific.barBody = this.barBody; + + return baseView; + }, + + setView: function(view){ + // set base properties + Widget.View.prototype.setView.call(this, view); + + if(view.options != undefined){ + this.setOptions(view.options); + } + + if(view.componentSpecific != undefined){ + this.renderBar(view.componentSpecific.barName, view.componentSpecific.barBody); + } } }); From c5e8555cb3a038a20b09e38ed7df94c9c2468da8 Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 5 Apr 2017 19:02:06 +0100 Subject: [PATCH 052/339] add showTitleBar + transparent background info to generic widget view state --- src/main/webapp/js/components/widgets/Widget.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index 4a1acbef5..a8d9c1ca3 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -38,6 +38,8 @@ define(function (require) { collapsed : false, widgetType: null, stateless: false, + showTitleBar: true, + transparentBackground: false, defaultSize : function(){return {height: 300, width: 350}}, defaultPosition : function(){return {left: "50%", top: "50%"}}, @@ -379,6 +381,8 @@ define(function (require) { * hides / shows the title bar */ showTitleBar: function (show) { + this.showTitleBar = show; + if (show) { this.$el.parent().find(".ui-dialog-titlebar").show(); } else { @@ -491,6 +495,8 @@ define(function (require) { * @param isTransparent */ setTrasparentBackground: function(isTransparent) { + this.transparentBackground = isTransparent; + if(isTransparent){ this.$el.parent().addClass('transparent-back'); this.previousMaxTransparency = true; @@ -674,6 +680,8 @@ define(function (require) { // get default stuff such as id, position and size return { widgetType: this.widgetType, + showTitleBar: this.showTitleBar, + transparentBackground: this.transparentBackground, name: this.name, size: { height: this.size.height, @@ -704,6 +712,14 @@ define(function (require) { if(view.name != undefined){ this.setName(view.name); } + + if(view.showTitleBar != undefined){ + this.showTitleBar(view.showTitleBar); + } + + if(view.transparentBackground != undefined){ + this.setTrasparentBackground(view.transparentBackground); + } } }) }; From 953cf65f5b410abe82dd17e7288c67541bae1d7e Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 5 Apr 2017 19:19:14 +0100 Subject: [PATCH 053/339] trigger auto size if we have no size infoin view state for widget --- src/main/webapp/js/components/widgets/Widget.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index a8d9c1ca3..4229cc5b6 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -701,8 +701,12 @@ define(function (require) { */ setView: function(view){ // set default stuff such as position and size - if(view.size != undefined){ + if(view.size != undefined && view.size.height != 0 && view.size.width != 0){ this.setSize(view.size.height, view.size.width); + } else { + // trigger auto size if we have no size info + this.setAutoWidth(); + this.setAutoHeight(); } if(view.position != undefined){ From 2394ab752c9bad6d8a12b5ef6e9832ec265a34e7 Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 5 Apr 2017 19:37:14 +0100 Subject: [PATCH 054/339] set/get state for function node plotting - work in progress --- src/main/webapp/js/components/widgets/plot/Plot.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 3603461ea..cc18bb169 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -35,6 +35,7 @@ define(function (require) { reIndexUpdate : 0, updateRedraw : 3, isFunctionNode: false, + functionNodeData: null, isXYData: false, xaxisAutoRange : false, yaxisAutoRange : false, @@ -814,9 +815,10 @@ define(function (require) { * @param {Node} functionNode - Function Node to be displayed */ plotFunctionNode: function (functionNode) { - // set flags + // set flags and keep track of state this.isFunctionNode = true; this.isXYData = false; + this.functionNodeData = functionNode; //Check there is metada information to plot if (functionNode.getInitialValues()[0].value.dynamics.functionPlot != null) { @@ -1013,7 +1015,7 @@ define(function (require) { // handle case of function node, data function and x,y data if(this.isFunctionNode){ baseView.dataType = 'function'; - // TODO: store data + baseView.data = this.functionNodeData; } else if (this.isXYData){ baseView.dataType = 'xyData'; // TODO: store data @@ -1038,7 +1040,7 @@ define(function (require) { } if(view.dataType == 'function'){ - // TODO pull function out of data attribute + this.plotFunctionNode(view.data); } else if(view.dataType == 'xyData'){ // TODO pull xy data out of data attribute } else if(view.dataType == 'object'){ From ac718880eb5ed993e860c9a9a4d7d7b1f2933d2e Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 5 Apr 2017 18:01:16 -0700 Subject: [PATCH 055/339] Creates generic Button React component that is now used by download project button. Modifies beginning of loop in java class for download of project --- .../controllers/ConnectionHandler.java | 12 +-- .../ComponentsInitialization.js | 57 ++++++++---- .../webapp/js/components/ComponentFactory.js | 2 +- .../js/components/controls/button/Button.js | 91 +++++++++++++++++++ 4 files changed, 131 insertions(+), 31 deletions(-) create mode 100644 src/main/webapp/js/components/controls/button/Button.js diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index 71159acfe..b78ec6a20 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -1400,13 +1400,8 @@ public void experimentError(String titleMessage, String logMessage, Exception ex public void downloadProject(String requestID, long projectId) { try { - IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); - - // Convert model - - URL url = URLReader.getURL("/projects/C302"); - File file = new File(url.getFile()); + File file = this.geppettoManager.downloadProject(geppettoProject); if(file != null) { @@ -1420,11 +1415,8 @@ public void downloadProject(String requestID, long projectId) { } } - catch (FileNotFoundException e) { - error(e, "Error downloading project"); - } catch (IOException e) { + catch (Exception e) { error(e, "Error downloading project"); } - } } diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index a5b07ab03..9c4d9d24e 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -35,29 +35,46 @@ define(function (require) { //Home button initialization GEPPETTO.ComponentFactory.addComponent('HOME', {}, document.getElementById("HomeButton")); - var toggleClickHandler = function(){ - $('.DownloadProjectButton').uitooltip({content: "The project is getting downloaded..."}); - $(".DownloadProjectButton").mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); + var eventHandler = function(component){ + GEPPETTO.on(GEPPETTO.Events.Project_downloaded, function(){ + component.showToolTip("The project was downloaded!"); + }); + + GEPPETTO.on("geppetto:error", function(){ + component.showToolTip("The project has failed download!"); + component.setState({icon:"fa fa-download"}); + }); + + GEPPETTO.on('spin_download', function() { + component.showToolTip("The project is getting downloaded..."); + component.setState({icon:"fa fa-download fa-spin"}); + }.bind($("#DownloadProjectButton"))); + + GEPPETTO.on('stop_spin_download', function() { + component.setState({icon:"fa fa-download"}); + }.bind($("#DownloadProjectButton"))); + }; + + var clickHandler = function(){ GEPPETTO.Console.executeCommand("Project.download();"); - GEPPETTO.trigger("spin_download"); - }; - + GEPPETTO.trigger("spin_download"); + }; + var configuration = { - id: "DownloadProjectButton", - onClick : toggleClickHandler, - tooltipPosition : { my: "right center", at : "left-10 center"}, - icon : "fa fa-download", - className : "btn DownloadProjectButton pull-right", - disabled : false, - hidden : false - }; - - //Save initialization - GEPPETTO.ComponentFactory.addComponent('BUTTON', {}, document.getElementById("SaveButton")); - - //Download Project Button initialization - GEPPETTO.ComponentFactory.addComponent('DownloadProjectButton', {}, document.getElementById("DownloadProjectButton")); + id: "DownloadProjectButton", + onClick : clickHandler, + eventHandler : eventHandler, + tooltipPosition : { my: "right center", at : "left-25 center"}, + tooltipLabel : "Click to download project!", + icon : "fa fa-download", + className : "btn DownloadProjectButton pull-right", + disabled : false, + hidden : false + }; + //Download Project Button initialization + GEPPETTO.ComponentFactory.addComponent('BUTTON', {configuration: configuration}, document.getElementById("DownloadProjectButton")); + //Simulation controls initialization GEPPETTO.ComponentFactory.addComponent('SIMULATIONCONTROLS', {}, document.getElementById("sim-toolbar")); diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 4007b24cd..1c4fceb5f 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -63,7 +63,7 @@ define(function (require) { 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', 'RAISEDBUTTON': 'controls/RaisedButton', - 'BUTTON':'controls/mixins/Button' + 'BUTTON':'controls/button/Button' } diff --git a/src/main/webapp/js/components/controls/button/Button.js b/src/main/webapp/js/components/controls/button/Button.js new file mode 100644 index 000000000..1685c877e --- /dev/null +++ b/src/main/webapp/js/components/controls/button/Button.js @@ -0,0 +1,91 @@ +/******************************************************************************* + * + * Copyright (c) 2011, 2016 OpenWorm. http://openworm.org + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the MIT License which accompanies this + * distribution, and is available at http://opensource.org/licenses/MIT + * + * Contributors: OpenWorm - http://openworm.org/people.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ******************************************************************************/ + +define(function(require) { + + var React = require('react'); + var GEPPETTO = require('geppetto'); + + $.widget.bridge('uitooltip', $.ui.tooltip); + + var button = React.createClass({ + attachTooltip: function(){ + var self = this; + $("#"+this.props.configuration.id).uitooltip({ + position: this.props.configuration.tooltipPosition, + tooltipClass: "tooltip-persist", + show: { + effect: "slide", + direction: "right", + delay: 200 + }, + hide: { + effect: "slide", + direction: "right", + delay: 200 + }, + content: function () { + return self.state.tooltipLabel; + }, + }); + }, + + showToolTip : function(tooltipLabel){ + // update contents of what's displayed on tooltip + $("#"+this.props.configuration.id).uitooltip({content: tooltipLabel, + position: this.props.configuration.tooltipPosition}); + $("#"+this.props.configuration.id).mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); + }, + + getInitialState: function() { + return { + disableButton : this.props.configuration.disabled, + tooltipLabel : this.props.configuration.tooltipLabel, + icon: this.props.configuration.icon + }; + }, + + componentDidMount: function() { + this.attachTooltip(); + this.props.configuration.eventHandler(this); + }, + + render: function () { + return ( +
          + +
          + ); + } + }); + return button; +}); \ No newline at end of file From dc0dfaabba4225ee69f69eb93d696247bae54dbb Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 5 Apr 2017 21:41:35 -0700 Subject: [PATCH 056/339] changes to how download project works from connection handler --- .../controllers/ConnectionHandler.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index b78ec6a20..e779acc32 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -8,6 +8,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -15,6 +16,7 @@ import java.util.Map; import java.util.Properties; +import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.geppetto.core.beans.PathConfiguration; @@ -1401,19 +1403,19 @@ public void downloadProject(String requestID, long projectId) { try { - File file = this.geppettoManager.downloadProject(geppettoProject); + Path zipPath = this.geppettoManager.downloadProject(geppettoProject); - if(file != null) + if(zipPath != null) { - // Zip folder - Zipper zipper = new Zipper(file.getAbsolutePath()); - Path path = zipper.getZipFromDirectory(file); - // Send zip file to the client - websocketConnection.sendBinaryMessage(requestID, path); + websocketConnection.sendBinaryMessage(requestID, zipPath); websocketConnection.sendMessage(requestID, OutboundMessages.DOWNLOAD_PROJECT,null); - } + //clean temporary directory where files where written + FileUtils.cleanDirectory(zipPath.toFile().getParentFile()); + }else{ + error(null, "Error downloading project"); + } } catch (Exception e) { error(e, "Error downloading project"); From b78f37abc6fa107f487dbec3de6b30e7f6c1356e Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 6 Apr 2017 21:58:42 -0700 Subject: [PATCH 057/339] removes download project button from default --- .../extensions/extensionsConfiguration.json | 4 +- .../ComponentsInitialization.js | 41 +------------------ .../controls/simulation-controls.less | 11 ----- 3 files changed, 4 insertions(+), 52 deletions(-) diff --git a/src/main/webapp/extensions/extensionsConfiguration.json b/src/main/webapp/extensions/extensionsConfiguration.json index 16982d7eb..f4b59e66a 100644 --- a/src/main/webapp/extensions/extensionsConfiguration.json +++ b/src/main/webapp/extensions/extensionsConfiguration.json @@ -1,6 +1,6 @@ { - "geppetto-default/ComponentsInitialization": true, - "geppetto-osb/ComponentsInitialization": false, + "geppetto-default/ComponentsInitialization": false, + "geppetto-osb/ComponentsInitialization": true, "geppetto-vfb/ComponentsInitialization": false, "geppetto-neuron/ComponentsInitialization": false } diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index 9c4d9d24e..ad856e2b2 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -35,46 +35,9 @@ define(function (require) { //Home button initialization GEPPETTO.ComponentFactory.addComponent('HOME', {}, document.getElementById("HomeButton")); - var eventHandler = function(component){ - GEPPETTO.on(GEPPETTO.Events.Project_downloaded, function(){ - component.showToolTip("The project was downloaded!"); - }); + //Save initialization + GEPPETTO.ComponentFactory.addComponent('SAVECONTROL', {}, document.getElementById("SaveButton")); - GEPPETTO.on("geppetto:error", function(){ - component.showToolTip("The project has failed download!"); - component.setState({icon:"fa fa-download"}); - }); - - GEPPETTO.on('spin_download', function() { - component.showToolTip("The project is getting downloaded..."); - component.setState({icon:"fa fa-download fa-spin"}); - }.bind($("#DownloadProjectButton"))); - - GEPPETTO.on('stop_spin_download', function() { - component.setState({icon:"fa fa-download"}); - }.bind($("#DownloadProjectButton"))); - }; - - var clickHandler = function(){ - GEPPETTO.Console.executeCommand("Project.download();"); - GEPPETTO.trigger("spin_download"); - }; - - var configuration = { - id: "DownloadProjectButton", - onClick : clickHandler, - eventHandler : eventHandler, - tooltipPosition : { my: "right center", at : "left-25 center"}, - tooltipLabel : "Click to download project!", - icon : "fa fa-download", - className : "btn DownloadProjectButton pull-right", - disabled : false, - hidden : false - }; - - //Download Project Button initialization - GEPPETTO.ComponentFactory.addComponent('BUTTON', {configuration: configuration}, document.getElementById("DownloadProjectButton")); - //Simulation controls initialization GEPPETTO.ComponentFactory.addComponent('SIMULATIONCONTROLS', {}, document.getElementById("sim-toolbar")); diff --git a/src/main/webapp/style/less/components/controls/simulation-controls.less b/src/main/webapp/style/less/components/controls/simulation-controls.less index 121ab8a8c..b00cbea99 100644 --- a/src/main/webapp/style/less/components/controls/simulation-controls.less +++ b/src/main/webapp/style/less/components/controls/simulation-controls.less @@ -24,17 +24,6 @@ color:@orange; } -.DownloadProjectButton{ - position: fixed; - top: 5px; - right: 569px; - height: 27px; -} - -.DownloadProjectButton:disabled{ - background:0; - color:@orange; -} .tooltip-persist { height: 25px; From f20799113ce16181afb948f1e9c244ec17bc2674 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 10 Apr 2017 12:20:00 +0100 Subject: [PATCH 058/339] function plot + plot options working --- .../components/widgets/buttonBar/ButtonBar.js | 7 ------- .../webapp/js/components/widgets/plot/Plot.js | 21 +++++++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js b/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js index faf82aa34..7f76ef07a 100644 --- a/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js +++ b/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js @@ -242,9 +242,6 @@ define(function (require) { getView: function(){ var baseView = Widget.View.prototype.getView.call(this); - // add options, whatever they are - baseView.options = this.options; - baseView.componentSpecific = {}; // component specific stuff @@ -258,10 +255,6 @@ define(function (require) { // set base properties Widget.View.prototype.setView.call(this, view); - if(view.options != undefined){ - this.setOptions(view.options); - } - if(view.componentSpecific != undefined){ this.renderBar(view.componentSpecific.barName, view.componentSpecific.barBody); } diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index cc18bb169..d4108186b 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -818,7 +818,7 @@ define(function (require) { // set flags and keep track of state this.isFunctionNode = true; this.isXYData = false; - this.functionNodeData = functionNode; + this.functionNodeData = functionNode.getPath(); //Check there is metada information to plot if (functionNode.getInitialValues()[0].value.dynamics.functionPlot != null) { @@ -1009,9 +1009,6 @@ define(function (require) { getView: function(){ var baseView = Widget.View.prototype.getView.call(this); - // add options, whatever they are - baseView.options = this.options; - // handle case of function node, data function and x,y data if(this.isFunctionNode){ baseView.dataType = 'function'; @@ -1028,6 +1025,11 @@ define(function (require) { }); } + baseView.componentSpecific = {}; + + // add component specific options + baseView.componentSpecific.plotOptions = this.plotOptions; + return baseView; }, @@ -1035,12 +1037,9 @@ define(function (require) { // set base properties Widget.View.prototype.setView.call(this, view); - if(view.options != undefined){ - this.setOptions(view.options); - } - if(view.dataType == 'function'){ - this.plotFunctionNode(view.data); + var functionNode = eval(view.data); + this.plotFunctionNode(functionNode); } else if(view.dataType == 'xyData'){ // TODO pull xy data out of data attribute } else if(view.dataType == 'object'){ @@ -1055,6 +1054,10 @@ define(function (require) { ) } } + + if(view.componentSpecific != undefined && view.componentSpecific.plotOptions != undefined){ + this.setOptions(view.plotOptions); + } } }); From a905ca4447ad532b40e8fd7c2678bac0058bfa30 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 10 Apr 2017 13:45:18 +0100 Subject: [PATCH 059/339] serialize / deserialize custom handlers in popup for view state + saving xy plotting in view state --- src/main/webapp/js/components/widgets/plot/Plot.js | 8 ++++++-- src/main/webapp/js/components/widgets/popup/Popup.js | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index d4108186b..7d6ac0102 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -37,6 +37,7 @@ define(function (require) { isFunctionNode: false, functionNodeData: null, isXYData: false, + xyData: null, xaxisAutoRange : false, yaxisAutoRange : false, imageTypes : [], @@ -960,6 +961,7 @@ define(function (require) { // set flags this.isXYData = true; this.isFunctionNode = false; + this.xyData = { dataY : dataY.getPath(), dataX: dataX.getPath() }; this.controller.addToHistory("Plot "+dataY.getInstancePath()+"/"+dataX.getInstancePath(),"plotXYData",[dataY,dataX,options],this.getId()); @@ -1015,7 +1017,7 @@ define(function (require) { baseView.data = this.functionNodeData; } else if (this.isXYData){ baseView.dataType = 'xyData'; - // TODO: store data + baseView.data = this.xyData; } else { // default case, simple plot with variables plots based on instance paths baseView.dataType = 'object'; @@ -1041,7 +1043,9 @@ define(function (require) { var functionNode = eval(view.data); this.plotFunctionNode(functionNode); } else if(view.dataType == 'xyData'){ - // TODO pull xy data out of data attribute + var yData = eval(view.data.dataY); + var xData = eval(view.data.dataX); + this.plotXYData(yData, xData); } else if(view.dataType == 'object'){ for (var index in view.data) { var path = view.data[index]; diff --git a/src/main/webapp/js/components/widgets/popup/Popup.js b/src/main/webapp/js/components/widgets/popup/Popup.js index e612bfdbb..0214aa165 100644 --- a/src/main/webapp/js/components/widgets/popup/Popup.js +++ b/src/main/webapp/js/components/widgets/popup/Popup.js @@ -357,7 +357,13 @@ define(function (require) { baseView.dataType = (typeof this.data == "string") ? "string" : "object"; baseView.data = this.data; baseView.componentSpecific = { - customHandlers: this.customHandlers, + customHandlers: this.customHandlers.map(function(item){ + return { + funct: item.funct.toString(), + event: item.event, + metType: item.metType + } + }), buttonBarControls: this.buttonBarControls, buttonBarConfig: this.buttonBarConfig }; @@ -384,7 +390,7 @@ define(function (require) { if(view.componentSpecific.customHandlers != undefined){ for(var i=0; i Date: Tue, 11 Apr 2017 09:46:24 +0100 Subject: [PATCH 060/339] typo in comment --- src/main/webapp/extensions/extensionsConfiguration.json | 4 ++-- .../widgets/variablevisualiser/VariableVisualiser.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/extensions/extensionsConfiguration.json b/src/main/webapp/extensions/extensionsConfiguration.json index 16982d7eb..f4b59e66a 100644 --- a/src/main/webapp/extensions/extensionsConfiguration.json +++ b/src/main/webapp/extensions/extensionsConfiguration.json @@ -1,6 +1,6 @@ { - "geppetto-default/ComponentsInitialization": true, - "geppetto-osb/ComponentsInitialization": false, + "geppetto-default/ComponentsInitialization": false, + "geppetto-osb/ComponentsInitialization": true, "geppetto-vfb/ComponentsInitialization": false, "geppetto-neuron/ComponentsInitialization": false } diff --git a/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js b/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js index d53e43e42..ca392a820 100644 --- a/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js +++ b/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js @@ -51,7 +51,7 @@ define(function (require) { default_height: 120, /** - * Initialises viriables visualiser with a set of options + * Initialises variables visualiser with a set of options * * @param {Object} options - Object with options for the widget */ From d88db0163a53347b3889d348b884b86351bae416 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 11 Apr 2017 12:15:15 +0100 Subject: [PATCH 061/339] modifying prototype for patient hm --- .../interface/highResViewer/HighResViewer.js | 70 +++++++++++++++---- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js b/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js index 799cd5321..dc0307073 100644 --- a/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js +++ b/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js @@ -25,16 +25,22 @@ define(function (require) { GoogleMapsLoader.load(function(google) { + // var current_slice_path = 'https://s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/HM/Slice_' + addZeros(id, 4) + '/Result'; + var current_slice_path = 'https://s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/HM/Slice_1207/Result'; + var map = new google.maps.Map(document.getElementById('highResViewer'), { center: {lat: 0, lng: 0}, zoom: 1, streetViewControl: false, mapTypeControlOptions: { - mapTypeIds: ['moon'] + mapTypeIds: ['hm'] } }); - var moonMapType = new google.maps.ImageMapType({ + var centreLat = 66.70383915858723; + var centreLon = -48.1640625; + + var hmMapType = new google.maps.ImageMapType({ // Normalizes the coords that tiles repeat across the x axis (horizontally) // like the standard Google map tiles. getNormalizedCoord: function(coord, zoom) { @@ -58,25 +64,59 @@ define(function (require) { return {x: x, y: y}; }, - getTileUrl: function(coord, zoom) { - var normalizedCoord = this.getNormalizedCoord(coord, zoom); - if (!normalizedCoord) { - return null; + // getTileUrl: function(coord, zoom) { + // var normalizedCoord = this.getNormalizedCoord(coord, zoom); + // if (!normalizedCoord) { + // return null; + // } + // var bound = Math.pow(2, zoom); + // return '//mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' + + // '/' + zoom + '/' + normalizedCoord.x + '/' + + // (bound - normalizedCoord.y - 1) + '.jpg'; + // }, + getTileUrl: function(a, b) { + // pervent wrap around + if (a.y < 0 || a.y >= (1 << b)) { + return null; + } + if (a.x < 0 || a.x >= (1 << b)) { + return null; + } + var c = Math.pow(2, b); + var d = a.x; + var e = a.y; + var f = "t"; + for (var g = 0; g < b; g++) { + c = c / 2; + if (e < c) { + if (d < c) { f += "q" } + else { f += "r"; d -= c } + } else { + if (d < c) { f += "t"; e -= c } + else { f += "s"; d -= c; e -= c } + } + } + subdirs = 3; + tmp = ""; + if (f.length >= subdirs) { // subdivide into sub-directories + for (i = 0; i < subdirs; i++) { + tmp += f.charAt(i) + "/"; + } } - var bound = Math.pow(2, zoom); - return '//mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' + - '/' + zoom + '/' + normalizedCoord.x + '/' + - (bound - normalizedCoord.y - 1) + '.jpg'; + tmp += f; + return current_slice_path + "/" + tmp + ".jpg"; }, + center: new google.maps.LatLng(50, 50), tileSize: new google.maps.Size(256, 256), - maxZoom: 9, + isPng: false, + maxZoom: 11, minZoom: 0, - radius: 1738000, - name: 'Moon' + radius: 1738000 + // name: 'HM' }); - map.mapTypes.set('moon', moonMapType); - map.setMapTypeId('moon'); + map.mapTypes.set('hm', hmMapType); + map.setMapTypeId('hm'); }); From 653e6f0689429df6d820f9a5735657ae7b08f760 Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 11 Apr 2017 15:02:03 +0100 Subject: [PATCH 062/339] view state for the variable visulizer widget --- .../variablevisualiser/VariableVisualiser.js | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js b/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js index ca392a820..376dbd114 100644 --- a/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js +++ b/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js @@ -56,9 +56,7 @@ define(function (require) { * @param {Object} options - Object with options for the widget */ initialize: function (options) { - this.id = options.id; - this.name = options.name; - this.options = options; + Widget.View.prototype.initialize.call(this, options); if (!('width' in options)) { options.width = this.default_width; @@ -156,6 +154,26 @@ define(function (require) { */ getSelector: function (name) { return $(this.root.selector + " ." + name); + }, + + getView: function(){ + var baseView = Widget.View.prototype.getView.call(this); + + // add data + baseView.dataType = 'object'; + baseView.data = this.variable.name; + + return baseView; + }, + + setView: function(view){ + // set base properties + Widget.View.prototype.setView.call(this, view); + + if(view.dataType == 'object' && view.data != undefined && view.data != ''){ + var variable = eval(view.data); + this.setVariable(variable); + } } }); }); From f816ed974fdfcab4cbc69e5f5adb5307b927b8dc Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 11 Apr 2017 18:13:59 +0100 Subject: [PATCH 063/339] roll back applying plotting options + work in progress for the connectivity widget state setup --- .../widgets/connectivity/Connectivity.js | 83 +++++++------------ .../webapp/js/components/widgets/plot/Plot.js | 9 -- 2 files changed, 31 insertions(+), 61 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index 5564a09ed..d19dd3413 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -27,7 +27,7 @@ define(function (require) { return Widget.View.extend({ dataset: {}, - + connectivityOptions: {}, defaultConnectivityOptions: { width: 660, height: 500, @@ -103,13 +103,11 @@ define(function (require) { this.createLayout(); } - return this; }, createDataFromConnections: function () { - - //TODO: Hardcoded NeuroML stuff + //TODO: remove Hardcoded NeuroML stuff if(Model.neuroml.connection){ var connectionVariables = Model.neuroml.connection.getVariableReferences(); @@ -269,7 +267,6 @@ define(function (require) { }, - createNode: function (id, type) { if (!(id in this.mapping)) { var nodeItem = { @@ -301,6 +298,9 @@ define(function (require) { * @param {Object} options - options to modify the plot widget */ setOptions: function (options) { + + this.connectivityOptions = options; + function strToFunc(body){ return new Function('x', 'return ' + body + ';'); } @@ -314,55 +314,12 @@ define(function (require) { $.extend(this.options, options); } }, - - /* TODO: Replace spread until jsdocs supports it - getHelp: function(){ - function dedent(callSite, ...args) { - function format(str) { - let size = -1; - return str.replace(/\n(\s+)/g, (m, m1) => { - if (size < 0) - size = m1.replace(/\t/g, " ").length; - return "\n" + m1.slice(Math.min(m1.length, size)); - }); - } - if (typeof callSite === "string") - return format(callSite); - if (typeof callSite === "function") - return (...args) => format(callSite(...args)); - let output = callSite - .slice(0, args.length + 1) - .map((text, i) => (i === 0 ? "" : args[i - 1]) + text) - .join(""); - return format(output); - } - var help = { - 'matrix':dedent(` - ### Adjacency matrix - `), - 'chord': dedent(` - ### Chord Diagram - Hover over populations ("slices") to highlight incoming / outgoing connections. - - Control-hover: outgoing connections - - Shift-hover: incoming connections - `), - 'hive':dedent(` - ### Hive Plot - `), - 'force':dedent(` - ### Force Directed Graph Drawing - `), - } - - return '## Connectivity Widget\n' + help[this.options.layout]; - }, - */ createLayoutSelector: function() { function imgPath(path){ return 'geppetto/js/components/widgets/connectivity/images/' + path; - }; + } var layoutOptions = [ {id: "matrix", label: 'Adjacency matrix', description: @@ -418,7 +375,7 @@ define(function (require) { var modalContent=$('') .append(this.createLayoutSelector()[0].outerHTML).modal(); function handleFirstClick(event) { - //TODO: Hardcoded NeuroML stuff + //TODO: remove Hardcoded NeuroML stuff var netTypes = GEPPETTO.ModelFactory.getAllTypesOfType(GEPPETTO.ModelFactory.geppettoModel.neuroml.network) var netInstances = _.flatten(_.map(netTypes, function(x){return GEPPETTO.ModelFactory.getAllInstancesOf(x)})); function synapseFromConnection(conn) { @@ -430,7 +387,7 @@ define(function (require) { else{ return "Gap junction"; } - }; + } that.setData(netInstances[0], {layout: event.currentTarget.id, linkType: synapseFromConnection}); //TODO: add option to select what to plot if #netInstance>1? firstClick=true; } @@ -446,8 +403,30 @@ define(function (require) { } } - modalContent.find('.card').on('click', clickHandler); + }, + + getView: function(){ + var baseView = Widget.View.prototype.getView.call(this); + + // add connectivity specific options + baseView.options = this.connectivityOptions; + + // add data + baseView.dataType = 'object'; + baseView.data = this.dataset["root"]; + + return baseView; + }, + + setView: function(view){ + // set base properties + Widget.View.prototype.setView.call(this, view); + + if(view.dataType == 'object' && view.data != undefined && view.data != ''){ + var obj = eval(view.data); + this.setData(obj, view.options); + } } }); }); diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 7d6ac0102..fe71b4ed0 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -1027,11 +1027,6 @@ define(function (require) { }); } - baseView.componentSpecific = {}; - - // add component specific options - baseView.componentSpecific.plotOptions = this.plotOptions; - return baseView; }, @@ -1058,10 +1053,6 @@ define(function (require) { ) } } - - if(view.componentSpecific != undefined && view.componentSpecific.plotOptions != undefined){ - this.setOptions(view.plotOptions); - } } }); From d2a03c06dc694cb261f39c704bac9d27cc362acb Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 11 Apr 2017 19:14:20 +0100 Subject: [PATCH 064/339] connectivity widget view state logic keeping into account that some properties can be injected logic --- .../widgets/connectivity/Connectivity.js | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index d19dd3413..1d290e67b 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -409,12 +409,24 @@ define(function (require) { getView: function(){ var baseView = Widget.View.prototype.getView.call(this); - // add connectivity specific options - baseView.options = this.connectivityOptions; + // add connectivity specific options - contains logic, iterate and serialize + var serializedOptions = {}; + for(var item in this.connectivityOptions){ + var serializedItem = {}; + if (typeof this.connectivityOptions[item] === "function") { + serializedItem.value = this.connectivityOptions[item].toString(); + serializedItem.type = 'function'; + } else { + serializedItem.value = this.connectivityOptions[item]; + serializedItem.type = 'primitive'; + } + serializedOptions[item] = serializedItem; + } + baseView.options = serializedOptions; // add data baseView.dataType = 'object'; - baseView.data = this.dataset["root"]; + baseView.data = this.dataset["root"].getPath(); return baseView; }, @@ -425,7 +437,16 @@ define(function (require) { if(view.dataType == 'object' && view.data != undefined && view.data != ''){ var obj = eval(view.data); - this.setData(obj, view.options); + var deserializedOptions = {}; + for(var item in view.options){ + if(view.options[item].type == "function"){ + deserializedOptions[item] = eval('(' + view.options[item].value + ')'); + } else { + deserializedOptions[item] = view.options[item].value; + } + } + + this.setData(obj, deserializedOptions); } } }); From 735f3cf46bd3225115ec97e033ccc3ea6dc9cb1a Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 12 Apr 2017 12:39:15 +0100 Subject: [PATCH 065/339] tree visualizer defaults to stateless --- .../controllers/TreeVisualiserControllerDAT.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js b/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js index 6de888141..7c5d35f3e 100644 --- a/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js +++ b/src/main/webapp/js/components/widgets/treevisualiser/treevisualiserdat/controllers/TreeVisualiserControllerDAT.js @@ -25,7 +25,8 @@ define(function (require) { */ addTreeVisualiserDATWidget: function (isStateless) { if(isStateless == undefined){ - isStateless = false; + // stateless by default + isStateless = true; } //look for a name and id for the new widget From e68f10468cb5b0b3c2319464fd166b0555452abd Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 12 Apr 2017 09:16:57 -0700 Subject: [PATCH 066/339] updates legends of active experiment to show project and experiment name, when another variable from an external experimen/project is added --- .../extensions/extensionsConfiguration.json | 4 +- .../3dCanvas/GEPPETTO.SceneController.js | 20 ++-- .../webapp/js/components/widgets/plot/Plot.js | 105 +++++++++++------- .../plot/controllers/PlotsController.js | 3 + 4 files changed, 78 insertions(+), 54 deletions(-) diff --git a/src/main/webapp/extensions/extensionsConfiguration.json b/src/main/webapp/extensions/extensionsConfiguration.json index 16982d7eb..f4b59e66a 100644 --- a/src/main/webapp/extensions/extensionsConfiguration.json +++ b/src/main/webapp/extensions/extensionsConfiguration.json @@ -1,6 +1,6 @@ { - "geppetto-default/ComponentsInitialization": true, - "geppetto-osb/ComponentsInitialization": false, + "geppetto-default/ComponentsInitialization": false, + "geppetto-osb/ComponentsInitialization": true, "geppetto-vfb/ComponentsInitialization": false, "geppetto-neuron/ComponentsInitialization": false } diff --git a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js index dab9f44d0..d6ab4fb83 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js @@ -99,16 +99,16 @@ define(function (require) { * intensity - the lighting intensity from 0 (no illumination) to 1 (full illumination) */ lightUpEntity: function (instance, colorfn, intensity) { - var threeObject; - if (instance in GEPPETTO.getVARS().meshes) { - threeObject = GEPPETTO.getVARS().meshes[instance]; - } - else { - threeObject = GEPPETTO.getVARS().splitMeshes[instance]; - } - - var [r,g,b] = colorfn(intensity); - threeObject.material.color.setRGB(r,g,b); +// var threeObject; +// if (instance in GEPPETTO.getVARS().meshes) { +// threeObject = GEPPETTO.getVARS().meshes[instance]; +// } +// else { +// threeObject = GEPPETTO.getVARS().splitMeshes[instance]; +// } +// +// var [r,g,b] = colorfn(intensity); +// threeObject.material.color.setRGB(r,g,b); }, /** diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 04041ad49..766951ba3 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -41,6 +41,7 @@ define(function (require) { plotElement : null, xVariable : null, firstStep : 0, + updateLegendsState : false, /** * Default options for plotly widget, used if none specified when plot @@ -209,48 +210,68 @@ define(function (require) { * @param {Object} instance - variable to change display label in legends * @param {String} legend - new legend name */ - setLegend: function (instance, legend) { - //Check if it is a string or a geppetto object - var instancePath = instance; - if ((typeof instance) != "string") { - instancePath = instance.getInstancePath(); - } + setLegend: function (instance, legend) { + //Check if it is a string or a geppetto object + var instancePath = instance; + if ((typeof instance) != "string") { + instancePath = instance.getInstancePath(); + } + + for(var i =0; i< this.datasets.length; i++){ + if(this.datasets[i].name == instancePath){ + this.datasets[i].name = legend; + } + } + + Plotly.relayout(this.plotDiv, this.plotOptions); + this.labelsMap[instancePath] = legend; + + return this; + }, + + updateLegends : function(legendExtension){ + if(!this.updateLegendsState){ + for(var i =0; i< this.datasets.length; i++){ + var oldLegendName = this.datasets[i].name; + var variable = this.variables[this.getLegendInstancePath(oldLegendName)]; + var newLegend = oldLegendName+ legendExtension; + this.variables[newLegend] = variable; + this.datasets[i].name = newLegend; + this.labelsMap[variable.getInstancePath()] = oldLegendName; + } + + Plotly.relayout(this.plotDiv, this.plotOptions); + this.updateLegendsState = true; + } + }, + + getVariables : function(){ + return this.variables; + }, + + plotGeneric: function(dataset) { + if (dataset != undefined) + this.datasets.push(dataset); + + if(this.plotly==null){ + this.plotOptions.xaxis.autorange = true; + this.xaxisAutoRange = true; + //Creates new plot using datasets and default options + this.plotly = Plotly.newPlot(this.plotDiv, this.datasets, this.plotOptions,{displayModeBar: false, doubleClick : false}); + var that = this; + this.plotDiv.on('plotly_doubleclick', function() { + that.resize(); + }); + this.plotDiv.on('plotly_click', function() { + that.resize(); + }); + }else{ + Plotly.newPlot(this.plotDiv, this.datasets, this.plotOptions,{doubleClick : false}); + } + this.resize(false); + }, - for(var i =0; i< this.datasets.length; i++){ - if(this.datasets[i].name == instancePath){ - this.datasets[i].name = legend; - } - } - - Plotly.relayout(this.plotDiv, this.plotOptions); - this.labelsMap[instancePath] = legend; - - return this; - }, - - plotGeneric: function(dataset) { - if (dataset != undefined) - this.datasets.push(dataset); - - if(this.plotly==null){ - this.plotOptions.xaxis.autorange = true; - this.xaxisAutoRange = true; - //Creates new plot using datasets and default options - this.plotly = Plotly.newPlot(this.plotDiv, this.datasets, this.plotOptions,{displayModeBar: false, doubleClick : false}); - var that = this; - this.plotDiv.on('plotly_doubleclick', function() { - that.resize(); - }); - this.plotDiv.on('plotly_click', function() { - that.resize(); - }); - }else{ - Plotly.newPlot(this.plotDiv, this.datasets, this.plotOptions,{doubleClick : false}); - } - this.resize(false); - }, - /** * Takes data series and plots them. To plot array(s) , use it as * plotData([[1,2],[2,3]]) To plot a geppetto simulation variable , use it as @@ -1016,8 +1037,8 @@ define(function (require) { } } - this.plotOptions.xaxis.autorange = true; - this.xaxisAutoRange = true; +// this.plotOptions.xaxis.autorange = true; +// this.xaxisAutoRange = true; this.plotly = Plotly.newPlot(this.plotDiv, this.datasets, this.plotOptions,{displayModeBar: false,doubleClick : false}); this.updateAxis(dataY.getInstancePath()); this.resize(); diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index 061a720ad..bd8c1c618 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -175,6 +175,7 @@ define(function(require) { if (inst != undefined && inst.getTimeSeries() != undefined) { // plot, we have data if (plotWidget != undefined) { + plotWidget.updateLegends(" [" + window.Project.getActiveExperiment().name + "]"); plotWidget.plotData(inst); this.updateLegend(plotWidget, inst, projectId, experimentId); } else { @@ -186,6 +187,7 @@ define(function(require) { var cb = function() { var i = window.Instances.getInstance(path); if (plotWidget != undefined) { + plotWidget.updateLegends(" [" + window.Project.getActiveExperiment().name + "]"); plotWidget.plotData(i); this.updateLegend(plotWidget, i, projectId, experimentId); } else { @@ -203,6 +205,7 @@ define(function(require) { var t = GEPPETTO.ExperimentsController.getExternalInstance(projectId, experimentId, 'time(StateVariable)'); if (plotWidget != undefined) { + plotWidget.updateLegends(" [" + window.Project.getActiveExperiment().name + "]"); plotWidget.plotXYData(i, t); self.updateLegend(plotWidget, i, projectId, experimentId); } else { From 10530a0f6fd99ca722b9b30e25520c07ff37337d Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 12 Apr 2017 10:06:23 -0700 Subject: [PATCH 067/339] bug fixing --- .../js/components/widgets/plot/controllers/PlotsController.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index bd8c1c618..f80f2e993 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -175,7 +175,7 @@ define(function(require) { if (inst != undefined && inst.getTimeSeries() != undefined) { // plot, we have data if (plotWidget != undefined) { - plotWidget.updateLegends(" [" + window.Project.getActiveExperiment().name + "]"); + //plotWidget.updateLegends(" [" + window.Project.getActiveExperiment().name + "]"); plotWidget.plotData(inst); this.updateLegend(plotWidget, inst, projectId, experimentId); } else { @@ -187,7 +187,7 @@ define(function(require) { var cb = function() { var i = window.Instances.getInstance(path); if (plotWidget != undefined) { - plotWidget.updateLegends(" [" + window.Project.getActiveExperiment().name + "]"); + //plotWidget.updateLegends(" [" + window.Project.getActiveExperiment().name + "]"); plotWidget.plotData(i); this.updateLegend(plotWidget, i, projectId, experimentId); } else { From c7a9c8e2c567aa26b66f12c8bc3254ad3f1c2ccb Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 12 Apr 2017 10:22:26 -0700 Subject: [PATCH 068/339] checks that unitSymbol is not null when retrieving unit label before doing a regex expression on it. A null unitSymbol is given to the plot when an external variable is plotted since AStateVariable has no variable associated with it --- src/main/webapp/js/components/widgets/plot/Plot.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 766951ba3..e84095940 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -758,8 +758,11 @@ define(function (require) { * @param unitSymbol - string representing unit symbol */ getUnitLabel: function (unitSymbol) { - - unitSymbol = unitSymbol.replace(/_per_/gi, " / "); + if(unitSymbol!= null || unitSymbol != undefined){ + unitSymbol = unitSymbol.replace(/_per_/gi, " / "); + }else{ + unitSymbol = ""; + } var unitLabel = unitSymbol; @@ -1037,8 +1040,8 @@ define(function (require) { } } -// this.plotOptions.xaxis.autorange = true; -// this.xaxisAutoRange = true; + this.plotOptions.xaxis.autorange = true; + this.xaxisAutoRange = true; this.plotly = Plotly.newPlot(this.plotDiv, this.datasets, this.plotOptions,{displayModeBar: false,doubleClick : false}); this.updateAxis(dataY.getInstancePath()); this.resize(); From de68fa0b741e544b0fb751a54096fe497768566f Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 12 Apr 2017 14:45:54 -0700 Subject: [PATCH 069/339] changes bacground of plot widgets to white for 100ms to make downloaded images have a white background --- .../webapp/js/components/widgets/plot/Plot.js | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index e84095940..cf0c72750 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -430,12 +430,36 @@ define(function (require) { * Downloads a screenshot of the graphing plots */ downloadImage: function (imageType) { + var self = this; + + //play around with settings to make background temporarily white + //and marging wider + this.plotOptions.paper_bgcolor = "rgb(255,255,255)"; + this.plotOptions.xaxis.linecolor = "rgb(0,0,0)"; + this.plotOptions.yaxis.linecolor = "rgb(0,0,0)"; + this.plotOptions.xaxis.tickfont.color = "rgb(0,0,0)"; + this.plotOptions.yaxis.tickfont.color = "rgb(0,0,0)"; + this.plotOptions.margin.r= 40; + Plotly.relayout(this.plotDiv,this.plotOptions); + Plotly.downloadImage( - this.plotDiv, { - format: imageType, - height: window.screen.availHeight, - width: window.screen.availWidth, - }); + this.plotDiv, { + format: imageType, + height: window.screen.availHeight, + width: window.screen.availWidth + }); + + var reset = function(){ + //reset background and margin + self.plotOptions.paper_bgcolor = "rgb(69,59,59)"; + self.plotOptions.xaxis.linecolor = "rgb(255,255,255)"; + self.plotOptions.yaxis.linecolor = "rgb(255,255,255)"; + self.plotOptions.xaxis.tickfont.color = "rgb(255,255,255)"; + self.plotOptions.yaxis.tickfont.color = "rgb(255,255,255)"; + self.plotOptions.margin.r= 0; + Plotly.relayout(self.plotDiv,self.plotOptions); + }; + setTimeout(reset, 100); }, /** From ff62dba91bb99fb16e71acc5bef233f89a8ecb54 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 12 Apr 2017 23:29:20 +0100 Subject: [PATCH 070/339] ignore gz files --- src/main/webapp/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index 3afe0113b..4ce850d08 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -155,7 +155,7 @@ module.exports = { loader: "json-loader" }, { - test: /\.(py|png|svg|gif|css|jpg|md|hbs|dcm)$/, + test: /\.(py|png|svg|gif|css|jpg|md|hbs|dcm|gz)$/, loader: 'ignore-loader' }, { test: /\.css$/, From 4f9cd339f2b22c62331c3862e9d1c7a68c64e7f8 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 12 Apr 2017 23:29:56 +0100 Subject: [PATCH 071/339] remove id from state in high level component otherwise real props.id gets overwritten --- src/main/webapp/js/components/widgets/NewWidget.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/NewWidget.js b/src/main/webapp/js/components/widgets/NewWidget.js index a81e229b7..bd0319927 100644 --- a/src/main/webapp/js/components/widgets/NewWidget.js +++ b/src/main/webapp/js/components/widgets/NewWidget.js @@ -22,7 +22,6 @@ define(function (require) { getInitialState: function () { return { - id: null, dialog: null, visible: true, destroyed: false, From 3822b04fee7368a1bc4af4962b29d7fc4c4b55a4 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 12 Apr 2017 23:31:00 +0100 Subject: [PATCH 072/339] add dat library, controls panels, extract path to file to a props, remove hardcoded id and change by classes, clean up --- .../interface/dicomViewer/DicomViewer.js | 126 +++++++++++------- src/main/webapp/package.json | 1 + 2 files changed, 81 insertions(+), 46 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index b7fe45481..f5adfcb1b 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -9,6 +9,7 @@ define(function (require) { var React = require('react'); window.THREE = require('three'); var AMI = require('ami.js'); + var dat = require('dat-gui'); var dicomViewerComponent = React.createClass({ @@ -25,7 +26,7 @@ define(function (require) { var HelpersStack = AMI.default.Helpers.Stack; // Setup renderer - var container = document.getElementById('dicomViewer'); + var container = document.getElementById(this.props.id).getElementsByClassName('dicomViewer')[0]; container.style.height = "300px"; container.style.width = "350px"; var renderer = new THREE.WebGLRenderer({ @@ -59,21 +60,21 @@ define(function (require) { width: container.offsetWidth, height: container.offsetHeight, }; - camera.fitBox(2,2); + camera.fitBox(2, 2); renderer.setSize(container.offsetWidth, container.offsetHeight); } window.addEventListener('resize', onWindowResize, false); - $("#" + this.props.containerid ).on("dialogresizestop", function (event, ui) { + $("#" + this.props.containerid).on("dialogresizestop", function (event, ui) { camera.canvas = { - width: ui.size.width-30, - height: ui.size.height-30, + width: ui.size.width - 260 - 30, + height: ui.size.height - 30, }; - camera.fitBox(2,2); + camera.fitBox(2, 2); + + renderer.setSize(ui.size.width - 260 - 30, ui.size.height - 30); - renderer.setSize( ui.size.width-30, ui.size.height-30); - }); /** @@ -90,41 +91,74 @@ define(function (require) { } animate(); - // Setup loader - // var loader = new LoadersVolume(container); - // var file = - // 'https://cdn.rawgit.com/FNNDSC/data/master/nifti/adi_brain/adi_brain.nii.gz'; + function gui(stackHelper, containerId) { + var gui = new dat.GUI({ + autoPlace: false, + }); - // instantiate the loader - // it loads and parses the dicom image - var loader = new LoadersVolume(container); + var customContainer = document.getElementById(containerId).getElementsByClassName('controlsContainer')[0]; + customContainer.appendChild(gui.domElement); + // only reason to use this object is to satusfy data.GUI + var camUtils = { + invertRows: false, + invertColumns: false, + rotate45: false, + rotate: 0, + orientation: 'default', + convention: 'radio', + }; - var t2 = [ - '00010001.dcm', '00010019.dcm', '00010037.dcm', '00010055.dcm', '00010073.dcm', '00010091.dcm', '00010109.dcm', '00010127.dcm', - '00010002.dcm', '00010020.dcm', '00010038.dcm', '00010056.dcm', '00010074.dcm', '00010092.dcm', '00010110.dcm', '00010128.dcm', - '00010003.dcm', '00010021.dcm', '00010039.dcm', '00010057.dcm', '00010075.dcm', '00010093.dcm', '00010111.dcm', '00010129.dcm', - '00010004.dcm', '00010022.dcm', '00010040.dcm', '00010058.dcm', '00010076.dcm', '00010094.dcm', '00010112.dcm', '00010130.dcm', - '00010005.dcm', '00010023.dcm', '00010041.dcm', '00010059.dcm', '00010077.dcm', '00010095.dcm', '00010113.dcm', '00010131.dcm', - '00010006.dcm', '00010024.dcm', '00010042.dcm', '00010060.dcm', '00010078.dcm', '00010096.dcm', '00010114.dcm', '00010132.dcm', - '00010007.dcm', '00010025.dcm', '00010043.dcm', '00010061.dcm', '00010079.dcm', '00010097.dcm', '00010115.dcm', '00010133.dcm', - '00010008.dcm', '00010026.dcm', '00010044.dcm', '00010062.dcm', '00010080.dcm', '00010098.dcm', '00010116.dcm', '00010134.dcm', - '00010009.dcm', '00010027.dcm', '00010045.dcm', '00010063.dcm', '00010081.dcm', '00010099.dcm', '00010117.dcm', '00010135.dcm', - '00010010.dcm', '00010028.dcm', '00010046.dcm', '00010064.dcm', '00010082.dcm', '00010100.dcm', '00010118.dcm', '00010136.dcm', - '00010011.dcm', '00010029.dcm', '00010047.dcm', '00010065.dcm', '00010083.dcm', '00010101.dcm', '00010119.dcm', '00010137.dcm', - '00010012.dcm', '00010030.dcm', '00010048.dcm', '00010066.dcm', '00010084.dcm', '00010102.dcm', '00010120.dcm', '00010138.dcm', - '00010013.dcm', '00010031.dcm', '00010049.dcm', '00010067.dcm', '00010085.dcm', '00010103.dcm', '00010121.dcm', - '00010014.dcm', '00010032.dcm', '00010050.dcm', '00010068.dcm', '00010086.dcm', '00010104.dcm', '00010122.dcm', - '00010015.dcm', '00010033.dcm', '00010051.dcm', '00010069.dcm', '00010087.dcm', '00010105.dcm', '00010123.dcm', - '00010016.dcm', '00010034.dcm', '00010052.dcm', '00010070.dcm', '00010088.dcm', '00010106.dcm', '00010124.dcm', - '00010017.dcm', '00010035.dcm', '00010053.dcm', '00010071.dcm', '00010089.dcm', '00010107.dcm', '00010125.dcm', - '00010018.dcm', '00010036.dcm', '00010054.dcm', '00010072.dcm', '00010090.dcm', '00010108.dcm', '00010126.dcm', - ]; - - var files = t2.map(function(v) { - return 'geppetto/extensions/geppetto-hm/samples/lowResPACStypical/' + v; - }); + // camera + var cameraFolder = gui.addFolder('Camera'); + var invertRows = cameraFolder.add(camUtils, 'invertRows'); + invertRows.onChange(function () { + camera.invertRows(); + }); + + var invertColumns = cameraFolder.add(camUtils, 'invertColumns'); + invertColumns.onChange(function () { + camera.invertColumns(); + }); + + var rotate45 = cameraFolder.add(camUtils, 'rotate45'); + rotate45.onChange(function () { + camera.rotate(); + }); + + cameraFolder.add(camera, 'angle', 0, 360).step(1).listen(); + + let orientationUpdate = cameraFolder.add( + camUtils, 'orientation', ['default', 'axial', 'coronal', 'sagittal']); + orientationUpdate.onChange(function (value) { + camera.orientation = value; + camera.update(); + camera.fitBox(2); + stackHelper.orientation = camera.stackOrientation; + }); + + let conventionUpdate = cameraFolder.add( + camUtils, 'convention', ['radio', 'neuro']); + conventionUpdate.onChange(function (value) { + camera.convention = value; + camera.update(); + camera.fitBox(2); + }); + + cameraFolder.open(); + + // of course we can do everything from lesson 01! + var stackFolder = gui.addFolder('Stack'); + stackFolder.add( + stackHelper, 'index', 0, stackHelper.stack.dimensionsIJK.z - 1) + .step(1).listen(); + stackFolder.add(stackHelper.slice, 'interpolation', 0, 1).step(1).listen(); + stackFolder.open(); + } - loader.load(files) + // Setup loader + var _this = this; + var loader = new LoadersVolume(container); + loader.load(this.props.files) .then(function () { // merge files into clean series/stack/frame structure var series = loader.data[0].mergeSeries(loader.data); @@ -145,6 +179,8 @@ define(function (require) { scene.add(stackHelper); + // build the gui + gui(stackHelper, _this.props.id); // hook up callbacks controls.addEventListener('OnScroll', function (e) { @@ -192,22 +228,20 @@ define(function (require) { camera.update(); // Not working properly. See issue: https://github.com/FNNDSC/ami/issues/120 - camera.fitBox(2,2); + camera.fitBox(2, 2); }) .catch(function (error) { window.console.log('oops... something went wrong...'); window.console.log(error); }); - - - - }, render: function () { return (
          -
          +
          +
          +
          ) diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index eaff11eaf..f39d75b2a 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -29,6 +29,7 @@ "css-loader": "^0.26.2", "d3": "^4.2.7", "d3-plugins-dist": "^3.2.0", + "dat-gui": "^0.5.0", "detector-webgl": "^2.0.0", "exports-loader": "^0.6.3", "express": "^4.14.0", From 3c0156fcf04665bf2b4fb3f47251907182a179e7 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 12 Apr 2017 22:56:33 -0700 Subject: [PATCH 073/339] work for reading relative paths when loading geppetto model --- .../frontend/controllers/ConnectionHandler.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index e779acc32..7ce67fb2e 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -113,7 +113,7 @@ public void loadProjectFromId(String requestID, long projectId, long experimentI } else { - loadGeppettoProject(requestID, geppettoProject, experimentId); + loadGeppettoProject(requestID, geppettoProject, experimentId,""); } } catch(NumberFormatException e) @@ -129,7 +129,7 @@ public void loadProjectFromId(String requestID, long projectId, long experimentI public void loadProjectFromContent(String requestID, String projectContent) { IGeppettoProject geppettoProject = DataManagerHelper.getDataManager().getProjectFromJson(getGson(), projectContent); - loadGeppettoProject(requestID, geppettoProject, -1l); + loadGeppettoProject(requestID, geppettoProject, -1l,""); } /** @@ -142,9 +142,11 @@ public void loadProjectFromURL(String requestID, String urlString) try { url = URLReader.getURL(urlString); + int index = url.toString().lastIndexOf('/'); + String urlBase = url.toString().substring(0, index + 1); BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); IGeppettoProject geppettoProject = DataManagerHelper.getDataManager().getProjectFromJson(getGson(), reader); - loadGeppettoProject(requestID, geppettoProject, -1l); + loadGeppettoProject(requestID, geppettoProject, -1l, urlBase); } catch(IOException e) { @@ -156,11 +158,11 @@ public void loadProjectFromURL(String requestID, String urlString) * @param requestID * @param geppettoProject */ - public void loadGeppettoProject(String requestID, IGeppettoProject geppettoProject, long experimentId) + public void loadGeppettoProject(String requestID, IGeppettoProject geppettoProject, long experimentId, String urlBase) { try { - geppettoManager.loadProject(requestID, geppettoProject); + geppettoManager.loadProject(requestID, geppettoProject,urlBase); boolean readOnly = true; if(geppettoProject.isVolatile()){ readOnly = false; From ebcec7bd199d5249b94f7f3dd474b1139f40a738 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 13 Apr 2017 08:42:55 -0700 Subject: [PATCH 074/339] work for relative paths --- .../controllers/ConnectionHandler.java | 19 ++++++++++++------- .../controllers/WebsocketConnection.java | 14 +------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index 7ce67fb2e..dc7fd3c74 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -84,6 +84,7 @@ public class ConnectionHandler implements IGeppettoManagerCallbackListener // the geppetto project active for this connection private IGeppettoProject geppettoProject; + private String urlBase = ""; /** * @param websocketConnection * @param geppettoManager @@ -113,7 +114,7 @@ public void loadProjectFromId(String requestID, long projectId, long experimentI } else { - loadGeppettoProject(requestID, geppettoProject, experimentId,""); + loadGeppettoProject(requestID, geppettoProject, experimentId,this.urlBase); } } catch(NumberFormatException e) @@ -129,7 +130,7 @@ public void loadProjectFromId(String requestID, long projectId, long experimentI public void loadProjectFromContent(String requestID, String projectContent) { IGeppettoProject geppettoProject = DataManagerHelper.getDataManager().getProjectFromJson(getGson(), projectContent); - loadGeppettoProject(requestID, geppettoProject, -1l,""); + loadGeppettoProject(requestID, geppettoProject, -1l,this.urlBase); } /** @@ -143,10 +144,11 @@ public void loadProjectFromURL(String requestID, String urlString) { url = URLReader.getURL(urlString); int index = url.toString().lastIndexOf('/'); - String urlBase = url.toString().substring(0, index + 1); + String urlBase = url.toString().substring(0, index); BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); IGeppettoProject geppettoProject = DataManagerHelper.getDataManager().getProjectFromJson(getGson(), reader); - loadGeppettoProject(requestID, geppettoProject, -1l, urlBase); + this.urlBase = urlBase; + loadGeppettoProject(requestID, geppettoProject, -1l, this.urlBase); } catch(IOException e) { @@ -635,16 +637,19 @@ public void getSupportedOuputs(String requestID, String aspectInstancePath, long /** * @param requestID - * @param url + * @param urlString * @param visitor */ - public void sendScriptData(String requestID, URL url, WebsocketConnection visitor) + public void sendScriptData(String requestID, String urlString, WebsocketConnection visitor) { try { String line = null; StringBuilder sb = new StringBuilder(); + String urlPath = urlBase + urlString; + URL url = URLReader.getURL(urlPath); + BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); while((line = br.readLine()) != null) @@ -657,7 +662,7 @@ public void sendScriptData(String requestID, URL url, WebsocketConnection visito } catch(IOException e) { - error(e, "Error while reading the script at " + url); + error(e, "Error while reading the script at " + urlString); } } diff --git a/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java b/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java index 731871765..4912b1cbe 100644 --- a/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java +++ b/src/main/java/org/geppetto/frontend/controllers/WebsocketConnection.java @@ -263,19 +263,7 @@ protected void onTextMessage(CharBuffer message) throws IOException case GET_SCRIPT: { String urlString = gmsg.data; - URL url = null; - try - { - - url = URLReader.getURL(urlString); - - connectionHandler.sendScriptData(requestID, url, this); - - } - catch(MalformedURLException e) - { - sendMessage(requestID, OutboundMessages.ERROR_READING_SCRIPT, ""); - } + connectionHandler.sendScriptData(requestID, urlString, this); break; } case GET_DATA_SOURCE_RESULTS: From 43d4f4a27ecb6a747bb2e6b84687b3dedaaa26a6 Mon Sep 17 00:00:00 2001 From: johnidol Date: Thu, 13 Apr 2017 19:05:12 +0100 Subject: [PATCH 075/339] save view state of plot xy data + plot from other experiments/projects - work in progress --- .../webapp/js/components/widgets/plot/Plot.js | 24 +++++++++++++++---- .../plot/controllers/PlotsController.js | 11 +++++---- .../webapp/js/geppettoModel/ModelFactory.js | 6 +++-- .../geppettoModel/model/ExternalInstance.js | 17 ++++--------- .../webapp/js/geppettoModel/model/Instance.js | 1 - .../geppettoProject/ExperimentsController.js | 4 ++-- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index fe71b4ed0..5b4c715c3 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -19,6 +19,8 @@ define(function (require) { var widgetUtility = require("../WidgetUtility"); widgetUtility.loadCss("geppetto/js/components/widgets/plot/Plot.css"); + var ExternalInstance = require('../../../geppettoModel/model/ExternalInstance'); + return Widget.View.extend({ plotly: null, plotDiv : null, @@ -962,6 +964,10 @@ define(function (require) { this.isXYData = true; this.isFunctionNode = false; this.xyData = { dataY : dataY.getPath(), dataX: dataX.getPath() }; + if(dataY instanceof ExternalInstance){ + this.xyData.projectId = dataY.projectId; + this.xyData.experimentId = dataY.experimentId; + } this.controller.addToHistory("Plot "+dataY.getInstancePath()+"/"+dataX.getInstancePath(),"plotXYData",[dataY,dataX,options],this.getId()); @@ -1038,19 +1044,27 @@ define(function (require) { var functionNode = eval(view.data); this.plotFunctionNode(functionNode); } else if(view.dataType == 'xyData'){ - var yData = eval(view.data.dataY); - var xData = eval(view.data.dataX); - this.plotXYData(yData, xData); + var yPath = view.data.dataY; + var xPath = view.data.dataX; + // project and experiment id could be any project and any experiment + var projectId = view.data.projectId != undefined ? view.data.projectId : Project.getId(); + var experimentId = view.data.projectId != undefined ? view.data.experimentId : Project.getActiveExperiment().getId(); + this.controller.plotStateVariable( + projectId, + experimentId, + yPath, + this, + xPath + ); } else if(view.dataType == 'object'){ for (var index in view.data) { var path = view.data[index]; - // TODO: project and experiment id could be any project and any experiment this.controller.plotStateVariable( Project.getId(), Project.getActiveExperiment().getId(), path, this - ) + ); } } } diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index 8149885e7..08df22b47 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -154,11 +154,12 @@ define(function (require) { * @param projectId * @param experimentId * @param path - * @param plotWidget - options, if not provided a new widget will be created + * @param plotWidget - optional, if not provided a new widget will be created + * @param xPath - optional, if plotting xy data a path for the x axis */ - plotStateVariable: function(projectId, experimentId, path, plotWidget){ + plotStateVariable: function(projectId, experimentId, path, plotWidget, xPath){ + var self = this; if(window.Project.getId() == projectId && window.Project.getActiveExperiment().getId() == experimentId){ - var self = this; // try to resolve path var inst = undefined; try { @@ -193,7 +194,9 @@ define(function (require) { // we are dealing with external instances, define re-usable callback for plotting external instances var plotExternalCallback = function(){ var i = GEPPETTO.ExperimentsController.getExternalInstance(projectId, experimentId, path); - var t = GEPPETTO.ExperimentsController.getExternalInstance(projectId, experimentId, 'time(StateVariable)'); + // if xPath is not specified, assume time + if(xPath == undefined){ xPath = 'time(StateVariable)'; } + var t = GEPPETTO.ExperimentsController.getExternalInstance(projectId, experimentId, xPath); if(plotWidget != undefined){ plotWidget.plotXYData(i,t); diff --git a/src/main/webapp/js/geppettoModel/ModelFactory.js b/src/main/webapp/js/geppettoModel/ModelFactory.js index 7873fae97..a57d86f0f 100644 --- a/src/main/webapp/js/geppettoModel/ModelFactory.js +++ b/src/main/webapp/js/geppettoModel/ModelFactory.js @@ -2133,10 +2133,12 @@ define(function (require) { }, /** Creates an instance */ - createExternalInstance: function (path) { + createExternalInstance: function (path, projectId, experimentId) { var options = { _metaType: GEPPETTO.Resources.INSTANCE_NODE, - path:path + path: path, + projectId: projectId, + experimentId: experimentId }; return new ExternalInstance(options); diff --git a/src/main/webapp/js/geppettoModel/model/ExternalInstance.js b/src/main/webapp/js/geppettoModel/model/ExternalInstance.js index 26368846c..d9590584a 100644 --- a/src/main/webapp/js/geppettoModel/model/ExternalInstance.js +++ b/src/main/webapp/js/geppettoModel/model/ExternalInstance.js @@ -1,8 +1,7 @@ /** - * Client class use to represent an instance object (instantiation of a - * variable). + * Client class use to represent an instance object (instantiation of a variable) * * @module model/Instance * @author Giovanni Idili @@ -12,16 +11,16 @@ define(function (require) { var Instance = require('./Instance'); - function ExternalInstance(options) { Instance.prototype.constructor.call(this, options); this.path = options.path; - }; + this.projectId = options.projectId; + this.experimentId = options.experimentId; + } ExternalInstance.prototype = Object.create(Instance.prototype); ExternalInstance.prototype.constructor = ExternalInstance; - /** * Get the type for this instance * @@ -84,7 +83,6 @@ define(function (require) { return undefined; }; - /** * Get the variable for this instance * @@ -121,7 +119,6 @@ define(function (require) { return this.path; }; - /** * Get raw instance path (without array shortening) * @@ -134,7 +131,6 @@ define(function (require) { throw "Invalid operation with ExternalInstance"; }; - /** * Get parent * @@ -156,8 +152,6 @@ define(function (require) { throw "Invalid operation with ExternalInstance"; }; - - /** * Return connections, user GEPPETTO.Resources.INPUT / OUTPUT / * INPUT_OUTPUT to filter @@ -171,7 +165,6 @@ define(function (require) { return this.connections; }; - /** * Deletes instance */ @@ -179,7 +172,5 @@ define(function (require) { throw "Invalid operation with ExternalInstance"; }; - - return ExternalInstance; }); diff --git a/src/main/webapp/js/geppettoModel/model/Instance.js b/src/main/webapp/js/geppettoModel/model/Instance.js index 8af3e8530..cc261576f 100644 --- a/src/main/webapp/js/geppettoModel/model/Instance.js +++ b/src/main/webapp/js/geppettoModel/model/Instance.js @@ -11,7 +11,6 @@ define(function (require) { function Instance(options) { - this.id = options.id; this.name = options.name; this._metaType = options._metaType; diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index b2dd4adfb..a6ebea0dc 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -97,7 +97,7 @@ define(function (require) { for (var i = 0; i < experimentState.recordedVariables.length; i++) { var recordedVariable = experimentState.recordedVariables[i]; var instancePath = recordedVariable.pointer.path; - var instance = GEPPETTO.ModelFactory.createExternalInstance(instancePath); + var instance = GEPPETTO.ModelFactory.createExternalInstance(instancePath, experimentState.projectId, experimentState.experimentId); instance.extendApi(AStateVariableCapability); instance.setWatched(true, false); if (recordedVariable.hasOwnProperty("value") && recordedVariable.value != undefined) { @@ -109,7 +109,7 @@ define(function (require) { for (var i = 0; i < experimentState.setParameters.length; i++) { var setParameter = experimentState.setParameters[i]; var instancePath = setParameter.pointer.path; - var instance = GEPPETTO.ModelFactory.createExternalInstance(instancePath); + var instance = GEPPETTO.ModelFactory.createExternalInstance(instancePath, experimentState.projectId, experimentState.experimentId); instance.extendApi(AParameterCapability); if (setParameter.hasOwnProperty("value") && setParameter.value != undefined) { instance.setValue(setParameter.value.value, false); From 746bc23a0dc615ccb2d746b2e4726fc5b338561f Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 13 Apr 2017 11:25:51 -0700 Subject: [PATCH 076/339] changes for download image styling --- .../3dCanvas/GEPPETTO.SceneController.js | 20 +++++------ .../webapp/js/components/widgets/plot/Plot.js | 36 +++++++++++++++++-- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js index d6ab4fb83..dab9f44d0 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js @@ -99,16 +99,16 @@ define(function (require) { * intensity - the lighting intensity from 0 (no illumination) to 1 (full illumination) */ lightUpEntity: function (instance, colorfn, intensity) { -// var threeObject; -// if (instance in GEPPETTO.getVARS().meshes) { -// threeObject = GEPPETTO.getVARS().meshes[instance]; -// } -// else { -// threeObject = GEPPETTO.getVARS().splitMeshes[instance]; -// } -// -// var [r,g,b] = colorfn(intensity); -// threeObject.material.color.setRGB(r,g,b); + var threeObject; + if (instance in GEPPETTO.getVARS().meshes) { + threeObject = GEPPETTO.getVARS().meshes[instance]; + } + else { + threeObject = GEPPETTO.getVARS().splitMeshes[instance]; + } + + var [r,g,b] = colorfn(intensity); + threeObject.material.color.setRGB(r,g,b); }, /** diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index cf0c72750..e811d961b 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -439,14 +439,36 @@ define(function (require) { this.plotOptions.yaxis.linecolor = "rgb(0,0,0)"; this.plotOptions.xaxis.tickfont.color = "rgb(0,0,0)"; this.plotOptions.yaxis.tickfont.color = "rgb(0,0,0)"; + this.plotOptions.yaxis.titlefont.color = "rgb(0,0,0)"; + this.plotOptions.xaxis.titlefont.color = "rgb(0,0,0)"; + this.plotOptions.xaxis.tickfont.size = 18; + this.plotOptions.yaxis.tickfont.size = 18; + this.plotOptions.xaxis.titlefont.size = 18; + this.plotOptions.yaxis.titlefont.size = 18; + this.plotOptions.legend.font.size = 18; + this.plotOptions.legend.font.family = 'Helvetica Neue'; + this.plotOptions.legend.font.color = "rgb(0,0,0)"; + this.plotOptions.legend.bgcolor = "rgb(255,255,255)"; this.plotOptions.margin.r= 40; + + var oldMarginLeft = this.plotOptions.margin.l; + this.plotOptions.margin.l= 70; Plotly.relayout(this.plotDiv,this.plotOptions); + var height = this.getSize().height; + var width = this.getSize().width; + + //double the size if the width and height is very small + if(height <500 || width <500){ + height = height*2; + width = width *2; + } + Plotly.downloadImage( this.plotDiv, { format: imageType, - height: window.screen.availHeight, - width: window.screen.availWidth + height: height, + width: width }); var reset = function(){ @@ -456,7 +478,17 @@ define(function (require) { self.plotOptions.yaxis.linecolor = "rgb(255,255,255)"; self.plotOptions.xaxis.tickfont.color = "rgb(255,255,255)"; self.plotOptions.yaxis.tickfont.color = "rgb(255,255,255)"; + self.plotOptions.yaxis.titlefont.color = "rgb(255,255,255)"; + self.plotOptions.xaxis.titlefont.color = "rgb(255,255,255)"; + self.plotOptions.xaxis.tickfont.size = 11; + self.plotOptions.yaxis.tickfont.size = 11; + self.plotOptions.xaxis.titlefont.size = 12; + self.plotOptions.yaxis.titlefont.size = 12; + self.plotOptions.legend.font.size = 12; + self.plotOptions.legend.font.color = "rgb(255,255,255)"; + self.plotOptions.legend.bgcolor = "rgba(66, 59, 59)"; self.plotOptions.margin.r= 0; + self.plotOptions.margin.l= oldMarginLeft; Plotly.relayout(self.plotDiv,self.plotOptions); }; setTimeout(reset, 100); From 571b0a7a92b6f413ccaae590d09110dfbe29339f Mon Sep 17 00:00:00 2001 From: johnidol Date: Thu, 13 Apr 2017 21:46:08 +0100 Subject: [PATCH 077/339] progress saving plot widget view state with data coming from different projects/experiments --- .../webapp/js/components/widgets/plot/Plot.js | 95 +++++++++++-------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 5b4c715c3..20b43a11e 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -38,8 +38,9 @@ define(function (require) { updateRedraw : 3, isFunctionNode: false, functionNodeData: null, - isXYData: false, - xyData: null, + hasXYData: false, + xyData: [], + hasStandardPlotData: false, xaxisAutoRange : false, yaxisAutoRange : false, imageTypes : [], @@ -148,6 +149,7 @@ define(function (require) { this.visible = options.visible; this.datasets = []; this.variables = []; + this.xyData = []; this.plotOptions = this.defaultOptions(); //Merge passed options into existing defaultOptions object $.extend( this.plotOptions, options); @@ -246,7 +248,7 @@ define(function (require) { plotData: function (data, options) { // set flags for function node and xy both to false this.isFunctionNode = false; - this.isXYData = false; + this.hasStandardPlotData = true; var validVariable = eval(data); if(validVariable == null || undefined){ @@ -820,7 +822,8 @@ define(function (require) { plotFunctionNode: function (functionNode) { // set flags and keep track of state this.isFunctionNode = true; - this.isXYData = false; + this.hasStandardPlotData = false; + this.hasXYData = false; this.functionNodeData = functionNode.getPath(); //Check there is metada information to plot @@ -961,13 +964,14 @@ define(function (require) { } // set flags - this.isXYData = true; + this.hasXYData = true; this.isFunctionNode = false; - this.xyData = { dataY : dataY.getPath(), dataX: dataX.getPath() }; + var xyDataEntry = { dataY : dataY.getPath(), dataX: dataX.getPath() }; if(dataY instanceof ExternalInstance){ - this.xyData.projectId = dataY.projectId; - this.xyData.experimentId = dataY.experimentId; + xyDataEntry.projectId = dataY.projectId; + xyDataEntry.experimentId = dataY.experimentId; } + this.xyData.push(xyDataEntry); this.controller.addToHistory("Plot "+dataY.getInstancePath()+"/"+dataX.getInstancePath(),"plotXYData",[dataY,dataX,options],this.getId()); @@ -1021,16 +1025,21 @@ define(function (require) { if(this.isFunctionNode){ baseView.dataType = 'function'; baseView.data = this.functionNodeData; - } else if (this.isXYData){ - baseView.dataType = 'xyData'; - baseView.data = this.xyData; } else { - // default case, simple plot with variables plots based on instance paths - baseView.dataType = 'object'; - baseView.data = this.datasets.map(function(item){ - // build array of paths - return item.name; - }); + + if (this.hasXYData){ + baseView.dataType = 'object'; + baseView.xyData = this.xyData.slice(0); + } + + if (this.hasStandardPlotData) { + // default case, simple plot with variables plots based on instance paths + baseView.dataType = 'object'; + baseView.data = this.datasets.map(function(item){ + // build array of paths + return item.name; + }); + } } return baseView; @@ -1043,28 +1052,36 @@ define(function (require) { if(view.dataType == 'function'){ var functionNode = eval(view.data); this.plotFunctionNode(functionNode); - } else if(view.dataType == 'xyData'){ - var yPath = view.data.dataY; - var xPath = view.data.dataX; - // project and experiment id could be any project and any experiment - var projectId = view.data.projectId != undefined ? view.data.projectId : Project.getId(); - var experimentId = view.data.projectId != undefined ? view.data.experimentId : Project.getActiveExperiment().getId(); - this.controller.plotStateVariable( - projectId, - experimentId, - yPath, - this, - xPath - ); - } else if(view.dataType == 'object'){ - for (var index in view.data) { - var path = view.data[index]; - this.controller.plotStateVariable( - Project.getId(), - Project.getActiveExperiment().getId(), - path, - this - ); + } else if (view.dataType == 'object') { + // if any xy data loop through it + if(view.xyData != undefined){ + for(var i=0; i Date: Thu, 13 Apr 2017 22:02:59 +0100 Subject: [PATCH 078/339] handle case of mixed datasets --- src/main/webapp/js/components/widgets/plot/Plot.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 20b43a11e..9331146c0 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -1033,12 +1033,15 @@ define(function (require) { } if (this.hasStandardPlotData) { - // default case, simple plot with variables plots based on instance paths + // simple plot with non external instances baseView.dataType = 'object'; - baseView.data = this.datasets.map(function(item){ - // build array of paths - return item.name; - }); + baseView.data = []; + for(var item in this.variables){ + // only add non external instances + if(!(this.variables[item] instanceof ExternalInstance)){ + baseView.data.push(item) + } + } } } From 489eff7dfb98079c6df1c43566b5a54092deb3bc Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Fri, 14 Apr 2017 22:31:40 +0100 Subject: [PATCH 079/339] remove commented line --- src/main/webapp/js/pages/geppetto/main.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/webapp/js/pages/geppetto/main.js b/src/main/webapp/js/pages/geppetto/main.js index 11430821b..ae5632784 100644 --- a/src/main/webapp/js/pages/geppetto/main.js +++ b/src/main/webapp/js/pages/geppetto/main.js @@ -46,8 +46,6 @@ var GEPPETTO = require('geppetto'); require('../../components/ComponentFactory')(GEPPETTO); -//GEPPETTO.ComponentFactory.addComponent('WIDGETCONTAINER',{}, document.getElementById('widgetContainer'), function(){GEPPETTO.widgetContainer=this;}) - GEPPETTO.ComponentFactory.loadSpinner(); jQuery(function () { From 0dc9d15d661e1b29b494a124ef6c22bf7703bed7 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Fri, 14 Apr 2017 22:34:30 +0100 Subject: [PATCH 080/339] cleaning up --- src/main/webapp/js/components/ComponentFactory.js | 3 --- src/main/webapp/js/components/widgets/NewWidget.js | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 59d2b0f75..3d0f16401 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -65,7 +65,6 @@ define(function (require) { 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', 'RAISEDBUTTON': 'controls/RaisedButton' - //'WIDGETCONTAINER': 'widgets/WidgetContainer' } @@ -91,8 +90,6 @@ define(function (require) { require(["./" + components[componentID]], function(loadedModule){ var component = React.createFactory(addWidget(loadedModule))(properties); var renderedComponent = that.renderComponent(component, document.getElementById('widgetContainer'), callback); - - //GEPPETTO.widgetContainer.addChildren(component) return renderedComponent; }); }, diff --git a/src/main/webapp/js/components/widgets/NewWidget.js b/src/main/webapp/js/components/widgets/NewWidget.js index a81e229b7..c960a0db0 100644 --- a/src/main/webapp/js/components/widgets/NewWidget.js +++ b/src/main/webapp/js/components/widgets/NewWidget.js @@ -1,9 +1,8 @@ - /** * - * Base Widget Class, all widgets extend this class. + * High Level widget component * @module Widgets/Widget - * @author Jesus R. Martinez (jesus@metacell.us) + * @author Adrian Quintana (adrian@metacell.us) */ define(function (require) { From d986ec87ed58793620acb08e10320b40305683e0 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Fri, 14 Apr 2017 22:45:37 +0100 Subject: [PATCH 081/339] remove unused css --- .../interface/dicomViewer/DicomViewer.css | 60 ------------------- 1 file changed, 60 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.css b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.css index 43fea684e..e69de29bb 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.css +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.css @@ -1,60 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2011, 2013 OpenWorm. - * http://openworm.org - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the MIT License - * which accompanies this distribution, and is available at - * http://opensource.org/licenses/MIT - * - * Contributors: - * OpenWorm - http://openworm.org/people.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - *******************************************************************************/ -.geppettoForm label { - width: 50%; - color: white; -} - -.geppettoForm input, -.geppettoForm select { - background: rgba(33, 29, 29, 0.4); - color: white; - width: 50%; - display: inline-block; - padding: 4px; - font-size: 13px; - line-height: 18px; - border: 1px solid #ccc; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.geppettoForm button{ - background-color: #fc6320 !important; - color: white !important; - float: right; - margin-top: -10px; - margin-bottom: 20px; - border: 0px; -} \ No newline at end of file From b694f4a32fb9384a9736753e3111dd84db7023d1 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Fri, 14 Apr 2017 22:46:07 +0100 Subject: [PATCH 082/339] a more elegant way of including ami.js from within node_modules --- src/main/webapp/webpack.config.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index 4ce850d08..b8ea835fc 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -139,13 +139,7 @@ module.exports = { module: { loaders: [ { - test: /\.(js)$/, exclude: [/node_modules/, /build/, /\.bundle/, ], loader: ['babel-loader'], - query: { - presets: ['react', 'es2015'] - } - }, - { - test: /\.(js)$/, include: [/node_modules\/ami.js/], loader: ['babel-loader'], + test: /\.(js)$/, exclude: [/node_modules\/(?!(ami.js)\/).*/, /build/, /\.bundle/, ], loader: ['babel-loader'], query: { presets: ['react', 'es2015'] } From 1ff03eb23cf8e2ba0f4b65d681e6baf253e99c58 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Fri, 14 Apr 2017 22:54:36 +0100 Subject: [PATCH 083/339] remove atlas file --- .../interface/highResViewer/atlas.js | 2052 ----------------- 1 file changed, 2052 deletions(-) delete mode 100644 src/main/webapp/js/components/interface/highResViewer/atlas.js diff --git a/src/main/webapp/js/components/interface/highResViewer/atlas.js b/src/main/webapp/js/components/interface/highResViewer/atlas.js deleted file mode 100644 index 3a0655b4a..000000000 --- a/src/main/webapp/js/components/interface/highResViewer/atlas.js +++ /dev/null @@ -1,2052 +0,0 @@ - -function LoadTabContent(ind, callback) { - if (ind == 2) { - $("#tabs-1").load('./lib/tab_1_content.html', callback); - } - if (ind > 0) { - for (i = 3; i <= ind; i++) { - if ($("#tabs-" + i).html() == '') { - $("#tabs-" + i).load('./lib/tab_' + i + '_content.html', callback); - } - } - } else { - for (i = 3; i <= 4; i++) { - if ($(window).height() + $(this).scrollTop() > $("#tabs-" + i).offset().top && $("#tabs-" + i).html() == '') { - $("#tabs-" + i).load('./lib/tab_' + i + '_content.html', callback); - } - } - } - if (ind == 2) { // after moving to the correct location update the map - resizeMapDiv(); - updateMap(); - } -} - -$(function () { - /*$("#tabs").tabs();*/ - $("#tabs-3").tabs(); - $("#tabs-3").tabs("select", "3"); - $("#tabs-3").tabs("select", "3"); -}); - - -$(function () { - var sub_nav = $('#sub-nav'); - pos = sub_nav.offset(); - - //main_nav_height = parseFloat($("#selector").css("margin-top")); - sloc = findPosC(document.getElementById('left-container')); - //contloc = findPosC(document.getElementById('selector')); - //selector_height = /*parseFloat($("#selector").css("height"));*/ sloc[1] - contloc [1] - - topNav_height = (parseFloat($("top-banner").css("height")) + parseFloat($("#primary").css("height"))); - - $(window).scroll(function () { - /* dynamic load tab content to save initial loading time */ - LoadTabContent(0); - - var cont = $('#cont').offset().left; - var left = $('#left-box').css({ 'left': (cont - $(window).scrollLeft() + 20) + 'px' }); - $('#banner').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); - $('#primary').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); - $('#left-container').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); - $('#left-box').css({ 'left': (cont - $(window).scrollLeft() + 20) + 'px' }); - // $('.sub-nav-fixed').css({'left': (cont - $(window).scrollLeft() + 384) + 'px' }); - // $('.sub-nav-default').css({'left': 0 + 'px' }); - - /* leave the sub-nav as fixed on the top for Design for a width of 768px */ - if (parseInt($(".container").css("width")) > 420) { - if ($(this).scrollTop() + topNav_height > sloc[1] && sub_nav.hasClass('sub-nav-default')) { - sub_nav.fadeOut('fast', function () { - $(this).removeClass('sub-nav-default').addClass('sub-nav-fixed').fadeIn('fast'); - }); - $("#left-box").fadeOut('fast', function () { - $(this).removeClass('left-box-default').addClass('left-box-fixed').fadeIn('fast'); - }); - $("#selector").fadeOut('fast', function () { - $(this).addClass('selector-scroll').fadeIn('fast'); - }); - } else if ($(this).scrollTop() + topNav_height <= sloc[1] && sub_nav.hasClass('sub-nav-fixed')) { - sub_nav.fadeOut('fast', function () { - $(this).removeClass('sub-nav-fixed').addClass('sub-nav-default').fadeIn('fast'); - $("#sub-nav").css("left:0px !important"); - }); - $("#left-box").fadeOut('fast', function () { - $(this).removeClass('left-box-fixed').addClass('left-box-default').fadeIn('fast'); - }); - $("#selector").fadeOut('fast', function () { - $(this).removeClass('selector-scroll').fadeIn('fast'); - }); - } - } - - for (i = 4; i > 0; i--) { - if ($(this).scrollTop() >= $("#tabs-" + i).offset().top - 130) { - $('#sub-nav .ui-state-active').removeClass('ui-state-active'); - $('#sub-nav .ui-tabs-selected').removeClass('ui-tabs-selected'); - $("#tabs-" + i + "-link").addClass('ui-state-active ui-tabs-selected'); - break; - } - } - - }); -}); - -$(window).resize(function () { - var cont = $('#cont').offset().left; - var left = $('#left-box').css({ 'left': (cont - $(window).scrollLeft() + 20) + 'px' }); - $('#banner').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); - $('#primary').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); - $('#left-container').css({ 'left': (cont - $(window).scrollLeft()) + 'px' }); - $('#left-box').css({ 'left': (cont - $(window).scrollLeft() + 20) + 'px' }); - // $('.sub-nav-fixed').css({'left': (cont - $(window).scrollLeft() + 384) + 'px' }); - // $('.sub-nav-default').css({'left': 0 + 'px' }); - - resizeMRIDiv(); - resizeMapDiv(); - resizeSegDiv(); - resizeMapDiv2(); - resizeBlockfaceDiv(); - resizeComparisonViewerDiv(); - resizeInfoViewerGoogleBrain(); - resizeInfoViewerSegmentation(); - resizeInfoViewerMRI(); - resizeInfoViewerModels(); - resizeInfoViewerBlockface(); - resizeInfoViewerWalkthrough(); - if (parseInt($(".container").css("width")) < 768) { - if ($("#sub-nav").hasClass('sub-nav-fixed')) { - $("#sub-nav").removeClass('sub-nav-fixed').addClass('sub-nav-default'); - } - if ($("#left-box").hasClass('left-box-fixed')) { - $("#left-box").removeClass('left-box-fixed').addClass('left-box-default'); - } - } - - if ($(".six.columns").width() == 268 || $(".six.columns").width() == 300) { - document.getElementById("label").size = 25; - } else if ($(".six.columns").width() == 420) { - document.getElementById("label").size = 42; - } else { - document.getElementById("label").size = 36; - } - $(".ui-autocomplete").css("left", ($("html").width() - $("#cont").width()) / 2); - $(".ui-autocomplete").css("width", parseInt($("#left-container").css("width")) + (parseInt($("#left-container").css("margin-left")) - 5) * 2); - -}); - -$('.sub-nav-tab').live('click', function (event) { - - /*...prevent the default behaviour...*/ - event.preventDefault(); - - var tabInd = $(this).parent().attr('id').match(/[0-9]/); - // (Hauke) uncommented the loading as it did not load the google map portion correctly - // This change destroys the load by usage feature! - //LoadTabContent(tabInd); - - // var divId = $(this).parent().attr("id").replace("-link",''); - var divId = "tabs-" + tabInd; - var offsetTop = $("#" + divId).offset().top - 104; - window.scroll(0, offsetTop); - - /* ...remove the tab_link_selected class from current active link... */ - $('#sub-nav .ui-state-active').removeClass('ui-state-active'); - $('#sub-nav .ui-tabs-selected').removeClass('ui-tabs-selected'); - - /* ...and add it to the new active link */ - $(this).parent().addClass('ui-state-active ui-tabs-selected'); -}); - - -function centerToTab(number) { - var divId = "tabs-" + number; - var offsetTop = $("#" + divId).offset().top - 104; - jQuery('html, body').animate({ scrollTop: offsetTop }, 1000); - // window.scroll(0,offsetTop); -} - -$(function () { - $("#label").autocomplete({ - source: availableLabel, - delay: 0, - open: function (event, ui) { - $(this).autocomplete("widget").css({ - "left": ($("html").width() - $("#cont").width()) / 2, - "width": parseInt($("#left-container").css("width")) + (parseInt($("#left-container").css("margin-left")) - 5) * 2 - }); - } - }); -}); - - - -var search_result_mycarousel_itemList = []; - -var searchFunc = function (e) { - if (typeof user_1279 !== "undefined" && user_1279 == "1") { - return; // do nothing if user is not logged in - } - - search_result_mycarousel_itemList = []; - jQuery('#mycarousel').jcarousel('scroll', 0); - $(".brain_jcarousel").html('
            '); - - var search_str = $("#label").val().toLowerCase().trim().replace(/ /g, '_'); - // alert('search now for ' + search_str); - $("#mycarousel img").removeClass("found"); - $(window).scrollTop(0); - $("#left-box").removeClass('left-box-fixed').addClass('left-box-default').fadeIn('fast'); - $("#sub-nav").removeClass('sub-nav-fixed').addClass('sub-nav-default').fadeIn('fast'); - if ($("#label").val() == '') { - //return false; - current_itemList = mycarousel_itemList; - - load_carousel(mycarousel_itemList); - - } else { - slice_label.filter(function (sl) { - if (sl.label.toLowerCase() == search_str) { - //$("img[alt=\"Section "+sl.slice_num+"\"]").addClass('found'); - if (mycarousel_itemList_pic_num.indexOf(parseInt(sl.slice_num)) >= 0) { - var len = sl.slice_num.toString().length; - var img_num = ''; - for (i = 0; i < 4 - len; i++) { - img_num += '0'; - } - img_num += sl.slice_num; - search_result_mycarousel_itemList.push({ url: "./data/Blockface/TINY/2500_FINAL_HM_Blockface_" + img_num + "_MEDIUM_TINY.jpg", title: "Section " + sl.slice_num }); - - } - - } - }); - - load_carousel(search_result_mycarousel_itemList); - - } - if (e.which == 13) { - $(".ui-autocomplete").css("display", "none"); - } -}; -$("#label").live("blur", function () { - var default_value = "Search by structure or slice number"; //$(this).attr("rel"); - if ($(this).val() == "") { - $(this).val(default_value); - } -}).live("focus", function () { - var default_value = "Search by structure or slice number"; //$(this).attr("rel"); - if ($(this).val() == default_value) { - $(this).val(""); - } -}); -var addFoundBorder = function (e) { - if (current_itemList.length != mycarousel_itemList.length) { - $("#mycarousel img").addClass("found"); - } -} - -$('#search').live('click', searchFunc); -$('#search-icon-small').live('click', searchFunc); -//$(".jcarousel-next").live('click',searchFunc); -//$(".jcarousel-prev").live('click',searchFunc); -$(".ui-menu-item").live('click', searchFunc); -$("#search-box #label").live('keyup', function (e) { - if (e.which == 13) { - $(".ui-autocomplete").css("display", "none"); - $("#search-icon-small").trigger('click'); - } - - if ($("#search-box #label").val() == '') { - current_itemList = mycarousel_itemList; - jQuery('#mycarousel').jcarousel('scroll', 0); - $(".brain_jcarousel").html('
              '); - load_carousel(mycarousel_itemList); - } - else if ($(".ui-autocomplete-input").val() == 'Search by structure or slice number') { - $(".ui-autocomplete-input").val(''); - } - else { - // $("#search-icon-small").trigger('click'); - } -}); -$(".jcarousel-next").live('click', addFoundBorder); -$(".jcarousel-prev").live('click', addFoundBorder); - - -$("#reset-buttons").live('click', function () { - // $("#label").val(''); - $(".ui-autocomplete-input").val("Search by structure or slice number"); - $("#mycarousel img").removeClass("found"); - jQuery('#mycarousel').jcarousel('scroll', 0); - $(".brain_jcarousel").html('
                '); - load_carousel(mycarousel_itemList); -}); - -$('#g-right li img').live('click', function (event) { - if (typeof user_1279 !== "undefined" && user_1279 == "1") { - alert('This functionality is only available for users with a valid account. Go to thedigitalbrainlibrary.org/hm_web to sign-up.'); - return; // do nothing - } - - var n = $(this).attr("imageNumber"); - $('#g-right li img').removeClass('active'); - $(this).addClass('active'); - - // get real image number - var sr = jQuery(this).attr('src'); - // extract the number from the string - var idS = sr.split('_'); - var idSS = idS[4]; - var id = parseFloat(idSS).toFixed(0); - - SliceNumber = addZeros(id, 4); - update(id); -}); - -function findPosC(obj) { - var curleft = curtop = 0; - if (obj.offsetParent) { - do { - curleft += obj.offsetLeft; - curtop += obj.offsetTop; - } while (obj = obj.offsetParent); - return [curleft, curtop]; - } -} - -function putSliceNrAsThal(id) { - // update the mm location in the side view - var thal = -(id - 955) * 70 / 1000.0; - var pos = -1.27 * thal - 17; - var talpos = (thal / 0.97).toFixed(2); - jQuery('#slice-location-thal').html('' + thal + ' mm'); - jQuery('#slice-location-thal').css('left', pos + 'px'); - jQuery('.Talar').html('' + thal + ' mm (' + talpos + ' mm)'); -} - -function ACPCpos(id) { - var y = (49 / 2304 * (id - 55) - 316).toFixed(4); - jQuery('#AC-PC').css('top', y + 'px'); -} - -// update everything -function update(id, _maplocation, _mapZoom) { - SliceNumber = addZeros(id, 4); - putSliceNrAsThal(id); - ACPCpos(id); - updateBlockface(); - current_slice_path = 'https://s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/HM/Slice_' + addZeros(id, 4) + '/Result'; - if (typeof _maplocation != "undefined" && typeof _mapZoom != "undefined") - updateMap(SliceNumber, _maplocation, _mapZoom); - else - updateMap(); - mpr1.infoOverlayEnabled = true; - mpr1.setLockPosition(false, false, false); - pos = mpr1.getPosition(); - mrId = parseInt(id / 2400 * 511); - mpr1.setPosition(pos[0], pos[1], mrId); - mpr1.infoOverlayEnabled = false; - mpr1.setLockPosition(false, false, true); - mpr1.scoutLineColor = "rgb(127,29,49)"; - resizeMRIDiv(); - resizeMapDiv(); - resizeSegDiv(); - var medium_src = './data/Blockface/MEDIUM/2500_FINAL_HM_Blockface_' + SliceNumber + '_MEDIUM.jpg'; - medium_src = 'getBlockface.php?h=' + SliceNumber; - //medium_src = medium_src.replace("TINY","MEDIUM"); - $("#left-box img").attr("src", medium_src); - - $("#left-box #img-title").html("SECTION NUMBER " + SliceNumber); - - //This loads the label file. - var lab = new Image(); - lab.onload = function () { - can = document.getElementById('labeldisplay').getContext('2d'); - can.drawImage(lab, 0, 0, 521, 457); - //$('#type').text('ready'); - } - - lab.src = "/hm_web/data/Segmented/" + seglabel; - - //Shows labels on hover over blockface - $('#blockface_label').mousemove(function (e) { - bfloc = findPosC(this); - cX = e.clientX; - cY = e.pageY; - bX = bfloc[0]; - bY = bfloc[1]; - sY = parseFloat(jQuery('#segmented').css('top')); - sX = parseFloat(jQuery('#segmented').css('left')); - var mouseX = -sX + cX - bfloc[0] + 0.5; - var mouseY = -sY + cY - bfloc[1] + 10; - var can = document.getElementById('labeldisplay').getContext('2d'); - var imageData = can.getImageData(mouseX, mouseY, 1, 1); - var labelValue = imageData.data; - $('#type').text(nameForKey(labelValue[0])[0].replace(/_/g, ' ')// + ' ' + labelValue[0] + ' ' + mouseX + ' ' + mouseY + ' ' + sX + ' ' + sY - ); - }); -} - -function resizeMapDiv() { - //Resize the height of the div containing the map. - //Do not call any map methods here as the resize is called before the map is created. - var d = document.getElementById("map"); - var offsetTop = 0; - var w = jQuery('#tabs-1').width(); - var w1 = w * .974; - var h = w * .917; h = h.toFixed(0); - jQuery('#map').css({ 'width': w1 + "px" }); - jQuery('#map').css({ 'height': h + "px" }); - jQuery('#tabs-1').css({ 'height': h + "px" }); - if (h >= 0) { - jQuery('#map').css({ 'height': h + "px" }); - } - for (var elem = d; elem != null; elem = elem.offsetParent) { - offsetTop += elem.offsetTop; - } - jQuery('#open-info-viewer-googlebrain').css('top', -jQuery('#map').height() - 37); - jQuery('#open-info-viewer-googlebrain').css('left', jQuery('#map').width() - 17); - jQuery('#open-info-viewer-googlebrain').fadeIn('slow'); - - jQuery('#open-map-overlay').css('top', -jQuery('#map').height() - 33); - jQuery('#open-map-overlay').css('left', jQuery('#map').width() - 18); - jQuery('#open-map-overlay').fadeIn('slow'); - - jQuery('#open-add-hotspot-location').css('top', -jQuery('#map').height() - 29); - jQuery('#open-add-hotspot-location').css('left', jQuery('#map').width() - 18); - jQuery('#open-add-hotspot-location').fadeIn('slow'); - - jQuery('#open-add-hotspot-notice-location').css('top', -jQuery('#map').height() - 25); - jQuery('#open-add-hotspot-notice-location').css('left', jQuery('#map').width() - 18); - jQuery('#open-add-hotspot-notice-location').fadeIn('slow'); - - jQuery('#open-comparison-viewer').css('top', -jQuery('#map').height() - 22); - jQuery('#open-comparison-viewer').css('left', jQuery('#map').width() - 17); - //jQuery('#open-comparison-viewer').fadeIn('slow'); - -} - -function resizeMapDiv2() { - //Resize the height of the div containing the map. - //Do not call any map methods here as the resize is called before the map is created. - var d = document.getElementById("map2"); - var offsetTop = 0; - var w = jQuery('#map-overlay').width(); - var h = jQuery('#map-overlay').height(); - jQuery('#map2').width(w - 12); - if (h >= 0) { - //jQuery('#map2').css({'height': (h-20)+"px"}); - jQuery('#map2').height(h - 12); - } - var w2 = jQuery(window).width(); - jQuery('#map-overlay').css('left', (w2 - w) / 2.0); - for (var elem = d; elem != null; elem = elem.offsetParent) { - offsetTop += elem.offsetTop; - } -} - -function resizeComparisonViewerDiv() { - var api = jQuery('#comparison-viewer').data("overlay"); - if (typeof api === "undefined") - return; - - var w = jQuery('#comparison-viewer').width(); - var h = jQuery('#comparison-viewer').height(); - jQuery('#comparison-viewer-div').width(w); - if (h >= 0) { - jQuery('#comparison-viewer-div').height(h); - } - jQuery('#comparison-viewer-right').css('width', w / 2 - 2); - jQuery('#comparison-viewer-left').css('width', w / 2 - 2); - jQuery('#map3').width(w / 2 - 2); - jQuery('#map4').width(w / 2 - 2); - jQuery('#map3').height(h - 2); - jQuery('#map4').height(h - 2); - var w2 = jQuery(window).width(); - jQuery('#comparison-viewer').css('left', (w2 - w) / 2.0); -} - -function resizeBlockfaceDiv() { - var api = jQuery('#blockface-overlay').data("overlay"); - if (typeof api === "undefined") - return; - - var w = jQuery('#blockface-overlay').width(); - var h = jQuery('#blockface-overlay').height(); - jQuery('#blockface-overlay-div').width(w - 12); - if (h >= 0) { - jQuery('#blockface-overlay-div').height(h - 12); - } - jQuery('#blockface-overlay-div').children().remove(); - - //jQuery('#blockface-overlay-div').css('background', 'url(./data/Blockface/LARGE/HM_Blockface_' + SliceNumber + '.jpg) center center no-repeat'); - //var url = 'url(getBlockface.php?h='+SliceNumber + '&type=large) center center no-repeat'; - var url = 'url(getBlockface.php?h=' + SliceNumber + '&type=large) center center no-repeat'; - jQuery('#blockface-overlay-div').css('background', url); - if (w < h) { - jQuery('#blockface-overlay-div').css('background-size', (w - 12) + 'px auto'); - // jQuery('#blockface-overlay-div').append("
                "); - } else { - jQuery('#blockface-overlay-div').css('background-size', 'auto ' + (h - 12) + 'px'); - var w2 = jQuery(window).width(); - jQuery('#blockface-overlay').css('left', (w2 - w) / 2.0); - } - -} - -function resizeMRIDiv() { - var d = document.getElementById("tabs-3-1"); - var e = document.getElementById("tabs-3-2"); - var f = document.getElementById("tabs-3-3"); - var g = document.getElementById("win0"); - var h = document.getElementById("win1"); - var i = document.getElementById("win2"); - var w = jQuery('#tabs-1').width(); - var x = w * .978; x = x.toFixed(0); - var y = x * .83; y = y.toFixed(0); - var z = y * .1; z = z.toFixed(0); - d.style.width = e.style.width = f.style.width = d.style.height = e.style.height = f.style.height = x + "px"; - g.style.width = h.style.width = i.style.width = g.style.height = h.style.height = i.style.height = x + "px"; - - - // g.style.width=h.style.width=i.style.width=y+"px"; - // g.style.height=h.style.height=i.style.height=x*.8+"px"; - jQuery('#window0').attr('width', y); - jQuery('#window1').attr('width', y); - jQuery('#window2').attr('width', y); - jQuery('#window0').attr('height', y); - jQuery('#window1').attr('height', y); - jQuery('#window2').attr('height', y); - jQuery('#window0').css({ 'margin-left': z + 'px' }); - jQuery('#window1').css({ 'margin-left': z + 'px' }); - jQuery('#window2').css({ 'margin-left': z + 'px' }); - jQuery('#window0').css({ 'margin-bottom': z + 'px' }); - jQuery('#window1').css({ 'margin-bottom': z + 'px' }); - jQuery('#window2').css({ 'margin-bottom': z + 'px' }); - mpr1.update(); - mpr2.update(); - mpr3.update(); - - jQuery('#open-info-viewer-MRI').css('top', -jQuery('#win2').height() - 35); - jQuery('#open-info-viewer-MRI').css('left', jQuery('#win2').width() + 4); - jQuery('#open-info-viewer-MRI').fadeIn('slow'); -} -function resizeSegDiv() { - //Resize the height of the div containing the map. - //Do not call any map methods here as the resize is called before the map is created. - var w = jQuery('#tabs-1').width(); - var x = w * .977; x = x.toFixed(0); - var y = x * .88 - 18; y = y.toFixed(0); - jQuery('#blockface_label').css({ 'width': x + "px" }); - jQuery('#blockface_label').css({ 'height': y + "px" }); - - jQuery('#open-info-viewer-segmentation').css('top', -jQuery('#blockface_label').height() - 74); - jQuery('#open-info-viewer-segmentation').css('left', jQuery('#blockface_label').width() + 1); - jQuery('#open-info-viewer-segmentation').fadeIn('slow'); - -} - -function resizeInfoViewerGoogleBrain() { - var w = jQuery(window).width(); - var x = w * .5; x = x.toFixed(0); - var y = x * .5; y = y.toFixed(0); - jQuery('#info-viewer-googlebrain').css({ 'width': x + "px" }); - // var y=jQuery('#info-viewer-googlebrain-content').height(); y= y.toFixed(0); - jQuery('#info-viewer-googlebrain').css({ 'height': y + "px" }); - - // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); - jQuery('#info-viewer-googlebrain').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); - // jQuery('#info-viewer-googlebrain').fadeIn('slow'); - -} - -function resizeInfoViewerSegmentation() { - var w = jQuery(window).width(); - var x = w * .5; x = x.toFixed(0); - var y = x * .5; y = y.toFixed(0); - jQuery('#info-viewer-segmentation').css({ 'width': x + "px" }); - // var y=jQuery('#info-viewer-segmentation-content').height(); y= y.toFixed(0); - jQuery('#info-viewer-segmentation').css({ 'height': y + "px" }); - - // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); - jQuery('#info-viewer-segmentation').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); - // jQuery('#info-viewer-googlebrain').fadeIn('slow'); - -} -function resizeInfoViewerMRI() { - var w = jQuery(window).width(); - var x = w * .5; x = x.toFixed(0); - var y = x * .5; y = y.toFixed(0); - jQuery('#info-viewer-MRI').css({ 'width': x + "px" }); - // var y=jQuery('#info-viewer-MRI-content').height(); y= y.toFixed(0); - jQuery('#info-viewer-MRI').css({ 'height': y + "px" }); - - // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); - jQuery('#info-viewer-MRI').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); - // jQuery('#info-viewer-googlebrain').fadeIn('slow'); - -} -function resizeInfoViewerModels() { - var w = jQuery(window).width(); - var x = w * .5; x = x.toFixed(0); - var y = x * .5; y = y.toFixed(0); - jQuery('#info-viewer-models').css({ 'width': x + "px" }); - // var y=jQuery('#info-viewer-models-content').height(); y= y.toFixed(0); - jQuery('#info-viewer-models').css({ 'height': y + "px" }); - - // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); - jQuery('#info-viewer-models').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); - // jQuery('#info-viewer-googlebrain').fadeIn('slow'); - -} -function resizeInfoViewerBlockface() { - var w = jQuery(window).width(); - var x = w * .5; x = x.toFixed(0); - var y = x * .5; y = y.toFixed(0); - jQuery('#info-viewer-blockface').css({ 'width': x + "px" }); - // var y=jQuery('#info-viewer-blockface-content').height(); y= y.toFixed(0); - jQuery('#info-viewer-blockface').css({ 'height': y + "px" }); - - // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); - // console.log('margin is:' + (parseFloat(w)-x)/2.0); - // console.log('w is: ' + w + ' x is: ' + x); - jQuery('#info-viewer-blockface').css('left', (parseFloat(w) - x) / 2.0); - // jQuery('#info-viewer-googlebrain').fadeIn('slow'); - -} -function resizeInfoViewerWalkthrough() { - var w = jQuery(window).width(); - var x = w * .5; x = x.toFixed(0); - var y = x * .5; y = y.toFixed(0); - jQuery('#info-viewer-walkthrough').css({ 'width': x + "px" }); - // var y=jQuery('#info-viewer-walkthrough-content').height(); y= y.toFixed(0); - jQuery('#info-viewer-walkthrough').css({ 'height': y + "px" }); - - // jQuery('#info-viewer-googlebrain').css('top', -jQuery('#blockface_label').height()-74); - jQuery('#info-viewer-walkthrough').css('left', parseInt((parseFloat(w) - x) / 2.0) + 'px'); - // jQuery('#info-viewer-googlebrain').fadeIn('slow'); - -} - -function updateBlockface() { - bfname = "2500_FINAL_HM_Blockface_" + SliceNumber + "_MEDIUM.jpg"; - segname = "colorwash_snapshot_" + SliceNumber + ".jpg"; - seglabel = "grayscale_snapshot_" + SliceNumber + ".png"; - talairach = "grid.png"; - $('#Blockface').attr('src', './data/Blockface/MEDIUM/"+bfname+"'); - // $('.Blockface').css('background-repeat', "no-repeat"); - $('.BlockfaceLabel').css('background', "url('/hm_web/data/Segmented/" + segname + "')"); - $('.BlockfaceLabel').css('background-repeat', "no-repeat"); - $('#section').text('Section ' + SliceNumber); -} - -function showMe(it, box) { - // var vis = (box.checked) ? "block" : "none"; - document.getElementById(it).style.display = vis; -} - -//load label names -function nameForKey(num) { - switch (num) { - case 0: { return [""]; } - case 1: { return ["Hippocampus"]; } - case 2: { return ["Putamen"]; } - case 3: { return ["Superior_Frontal_Gyrus"]; } - case 4: { return ["Caudate"]; } - case 5: { return [""]; } - case 6: { return [""]; } - case 7: { return ["Cingulate_Gyrus"]; } - case 8: { return ["Paracentral_Lobule"]; } - case 9: { return ["Precentral_Gyrus"]; } - case 10: { return ["Postcentral_Gyrus"]; } - case 11: { return ["Parietal_Operculum"]; } - case 12: { return ["Frontal_Operculum"]; } - case 13: { return ["Insular_Gyrus"]; } - case 14: { return ["Claustrum"]; } - case 15: { return ["Planum_Polare"]; } - case 16: { return ["Transverse_Temporal_Gyrus"]; } - case 17: { return ["Planum_Temporale"]; } - case 18: { return ["Superior_Temporal_Gyrus"]; } - case 19: { return ["Middle_Temporal_Gyrus"]; } - case 20: { return ["Inferior_Temporal_Gyrus"]; } - case 21: { return ["Fusiform_Gyrus"]; } - case 22: { return ["Parahippocampal_Gyrus"]; } - case 23: { return ["Entorhinal_Cortex"]; } - case 24: { return ["Amygdala"]; } - case 25: { return ["Optic_Tract"]; } - case 26: { return ["Internal_Globus_Pallidus"]; } - case 27: { return ["Stria_Terminalis"]; } - case 28: { return ["Thalamus"]; } - case 29: { return ["Zona_Incerta"]; } - case 30: { return ["Substantia_Nigra"]; } - case 31: { return ["Subthalamic_Nucleus"]; } - case 32: { return ["Supramarginal_Gyrus"]; } - case 33: { return ["Lateral_Geniculate_Nucleus"]; } - case 34: { return ["Medial_Geniculate_Nucleus"]; } - case 35: { return [""]; } - case 36: { return ["Red_Nucleus"]; } - case 37: { return [""]; } - case 38: { return ["Inferior_Colliculus"]; } - case 39: { return [""]; } - case 40: { return [""]; } - case 41: { return [""]; } - case 42: { return ["Periaqueductal_Gray"]; } - case 43: { return [""]; } - case 44: { return ["Pineal_Gland"]; } - case 45: { return ["Hippocampal_Commissure"]; } - case 46: { return ["Precuneus_Gyrus"]; } - case 47: { return ["Lingual_Gyrus"]; } - case 48: { return [""]; } - case 49: { return ["Superior_Parietal_Lobule"]; } - case 50: { return ["Occipital_Gyrus"]; } - case 51: { return ["Angular_Gyrus"]; } - case 52: { return ["Striate_Area"]; } - case 53: { return ["Parietooccipital_Cortex"]; } - case 54: { return ["Temporooccipital_Cortex"]; } - case 55: { return ["Hypothalamus"]; } - case 56: { return ["Mammillary_Bodies"]; } - case 57: { return ["Oculomotor_Nerve"]; } - case 58: { return [""]; } - case 59: { return ["Middle_Frontal_Gyrus"]; } - case 60: { return ["Ambiens_Gyrus"]; } - case 61: { return ["Basal_Nucleus"]; } - case 62: { return ["Piriform_Cortex"]; } - case 63: { return ["Accumbens_Nucleus"]; } - case 64: { return ["Paraterminal_Gyrus"]; } - case 65: { return ["Inferior_Frontal_Gyrus_Opercular"]; } - case 66: { return ["Fundus_Region"]; } - case 67: { return ["Straight_Gyrus"]; } - case 68: { return ["Posteromedial_Orbital_Lobule"]; } - case 69: { return ["Area_Piriformis_Insulae"]; } - case 70: { return ["Posterior_Orbital_Gyrus"]; } - case 71: { return ["Inferior_Frontal_Gyrus_Triangular"]; } - case 72: { return ["Basal_Operculum"]; } - case 73: { return ["Medial_Orbital_Gyrus"]; } - case 74: { return ["Inferior_Rostal_Gyrus"]; } - case 75: { return ["Subcallosal_Gyrus"]; } - case 76: { return ["Inferior_Frontal_Gyrus_Orbital"]; } - case 77: { return ["Lateral_Orbital_Gyrus"]; } - case 78: { return ["Middle_Frontopolar_Gyrus"]; } - case 79: { return ["Inferior_Frontopolar_Gyrus"]; } - case 80: { return ["Frontomarginal_Gyrus"]; } - case 81: { return ["Superior_Rostral_Gyrus"]; } - case 82: { return ["Intermediate_Orbital_Gyrus"]; } - case 83: { return ["Declive"]; } - case 84: { return ["Central_Lobule"]; } - case 85: { return ["Folium"]; } - case 86: { return ["Lingula"]; } - case 87: { return ["Tuber"]; } - case 88: { return ["Culmen"]; } - case 89: { return ["Uvula"]; } - case 90: { return ["Nodulus"]; } - case 91: { return ["Pyramis"]; } - case 92: { return ["Ala_of_the_Central_Lobule"]; } - case 93: { return ["Anterior_Quadrangular_Lobule"]; } - case 94: { return ["Posterior_Quadrangular_Lobule"]; } - case 95: { return ["Superior_Semilunar_Lobule"]; } - case 96: { return ["Inferior_Semilunar_Lobule"]; } - case 97: { return ["Gracile_Lobule"]; } - case 98: { return ["Biventral_Lobule"]; } - case 99: { return ["Tonsilla"]; } - case 100: { return [""]; } - case 101: { return ["Flocculus"]; } - case 102: { return ["White_Matter"]; } - case 103: { return [""]; } - case 104: { return [""]; } - case 105: { return ["Optic_Chiasm"]; } - case 106: { return ["Subcallosal_Area"]; } - case 107: { return ["Olfactory_Area"]; } - case 108: { return ["Perirhinal_Cortex"]; } - case 109: { return [""]; } - case 110: { return ["Bed_Nucleus_of_the_Stria"]; } - case 111: { return ["External_Globus_Pallidus"]; } - case 112: { return ["Anterior_Commissure"]; } - case 113: { return [""]; } - case 114: { return [""]; } - case 115: { return [""]; } - case 116: { return [""]; } - case 117: { return [""]; } - case 118: { return [""]; } - case 119: { return [""]; } - case 120: { return [""]; } - case 121: { return [""]; } - case 122: { return ["Cerebral_Peduncle"]; } - case 123: { return [""]; } - case 124: { return [""]; } - case 125: { return ["Third_Ventricle"]; } - case 126: { return [""]; } - case 127: { return ["Fornix"]; } - case 128: { return [""]; } - case 129: { return ["Internal_Capsule"]; } - case 130: { return ["Lateral_Ventricle"]; } - case 131: { return ["Striatal_Cell_Bridges"]; } - case 132: { return ["Pons"]; } - case 133: { return ["Superior_Cerebellar_Peduncle"]; } - case 134: { return [""]; } - case 135: { return [""]; } - case 136: { return [""]; } - case 137: { return [""]; } - case 138: { return [""]; } - case 139: { return [""]; } - case 140: { return ["Fimbria"]; } - case 141: { return [""]; } - case 142: { return ["Posterior_Commissure"]; } - case 143: { return ["Cerebral_Aqueduct"]; } - case 144: { return [""]; } - case 145: { return ["Optic_Radiation"]; } - case 146: { return ["Superior_Colliculus"]; } - case 147: { return ["Indusium_Griseum"]; } - case 148: { return ["Area_Orbitoinsularis"]; } - case 149: { return ["Fourth_Ventricle"]; } - case 150: { return ["Inferior_Olivary_Nucleus"]; } - case 151: { return ["Cortex"]; } - case 152: { return ["Lesion"]; } - case 153: { return ["Olfactory_Tract"]; } - case 154: { return ["Anterior_Olfactory_Nucleus"]; } - case 155: { return ["Septal_Nuclei"]; } - case 156: { return ["Simple_Lobule"]; } - case 157: { return ["Dentate_Nucleus"]; } - case 158: { return ["Middle_Cerebellar_Peduncle"]; } - case 159: { return [""]; } - case 160: { return ["Removed_MTL_Guess"]; } - case 161: { return ["Surgical_Clip"]; } - default: { return [" "]; } - } -} - -var cX = 0; var cY = 0; var rX = 0; var rY = 0; - -function UpdateCursorPosition(e) { cX = e.pageX; cY = e.pageY; } -function UpdateCursorPositionDocAll(e) { cX = event.clientX; cY = event.clientY; } - -if (document.all) { document.onmousemove = UpdateCursorPositionDocAll; } -else { document.onmousemove = UpdateCursorPosition; } - -function AssignPosition(d) { - if (self.pageYOffset) { - rX = self.pageXOffset; - rY = self.pageYOffset; - } - else if (document.documentElement && document.documentElement.scrollTop) { - rX = document.documentElement.scrollLeft; - rY = document.documentElement.scrollTop; - } - else if (document.body) { - rX = document.body.scrollLeft; - rY = document.body.scrollTop; - } - if (document.all) { - cX += rX; - cY += rY; - } - // d.style.left = (cX+10) + "px"; - // d.style.top = (cY+10) + "px"; -} - -function HideContent(d) { - if (d.length < 1) { return; } - document.getElementById(d).style.display = "none"; -} -function ShowContent(d) { - if (d.length < 1) { return; } - var dd = document.getElementById(d); - dd.style.display = "block"; - // bfloc = findPosC(document.getElementById('blockface_nolabel')) - // var mouseX = e.pageX - bfloc[0]; - // var mouseY = e.pageY - bfloc[1]; -} -function ReverseContentDisplay(d) { - if (d.length < 1) { return; } - var dd = document.getElementById(d); - AssignPosition(dd); - if (dd.style.display == "none") { dd.style.display = "block"; } - else { dd.style.display = "none"; } -} - -var locationBySlice = {}; -locationBySlice['0199'] = { centerLat: 44.43137917475613, centerLng: -35.5078125 }; -locationBySlice['0235'] = { centerLat: 42.38852556705306, centerLng: -34.1015625 }; -locationBySlice['0271'] = { centerLat: 28.726182356515025, centerLng: -17.2265625 }; -locationBySlice['0307'] = { centerLat: 39.195599922646224, centerLng: -21.4453125 }; -locationBySlice['0343'] = { centerLat: 40.27696080353267, centerLng: -5.9765625 }; -locationBySlice['0379'] = { centerLat: 71.2291387369125, centerLng: -90.3515625 }; -locationBySlice['0415'] = { centerLat: 71.67642594903772, centerLng: -93.8671875 }; -locationBySlice['0433'] = { centerLat: 65.85538134683627, centerLng: -93.1640625 }; -locationBySlice['0487'] = { centerLat: 65.85538134683627, centerLng: -93.1640625 }; -locationBySlice['0523'] = { centerLat: 65.85538134683627, centerLng: -90.3515625 }; -locationBySlice['0559'] = { centerLat: 65.85538134683627, centerLng: -87.5390625 }; -locationBySlice['0595'] = { centerLat: 65.85538134683627, centerLng: -84.0234375 }; -locationBySlice['0631'] = { centerLat: 65.85538134683627, centerLng: -87.5390625 }; -locationBySlice['0649'] = { centerLat: 65.85538134683627, centerLng: -88.2421875 }; -locationBySlice['0667'] = { centerLat: 65.85538134683627, centerLng: -82.6171875 }; -locationBySlice['0703'] = { centerLat: 65.85538134683627, centerLng: -86.1328125 }; -locationBySlice['0721'] = { centerLat: 65.85538134683627, centerLng: -88.2421875 }; -locationBySlice['0739'] = { centerLat: 65.85538134683627, centerLng: -85.4296875 }; -locationBySlice['0775'] = { centerLat: 65.85538134683627, centerLng: -79.8046875 }; -locationBySlice['0793'] = { centerLat: 65.85538134683627, centerLng: -80.5078125 }; -locationBySlice['0811'] = { centerLat: 65.85538134683627, centerLng: -85.4296875 }; -locationBySlice['0847'] = { centerLat: 65.85538134683627, centerLng: -75.5859375 }; -locationBySlice['0883'] = { centerLat: 65.85538134683627, centerLng: -76.9921875 }; -locationBySlice['0901'] = { centerLat: 65.85538134683627, centerLng: -72.7734375 }; -locationBySlice['0919'] = { centerLat: 65.85538134683627, centerLng: -74.8828125 }; -locationBySlice['0955'] = { centerLat: 65.85538134683627, centerLng: -72.7734375 }; -locationBySlice['0973'] = { centerLat: 65.85538134683627, centerLng: -77.6953125 }; -locationBySlice['0991'] = { centerLat: 65.85538134683627, centerLng: -74.8828125 }; -locationBySlice['1027'] = { centerLat: 65.85538134683627, centerLng: -69.2578125 }; -locationBySlice['1063'] = { centerLat: 65.85538134683627, centerLng: -63.6328125 }; -locationBySlice['1099'] = { centerLat: 65.85538134683627, centerLng: -64.3359375 }; -locationBySlice['1135'] = { centerLat: 65.85538134683627, centerLng: -63.6328125 }; -locationBySlice['1171'] = { centerLat: 65.85538134683627, centerLng: -60.8203125 }; -locationBySlice['1207'] = { centerLat: 65.85538134683627, centerLng: -58.0078125 }; -locationBySlice['1855'] = { centerLat: 65.85538134683627, centerLng: -72.7734375 }; -locationBySlice['1891'] = { centerLat: 65.85538134683627, centerLng: -72.7734375 }; -locationBySlice['1927'] = { centerLat: 65.85538134683627, centerLng: -76.9921875 }; -locationBySlice['1963'] = { centerLat: 65.85538134683627, centerLng: -78.3984375 }; -locationBySlice['1999'] = { centerLat: 65.85538134683627, centerLng: -86.1328125 }; -locationBySlice['2035'] = { centerLat: 24.963092337069753, centerLng: -5.9765625 }; -locationBySlice['2071'] = { centerLat: 22.38760541276085, centerLng: -3.1640625 }; -locationBySlice['2107'] = { centerLat: 27.48579875372881, centerLng: -5.9765625 }; -locationBySlice['2143'] = { centerLat: 36.982317556959366, centerLng: -21.4453125 }; -locationBySlice['2179'] = { centerLat: 45.426939316192815, centerLng: -22.8515625 }; -locationBySlice['2215'] = { centerLat: 56.045621879849854, centerLng: -34.1015625 }; -locationBySlice['2251'] = { centerLat: 59.77682973372056, centerLng: -39.7265625 }; -locationBySlice['2287'] = { centerLat: 60.47722221435968, centerLng: -52.3828125 }; -locationBySlice['2359'] = { centerLat: 36.982317556959366, centerLng: 1.0546875 }; - -function tileURL(a, b) { - // pervent wrap around - if (a.y < 0 || a.y >= (1 << b)) { - return null; - } - if (a.x < 0 || a.x >= (1 << b)) { - return null; - } - var c = Math.pow(2, b); - var d = a.x; - var e = a.y; - var f = "t"; - for (var g = 0; g < b; g++) { - c = c / 2; - if (e < c) { - if (d < c) { f += "q" } - else { f += "r"; d -= c } - } else { - if (d < c) { f += "t"; e -= c } - else { f += "s"; d -= c; e -= c } - } - } - subdirs = 3; - tmp = ""; - if (f.length >= subdirs) { // subdivide into sub-directories - for (i = 0; i < subdirs; i++) { - tmp += f.charAt(i) + "/"; - } - } - tmp += f; - return current_slice_path + "/" + tmp + ".jpg"; -} -function tileURL2(a, b) { - // pervent wrap around - if (a.y < 0 || a.y >= (1 << b)) { - return null; - } - if (a.x < 0 || a.x >= (1 << b)) { - return null; - } - var c = Math.pow(2, b); - var d = a.x; - var e = a.y; - var f = "t"; - for (var g = 0; g < b; g++) { - c = c / 2; - if (e < c) { - if (d < c) { f += "q" } - else { f += "r"; d -= c } - } else { - if (d < c) { f += "t"; e -= c } - else { f += "s"; d -= c; e -= c } - } - } - subdirs = 3; - tmp = ""; - if (f.length >= subdirs) { // subdivide into sub-directories - for (i = 0; i < subdirs; i++) { - tmp += f.charAt(i) + "/"; - } - } - tmp += f; - return alternate_slice_path + "/" + tmp + ".jpg"; -} - -function updateMap(_SliceNumber, _maplocation, _zoom) { - var maxZoom = 11; - var initialZoom = 1; - - // change zoom level based on size of region - - if (typeof _zoom != "undefined") - initialZoom = _zoom; - - var customMapOptions = { - getTileUrl: tileURL, - isPng: false, - maxZoom: maxZoom, - minZoom: 1, - tileSize: new google.maps.Size(256, 256), - radius: 1738000, - name: "HM", - streetViewControl: false - }; - if (typeof SliceNumber !== "undefined" - && (SliceNumber < 379 || SliceNumber > 1999)) { - initialZoom = 0; - maxZoom = 10; - var customMapOptions = { - getTileUrl: tileURL, - isPng: false, - maxZoom: maxZoom, - minZoom: 0, - tileSize: new google.maps.Size(256, 256), - radius: 1738000, - name: "HM", - streetViewControl: false - }; - } - if (typeof SliceNumber !== "undefined" - && (SliceNumber < 91 || SliceNumber > 2359)) { - initialZoom = -1; - maxZoom = 9; - var customMapOptions = { - getTileUrl: tileURL, - isPng: false, - maxZoom: maxZoom, - minZoom: -1, - tileSize: new google.maps.Size(256, 256), - radius: 1738000, - name: "HM", - streetViewControl: false - }; - } - var customMapType = new google.maps.ImageMapType(customMapOptions); - - var centreLat = 66.70383915858723; - var centreLon = -48.1640625; - if (typeof SliceNumber !== "undefined" && - typeof locationBySlice[SliceNumber] !== "undefined") { - centreLat = locationBySlice[SliceNumber].centerLat; - centreLon = locationBySlice[SliceNumber].centerLng; - } - if (typeof _maplocation != "undefined") { - centreLat = _maplocation[0]; - centreLon = _maplocation[1]; - } - - var mycenter = new google.maps.LatLng(centreLat, centreLon); - var myOptions = { - center: mycenter, - zoom: initialZoom, - mapTypeControlOptions: { - mapTypeIds: ['map'] - }, - mapTypeId: 'map', - backgroundColor: "#FFFFFF", - zoomControl: true, - scrollwheel: true, - streetViewControl: false, - draggableCursor: 'crosshair', - overviewMapControl: true, - overviewMapControlOptions: { - position: google.maps.ControlPosition.RIGHT_TOP - }, - zoomControlOptions: { - style: google.maps.ZoomControlStyle.DEFAULT - } - }; - map = new google.maps.Map(document.getElementById("map"), myOptions); - map.mapTypes.set('HM', customMapType); - map.setMapTypeId('HM'); - //map.enableScrollWheelZoom(); - //map.enableContinuousZoom(); - - google.maps.event.addListener(map, "bounds_changed", function (event) { - // sO.draw(); - // line drawn on screen is 100px == 200micron - var mz = customMapOptions.maxZoom; - var st = document.getElementById('scaleText'); - // if (typeof str === "undefined") - // return; - var zoomDif = (mz + 1) - map.getZoom(); - var dif = 20 / zoomDif; // 20x is our max zoom, added factor of 2.0 - var siz1 = (50 * 0.37 * Math.pow(2, zoomDif - 1)); - var unit1 = 'µm '; - if (siz1 > 1000) { - siz1 = (siz1 / 1000.0).toFixed(1); - unit1 = 'mm '; - } - var siz2 = (20 / Math.pow(2, zoomDif - 1)).toFixed(2); - st.innerHTML = siz1 + unit1 //+ "[" + siz2 + "x" + "]"; - jQuery('.gmnoprint a').html('Google Terms of Use'); - }); - - - google.maps.event.addListener(map, 'mousewheel DOMMouseScroll', function (e) { - if (!e) { - e = window.event - } - if (e.preventDefault) { - e.preventDefault() - } - e.returnValue = false; - }); - - // switch on comparison viewer if slice has a comparison slice available - if (typeof SliceNumber !== "undefined") { - var found = false; - for (var i = 0; i < alternate_pairings.length; i++) { - if (alternate_pairings[i][0] == SliceNumber) { - jQuery('#open-comparison-viewer').fadeIn('slow'); - found = true; - break; - } - } - if (found == false) - jQuery('#open-comparison-viewer').fadeOut(); - } -} - -/*Pairing for comparison viewer*/ -var alternate_slice_path = "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/GoogleBrain/HM/Slice_1207/Result"; -var alternate_pairings = [["1243", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0009/Slice_1234/Result"], -["1171", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0015/Slice_1162/Result"], -["0811", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0001/Slice_0718/Result"], -["1315", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0009/Slice_1306/Result"], -["0739", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0009/Slice_0730/Result"], -["0955", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0009/Slice_0947/Result"], -["1027", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-AG-0015/Slice_0910/Result"] -]; - -if (user_name == "jannese") { - alternate_pairings.push(["1207", "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/TBO-ME-0028/Slice_0671/Result"]); -} - -var shift_3_to_4 = [0, 0]; - -// Comparison viewer update -function updateMap34() { - - alternate_slice_path = "//s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/RM/Slice_1234/Result"; - for (var i = 0; i < alternate_pairings.length; i++) { - if (SliceNumber == alternate_pairings[i][0]) { - alternate_slice_path = alternate_pairings[i][1]; - break; - } - } - var customMapOptions = { - getTileUrl: tileURL, - isPng: false, - maxZoom: 11, - minZoom: 0, - tileSize: new google.maps.Size(256, 256), - radius: 1738000, - name: "HM", - streetViewControl: false - }; - var customMapType = new google.maps.ImageMapType(customMapOptions); - var customMapOptions2 = { - getTileUrl: tileURL2, - isPng: false, - maxZoom: 11, - minZoom: 0, - tileSize: new google.maps.Size(256, 256), - radius: 1738000, - name: "HM", - streetViewControl: false - }; - var customMapType2 = new google.maps.ImageMapType(customMapOptions2); - - initialZoom = 1; - var centreLat = 66.70383915858723; - var centreLon = -48.1640625; - if (typeof SliceNumber !== "undefined" && - typeof locationBySlice[SliceNumber] !== "undefined") { - centreLat = locationBySlice[SliceNumber].centerLat; - centreLon = locationBySlice[SliceNumber].centerLng; - } - - var mycenter = new google.maps.LatLng(centreLat, centreLon); - var myOptions = { - center: mycenter, - zoom: initialZoom, - mapTypeControlOptions: { - mapTypeIds: ['map3'] - }, - mapTypeId: 'map3', - backgroundColor: "#FFFFFF", - draggableCursor: 'crosshair', - zoomControl: true, - scrollwheel: true, - streetViewControl: false, - overviewMapControl: true, - overviewMapControlOptions: { - opened: true, - position: google.maps.ControlPosition.RIGHT_TOP - }, - zoomControlOptions: { - style: google.maps.ZoomControlStyle.DEFAULT - } - }; - map3 = new google.maps.Map(document.getElementById("map3"), myOptions); - map3.mapTypes.set('HM', customMapType2); - map3.setMapTypeId('HM'); - map4 = new google.maps.Map(document.getElementById("map4"), myOptions); - map4.mapTypes.set('HM', customMapType); - map4.setMapTypeId('HM'); - //map.enableScrollWheelZoom(); - //map.enableContinuousZoom(); - - google.maps.event.addListener(map4, "zoom_changed", function (event) { - var zoom = map4.getZoom(); - map3.setZoom(zoom); - }); - google.maps.event.addListener(map4, "center_changed", function (event) { - var center = map4.getCenter(); - var newPos = new google.maps.LatLng(center.lat() - shift_3_to_4[0], center.lng() - shift_3_to_4[1]); - - map3.setCenter(newPos); // keep the old shift - }); - - google.maps.event.addListener(map3, "drag", function () { - var cent3 = map3.getCenter(); - var cent4 = map4.getCenter(); - shift_3_to_4 = [cent4.lat() - cent3.lat(), cent4.lng() - cent3.lng()]; - }); - - google.maps.event.addListener(map3, "bounds_changed", function (event) { - - // sO.draw(); - // line drawn on screen is 100px == 200micron - var mz = customMapOptions.maxZoom; - var st = document.getElementById('scaleText2'); - var zoomDif = (mz + 1) - map3.getZoom(); - var dif = 20 / zoomDif; // 20x is our max zoom, added factor of 2.0 - var siz1 = (50 * 0.37 * Math.pow(2, zoomDif - 1)); - var unit1 = 'µm '; - if (siz1 > 1000) { - siz1 = (siz1 / 1000.0).toFixed(1); - unit1 = 'mm '; - } - var siz2 = (20 / Math.pow(2, zoomDif - 1)).toFixed(2); - st.innerHTML = siz1 + unit1 //+ "[" + siz2 + "x" + "]" - ; - //st.innerHTML = siz1 + unit1 + siz2 + "x "; - - // st.innerHTML = (100 * 0.37*2.0 * Math.pow(2, zoomDif - 1)) + 'µm ' + (20 / Math.pow(2, zoomDif-1)).toFixed(1) + "x "; - jQuery('.gmnoprint a').html('Google Terms of Use'); - }); - - - google.maps.event.addListener(map3, 'mousewheel DOMMouseScroll', function (e) { - if (!e) { - e = window.event - } - if (e.preventDefault) { - e.preventDefault() - } - e.returnValue = false; - }); - google.maps.event.addListener(map4, "bounds_changed", function (event) { - // sO.draw(); - // line drawn on screen is 100px == 200micron - var mz = customMapOptions.maxZoom; - var st = document.getElementById('scaleText2'); - var zoomDif = (mz + 1) - map3.getZoom(); - var dif = 20 / zoomDif; // 20x is our max zoom, added factor of 2.0 - var siz1 = (50 * 0.37 * Math.pow(2, zoomDif - 1)); - var unit1 = 'µm '; - if (siz1 > 1000) { - siz1 = (siz1 / 1000.0).toFixed(1); - unit1 = 'mm '; - } - var siz2 = (20 / Math.pow(2, zoomDif - 1)).toFixed(2); - st.innerHTML = siz1 + unit1 //+ "[" + siz2 + "x" + "]" - ; - //st.innerHTML = siz1 + unit1 + siz2 + "x "; - - // st.innerHTML = (100 * 0.37*2.0 * Math.pow(2, zoomDif - 1)) + 'µm ' + (20 / Math.pow(2, zoomDif-1)).toFixed(1) + "x "; - jQuery('.gmnoprint a').html('Google Terms of Use'); - }); - - - google.maps.event.addListener(map4, 'mousewheel DOMMouseScroll', function (e) { - if (!e) { - e = window.event - } - if (e.preventDefault) { - e.preventDefault() - } - e.returnValue = false; - }); -} - -function updateMap2() { - var customMapOptions = { - getTileUrl: tileURL, - isPng: false, - maxZoom: 11, - minZoom: 0, - tileSize: new google.maps.Size(256, 256), - radius: 1738000, - name: "HM", - streetViewControl: false - }; - var customMapType = new google.maps.ImageMapType(customMapOptions); - - initialZoom = 1; - var centreLat = 66.70383915858723; - var centreLon = -48.1640625; - if (typeof SliceNumber !== "undefined" && - typeof locationBySlice[SliceNumber] !== "undefined") { - centreLat = locationBySlice[SliceNumber].centerLat; - centreLon = locationBySlice[SliceNumber].centerLng; - } - - var mycenter = new google.maps.LatLng(centreLat, centreLon); - var myOptions = { - center: mycenter, - zoom: initialZoom, - mapTypeControlOptions: { - mapTypeIds: ['map2'] - }, - mapTypeId: 'map2', - backgroundColor: "#FFFFFF", - draggableCursor: 'crosshair', - zoomControl: true, - scrollwheel: true, - streetViewControl: false, - overviewMapControl: true, - overviewMapControlOptions: { - opened: true, - position: google.maps.ControlPosition.RIGHT_TOP - }, - zoomControlOptions: { - style: google.maps.ZoomControlStyle.DEFAULT - } - }; - map2 = new google.maps.Map(document.getElementById("map2"), myOptions); - map2.mapTypes.set('HM', customMapType); - map2.setMapTypeId('HM'); - //map.enableScrollWheelZoom(); - //map.enableContinuousZoom(); - - google.maps.event.addListener(map2, "bounds_changed", function (event) { - // sO.draw(); - // line drawn on screen is 100px == 200micron - var mz = customMapOptions.maxZoom; - var st = document.getElementById('scaleText2'); - var zoomDif = (mz + 1) - map2.getZoom(); - var dif = 20 / zoomDif; // 20x is our max zoom, added factor of 2.0 - var siz1 = (50 * 0.37 * Math.pow(2, zoomDif - 1)); - var unit1 = 'µm '; - if (siz1 > 1000) { - siz1 = (siz1 / 1000.0).toFixed(1); - unit1 = 'mm '; - } - var siz2 = (20 / Math.pow(2, zoomDif - 1)).toFixed(2); - st.innerHTML = siz1 + unit1 //+ "[" + siz2 + "x" + "]" - ; - //st.innerHTML = siz1 + unit1 + siz2 + "x "; - - // st.innerHTML = (100 * 0.37*2.0 * Math.pow(2, zoomDif - 1)) + 'µm ' + (20 / Math.pow(2, zoomDif-1)).toFixed(1) + "x "; - jQuery('.gmnoprint a').html('Google Terms of Use'); - }); - - - google.maps.event.addListener(map2, 'mousewheel DOMMouseScroll', function (e) { - if (!e) { - e = window.event - } - if (e.preventDefault) { - e.preventDefault() - } - e.returnValue = false; - }); -} - -var current_slice_path = "/hm_web/GoogleBrain/Subjects/HM/Slice_1207/Result"; -if (typeof user_1279 !== "undefined" && user_1279 == "1") { - current_slice_path = "/hm_web/GoogleBrain/Subjects/HM/Slice_1279/Result"; -} - -$(document).ready(function () { - - - $.extend($.fn.disableTextSelect = function () { - return this.each(function () { - if ($.browser.mozilla) {//Firefox - $(this).css('MozUserSelect', 'none'); - } else if ($.browser.msie) {//IE - $(this).bind('selectstart', function () { return false; }); - } else {//Opera, etc. - $(this).mousedown(function () { return false; }); - } - }); - }); - $('.noSelect').disableTextSelect();//No text selection on elements with a class of 'noSelect' - - - // if the user presses the next slide button we should go to the next slide - jQuery('#walkthrough-scroll-next').click(function () { - // what is the currently displayed div? - var thisDiv = 0; - var thisLocation = jQuery('.walkthrough').scrollTop(); - var slides = jQuery('.walkthrough .slide'); - var minDist, location, winnerPos; - var first = true; - var locations = new Array(); - jQuery.each(slides, function (index, value) { - //var pos = jQuery(value).offset().top; - var pos = thisLocation + jQuery(value).position().top; - var pos2 = jQuery(value).position().top; - var pos3 = jQuery('.walkthrough')[0].scrollHeight; - locations[index] = pos; - if (first) { - first = false; - winnerPos = pos; - minDist = Math.abs(pos - thisLocation); - thisDiv = index; - } - if (Math.abs(pos - thisLocation) < minDist) { - minDist = Math.abs(pos - thisLocation); - thisDiv = index; - winnerPos = pos; - } - }); - - // scroll to the next - var nextDiv = thisDiv + 1; - if (nextDiv > locations.length - 1) { - nextDiv = 0; - } - jQuery('.walkthrough').animate({ scrollTop: locations[nextDiv] }, 'slow'); - - }); - jQuery('#walkthrough-scroll-prev').click(function () { - // what is the currently displayed div? - var thisDiv = 0; - var thisLocation = jQuery('.walkthrough').scrollTop(); - var slides = jQuery('.walkthrough .slide'); - var minDist, location, winnerPos; - var first = true; - var locations = new Array(); - jQuery.each(slides, function (index, value) { - //var pos = jQuery(value).offset().top; - var pos = thisLocation + jQuery(value).position().top; - var pos2 = jQuery(value).position().top; - var pos3 = jQuery('.walkthrough')[0].scrollHeight; - locations[index] = pos; - if (first) { - first = false; - winnerPos = pos; - minDist = Math.abs(pos - thisLocation); - thisDiv = index; - } - if (Math.abs(pos - thisLocation) < minDist) { - minDist = Math.abs(pos - thisLocation); - thisDiv = index; - winnerPos = pos; - } - }); - - // scroll to the next - var nextDiv = thisDiv - 1; - if (nextDiv < 0) { - nextDiv = locations.length - 1; - } - jQuery('.walkthrough').animate({ scrollTop: locations[nextDiv] }, 'slow'); - - }); - - //$('#rad-sub-nav li a').last().css('background-image', 'none'); - //$('#sub-nav li a').last().css('background-image', 'none'); - jQuery('#model-interface').buttonset(); - - for (var i = 0; i < jQuery('#model-interface').children().length / 2.0; i++) { // set all buttons to off - jQuery('#model-' + i).attr('checked', false).button("refresh"); - } - AEROTWIST.A3.Sample.finishedLoading = function () { - //jQuery('#model-5').trigger('change'); - }; - - jQuery('#model-interface :checkbox').change(function () { - var buttonChanged = jQuery(this).attr('id'); - // If we pressed something other than "BRAIN SURFACE" make sure that - // "BRAIN SURFACE" is off. - //if (buttonChanged != "model-5") { - // jQuery('#model-5').attr('checked', false); - // jQuery('#model-interface').buttonset('refresh'); - //} - - for (var i = 0; i < AEROTWIST.A3.Sample.geometries.length; i++) { - // jQuery('#model-'+i).attr('checked',false).button("refresh"); - if (jQuery('#model-' + i).attr('checked') - && AEROTWIST.A3.Sample.geometries[i] == null) { - var b = i; // make a local copy, don't reference the parents i - AEROTWIST.A3.Sample.loadThis(b, function () { - AEROTWIST.A3.Sample.geometries[b].visible = jQuery('#model-' + b).attr('checked'); - }); - - } else { - if (AEROTWIST.A3.Sample.geometries[i] !== null) - AEROTWIST.A3.Sample.geometries[i].visible = jQuery('#model-' + i).attr('checked'); - } - } - }); - //jQuery('#model-5').attr('checked',true).button("refresh"); - //jQuery('#model-6').attr('checked',true).button("refresh"); - - jQuery('#open-info-viewer-models').css('top', -jQuery('#model_container').height() + 1); - jQuery('#open-info-viewer-models').css('left', jQuery('#model_container').width() - 18); - jQuery('#open-info-viewer-models').fadeIn('slow'); - - jQuery('#open-info-viewer-blockface').fadeIn('slow'); - - jQuery('#open-info-viewer-walkthrough').css('top', -jQuery('#guided').height() + 194); - jQuery('#open-info-viewer-walkthrough').css('left', jQuery('#guided').width() - 18); - jQuery('#open-info-viewer-walkthrough').fadeIn('slow'); - - jQuery('#open-blockface-overlay').fadeIn('slow'); - - jQuery('#open-add-hotspot-location').live('click', function () { - jQuery('#add-hotspot-location').live('onLoad', function () { - jQuery('#add-hotspot-location').css('width', 300); - jQuery('#add-hotspot-location').css('height', 100); - }); - jQuery('#add-hotspot-location').dialog({ - modal: true, - title: "Add location to the Histological Slide Box", - closeOnClick: false, - effect: 'apple', - buttons: { - "Cancel": function () { - jQuery('#add-hotspot-location').dialog('close'); - return false; - }, - "Submit": function () { - var location = map.getCenter(); - var zoom = map.getZoom(); - var numTiles = 1 << map.getZoom(); - var projection = map.getProjection(); - var worldCoordinate = projection.fromLatLngToPoint(location); - var pixelCoordinate = new google.maps.Point( - worldCoordinate.x * numTiles, - worldCoordinate.y * numTiles); - var tileCoordinate = new google.maps.Point( - Math.floor(pixelCoordinate.x / 256), - Math.floor(pixelCoordinate.y / 256)); - var tile = tileURL(tileCoordinate, zoom); - // submit the data to hotspot - jQuery.ajax({ - type: "POST", - url: '/hm_web/data/hotspots/process.php?callback=success', - data: { - user: user_name == "" ? "anonymous" : user_name, - add: jQuery('#hotspot-location-title').val(), - body: jQuery('#hotspot-location-body').val().replace(/\n/g, '
                '), - location: "[" + SliceNumber + "," + location.lat() + "," + location.lng() + "," + zoom + "," + tile + "]" - }, - dataType: 'jsonp', - context: document.body, - success: function (data) { // this is very strange, we should use success here, but it does not work... - jQuery('#add-hotspot-location').dialog('close'); - - // show a dialog that adding the hotspot was successful - jQuery('#add-hotspot-location-success').dialog({ - modal: true, - title: "Added hotspot location", - closeOnClick: false, - effect: 'apply', - load: true, - buttons: { - "OK": function () { jQuery('#add-hotspot-location-success').dialog('close'); } - } - }); - } - }); - return false; - } - }, - load: true - }); - }); - - jQuery('#open-add-hotspot-notice-location').live('click', function () { - jQuery('#add-hotspot-notice-location').live('onLoad', function () { - jQuery('#add-hotspot-notice-location').css('width', 300); - jQuery('#add-hotspot-notice-location').css('height', 100); - }); - jQuery('#add-hotspot-notice-location').dialog({ - modal: true, - title: "REPORT LOCATION", - closeOnClick: false, - effect: 'apple', - buttons: { - "Cancel": function () { - jQuery('#add-hotspot-notice-location').dialog('close'); - return false; - }, - "Submit": function () { - var location = map.getCenter(); - var zoom = map.getZoom(); - var numTiles = 1 << map.getZoom(); - var projection = map.getProjection(); - var worldCoordinate = projection.fromLatLngToPoint(location); - var pixelCoordinate = new google.maps.Point( - worldCoordinate.x * numTiles, - worldCoordinate.y * numTiles); - var tileCoordinate = new google.maps.Point( - Math.floor(pixelCoordinate.x / 256), - Math.floor(pixelCoordinate.y / 256)); - var tile = tileURL(tileCoordinate, zoom); - // submit the data to hotspot - jQuery.ajax({ - type: "POST", - url: '/hm_web/data/hotspots-notice/process.php?callback=success', - data: { - user: user_name == "" ? "anonymous" : user_name, - add: jQuery('#hotspot-notice-location-title').val(), - body: jQuery('#hotspot-notice-location-body').val().replace(/\n/g, '
                '), - location: "[" + SliceNumber + "," + location.lat() + "," + location.lng() + "," + zoom + "," + tile + "]" - }, - dataType: 'jsonp', - context: document.body, - success: function (data) { // this is very strange, we should use success here, but it does not work... - jQuery('#add-hotspot-notice-location').dialog('close'); - // show a dialog that adding the hotspot was successful - jQuery('#add-hotspot-notice-location-success').dialog({ - modal: true, - title: "Problem reported successfully", - closeOnClick: false, - effect: 'apply', - load: true, - buttons: { - "OK": function () { jQuery('#add-hotspot-notice-location-success').dialog('close'); } - } - }); - } - }); - return false; - } - }, - load: true - }); - }); - - jQuery('#open-comparison-viewer').live('click', function () { - setTimeout("resizeComparisonViewerDiv();", 200); - jQuery('#comparison-viewer').live('onLoad', function () { - updateMap34(); - resizeComparisonViewerDiv(); - }); - jQuery('#comparison-viewer').overlay({ - closeOnClick: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.5 - } - }); - var api = jQuery('#comparison-viewer').data("overlay"); - api.onBeforeLoad = function () { - updateMap34(); - resizeComparisonViewerDiv(); - }; - api.load(); - }); - - jQuery('#open-blockface-overlay').click(function () { - jQuery('#blockface-overlay').overlay({ - closeOnClick: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.5 - } - }); - var api = jQuery('#blockface-overlay').data("overlay"); - api.onBeforeLoad = function () { - resizeBlockfaceDiv(); - }; - api.load(); - }); - - - /* based on the screen size, load minimal tab content to save initial loading time */ - LoadTabContent(2, function () { - jQuery('#open-map-overlay').click(function () { - setTimeout("resizeMapDiv2();", 200); - jQuery('#map-overlay').overlay({ - closeOnClick: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.5 - } - }); - var api = jQuery('#map-overlay').data("overlay"); - jQuery('#map-overlay').on('onLoad', function () { - updateMap2(); - resizeMapDiv2(); - map2.setCenter(map.getCenter()); - map2.setZoom(map.getZoom() + 1); - }); - api.load(); - }); - jQuery('#open-info-viewer-googlebrain').on('click', function () { - jQuery('#info-viewer-googlebrain').overlay({ - oneInstance: false, - closeOnClick: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.5 - } - }); - var api = jQuery('#info-viewer-googlebrain').data("overlay"); - api.onBeforeLoad = function () { - resizeInfoViewerGoogleBrain(); - }; - api.load(); - jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up - }); - - }); - - - jQuery('#open-info-viewer-segmentation').on('click', function () { - jQuery('#info-viewer-segmentation').overlay({ - oneInstance: false, - closeOnClick: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.5 - } - }); - var api = jQuery('#info-viewer-segmentation').data("overlay"); - api.onBeforeLoad = function () { - resizeInfoViewerSegmentation(); - }; - api.load(); - jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up - }); - - jQuery('#open-info-viewer-MRI').on('click', function () { - jQuery('#info-viewer-MRI').overlay({ - oneInstance: false, - closeOnClick: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.5 - } - }); - var api = jQuery('#info-viewer-MRI').data("overlay"); - api.onBeforeLoad = function () { - resizeInfoViewerMRI(); - }; - api.load(); - jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up - }); - - jQuery('#open-info-viewer-models').on('click', function () { - jQuery('#info-viewer-models').overlay({ - closeOnClick: false, - oneInstance: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.9 - } - }); - var api = jQuery('#info-viewer-models').data("overlay"); - api.onBeforeLoad = function () { - resizeInfoViewerModels(); - return false; - }; - api.load(); - jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up - }); - - jQuery('#open-info-viewer-blockface').on('click', function () { - jQuery('#info-viewer-blockface').overlay({ - closeOnClick: false, - oneInstance: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.5 - } - }); - var api = jQuery('#info-viewer-blockface').data("overlay"); - api.onBeforeLoad = function () { - resizeInfoViewerBlockface(); - }; - api.load(); - jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up - }); - - jQuery('#open-info-viewer-walkthrough').on('click', function () { - jQuery('#info-viewer-walkthrough').overlay({ - closeOnClick: false, - oneInstance: false, - effect: 'apple', - mask: { - color: '#fff', - loadSpeed: 200, - opacity: 0.5 - } - }); - var api = jQuery('#info-viewer-walkthrough').data("overlay"); - api.onBeforeLoad = function () { - resizeInfoViewerWalkthrough(); - }; - api.load(); - jQuery('img[src=non]').remove(); //ugly hack to remove broken image showing up - }); - - mpr1 = new mpr(0); - mpr1.scoutLineColor = "rgb(127,29,49)"; - mpr1.setVoxelSize([1, 1, 1]); - mpr1.setFlipSliceDirections([false, false, false]); - mpr1.bindWindow("#window0", [0, 1, 0]); - mpr1.bindWindow("#window1", [0, 0, 1]); // make sure that cross-hair is syncronized - mpr1.bindWindow("#window2", [1, 0, 0]); // make sure that cross-hair is syncronized - mpr1.setCacheSize(3); // speed up display by caching 1 image only - mpr1.setPatientInformation("H.M.", "March 2009", "T1", "none"); - mpr1.setDataPath('data/JPEGS/T1'); - mpr1.infoOverlayEnabled = false; - mpr1.setLockPosition(false, false, true); - mpr2 = new mpr(1); - mpr2.scoutLineColor = "rgb(127,29,49)"; - mpr2.setVoxelSize([1, 1, 1]); - mpr2.setFlipSliceDirections([false, false, false]); - //mpr2.bindWindow("#window1", [0,0,1]); - mpr2.setCacheSize(3); // speed up display by caching 1 image only - mpr2.setPatientInformation("H. M.", "2012/02/02", "T1", "none"); - mpr2.setDataPath('data/JPEGS/T1'); - mpr3 = new mpr(2); - mpr3.scoutLineColor = "rgb(127,29,49)"; - mpr3.setVoxelSize([1, 1, 1]); - mpr3.setFlipSliceDirections([false, false, false]); - //mpr3.bindWindow("#window2", [1,0,0]); - mpr3.setCacheSize(3); // speed up display by caching 1 image only - mpr3.setPatientInformation("H. M.", "2012/02/02", "T1", "none"); - mpr3.setDataPath('data/JPEGS/T1'); - - SliceNumber = "1207"; - if (typeof user_1279 !== "undefined" && user_1279 == "1") { - SliceNumber = "1279"; - } - - if (typeof slice != "undefined" && slice != "") { - SliceNumber = slice; - } - if (typeof maplocation != "undefined" && maplocation != "") { - initialMapLocation = maplocation; - } - if (typeof mapZoom != "undefined" && mapZoom != "") { - initialMapZoom = mapZoom; - } - resizeMRIDiv(); - if (typeof maplocation != "undefined" && - typeof mapZoom != "undefined" && - typeof slice != "undefined") - updateMap(SliceNumber, initialMapLocation, initialMapZoom); - else - updateMap(); - resizeMapDiv(); - $('#Blockface').attr("src", "/hm_web/data/Blockface/MEDIUM/2500_FINAL_HM_Blockface_" + SliceNumber + "_MEDIUM.jpg"); - // $('.Blockface').css('background-repeat', "no-repeat"); - $('#section').text('Section ' + SliceNumber); - $("#tabs-1 #id").css("height", $('#tabs-1').width() * .917); - $(".ui-autocomplete-input").val("Search by structure or slice number"); - /* - $('#search').live('click', searchFunc); - $('#search-icon-small').live('click', searchFunc); - $(".jcarousel-next").live('click',searchFunc); - $(".jcarousel-prev").live('click',searchFunc); - $(".ui-menu-item").live('click',searchFunc); - $("#search-box #label").live('keyup',function(){ - if($("#ui-complete-input").val()=='Search by Brain Region') - $("#ui-complete-input").val(''); - searchFunc; - }); - */ - if ($(".six.columns").width() == 268 || $(".six.columns").width() == 300) { - document.getElementById("label").size = 25; - } else if ($(".six.columns").width() == 420) { - document.getElementById("label").size = 42; - } else { - document.getElementById("label").size = 36; - } - - - //This loads the label file. - var lab = new Image(); - lab.onload = function () { - can = document.getElementById('labeldisplay').getContext('2d'); - can.drawImage(lab, 0, 0, 521, 457); - //$('#type').text('ready'); - } - - //This loads the colored labels. - var img = new Image(); - lab.src = "/hm_web/data/Segmented/grayscale_snapshot_" + SliceNumber + ".png"; - //Shows labels on hover over blockface - // $('#blockface_label').mousemove(function(e) { - // bfloc = findPosC(this); - // cX = e.clientX; - // cY = e.pageY; - // bX = bfloc[0]; - // bY = bfloc[1]; - // var mouseX = cX - bfloc[0]; - // var mouseY = cY - bfloc[1]; - // var can = document.getElementById('labeldisplay').getContext('2d'); - // var imageData = can.getImageData(mouseX, mouseY, 1, 1); - // var labelValue = imageData.data; - // $('#type').text(nameForKey(labelValue[0])[0].replace(/_/g,' ')+ ' ' + mouseX + ' ' + mouseY - // ); - // }); - jQuery('.slide').click(function () { - if (typeof user_1279 !== "undefined" && user_1279 == "1") { - alert('This functionality is only available for users with a valid account. Go to thedigitalbrainlibrary.org/hm_web to sign-up.'); - return; // do nothing if not logged in - } - - var slice = jQuery(this).attr('mapLocationSlice'); - var lat = jQuery(this).attr('mapLocationLat'); - var lng = jQuery(this).attr('mapLocationLng'); - var tab = jQuery(this).attr('tab'); - var mprlocation1 = jQuery(this).attr('mprLocation1'); - var mprlocation2 = jQuery(this).attr('mprLocation2'); - var mprlocation3 = jQuery(this).attr('mprLocation3'); - if (typeof slice === "undefined") - return; - var zoom = parseFloat(jQuery(this).attr('mapLocationZoom')); - if (SliceNumber != slice) - update(slice); - map.setCenter(new google.maps.LatLng(lat, lng)); - map.setZoom(zoom); - centerToTab(parseFloat(tab).toFixed(0)); - if (typeof mprlocation1 !== "undefined" && - typeof mprlocation2 !== "undefined" && - typeof mprlocation3 !== "undefined") { - mpr1.setPosition(parseInt(mprlocation1), - parseInt(mprlocation2), - parseInt(mprlocation3)); - } - - //jQuery(this).parent().animate({ scrollTop: jQuery(this).offset().top }, 1000); - }); - if (typeof maplocation != "undefined") // in case we have an initial map location - setTimeout("update( \"" + SliceNumber + "\", [" + initialMapLocation[0] + "," + initialMapLocation[1] + "], " + initialMapZoom + " );", 1000); - else - setTimeout("update( \"" + SliceNumber + "\" );", 1000); // display the first image - -}); - - - - - - -var lastID = 0; -function mycarousel_itemLoadCallback(carousel, state) { - for (var i = carousel.first; i <= carousel.last; i++) { - if (carousel.has(i * 2 - 1)) { - // continue; - } - - if (i > Math.ceil(current_itemList.length / 2)) { - break; - } - - //carousel.add(i, mycarousel_getItemHTML(mycarousel_itemList[i-1])); - carousel.add(i, mycarousel_getItemHTML(i * 2 - 2)) - } - jQuery('.jcarousel-item img').hover(function () { - var sr = jQuery(this).attr('src'); - // extract the number from the string - var idS = sr.split('_'); - var idSS = idS[4]; - var id = parseFloat(idSS).toFixed(0); - if (id != lastID) { - jQuery('#g-left-img').attr('src', './data/KeyFrames/line' + addZeros(id, 4) + '.png').fadeIn('fast'); - lastID = id; - } - // update the mm location in the side view - putSliceNrAsThal(id); - }); -}; - -/** - * Item html creation helper. - */ -function mycarousel_getItemHTML(I) { - //return '' + item.url + ''; - item_0 = current_itemList[I]; - itemHTML = '' + item_0.title + ''; - if (current_itemList.length - 1 > I) { - item_1 = current_itemList[I + 1]; - itemHTML += '' + item_1.title + ''; - } - return itemHTML; -}; - -function addZeros(num, size) { - var s = num + ""; - while (s.length < size) s = "0" + s; - return s; -} - -jQuery(document).ready(function () { - load_carousel(mycarousel_itemList); -}); - -var current_itemList; -var j_scroll; - -function load_carousel(itemList) { - if (itemList.length == 0) { - itemList = mycarousel_itemList; - } - current_itemList = itemList; - j_scroll = Math.floor((parseInt($("#g-right").css("width")) - 28) / 102); - jQuery('#mycarousel').jcarousel({ - size: Math.ceil(itemList.length / 2), - scroll: j_scroll, - vertical: false, - rows: 2, - itemLoadCallback: { onBeforeAnimation: mycarousel_itemLoadCallback }, - }); - if (current_itemList.length != mycarousel_itemList.length) { - $("#mycarousel img").addClass("found"); - } - else { - // jQuery('#mycarousel').jcarousel('scroll',1); - } -}; -function newPopup(url) { - popupWindow = window.open( - url, 'popUpWindow', 'height=740,width=800,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=yes,menubar=no,location=no,directories=no,status=yes') -}; - From 864edd254f26fc7a4a52e12743a6ff10fccbd135 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sat, 15 Apr 2017 12:17:19 +0100 Subject: [PATCH 084/339] add opensourcedragon viewer, refactor and clean up google viewer, improve dicom viewer, bug fixing --- .../webapp/js/components/ComponentFactory.js | 3 +- .../bigImageViewer/BigImageViewer.css | 3 ++ .../bigImageViewer/BigImageViewer.js | 35 ++++++++++++++ .../images/fullpage_grouphover.png | Bin 0 -> 4907 bytes .../bigImageViewer/images/fullpage_hover.png | Bin 0 -> 5214 bytes .../images/fullpage_pressed.png | Bin 0 -> 5213 bytes .../bigImageViewer/images/fullpage_rest.png | Bin 0 -> 5155 bytes .../bigImageViewer/images/home_grouphover.png | Bin 0 -> 4808 bytes .../bigImageViewer/images/home_hover.png | Bin 0 -> 5107 bytes .../bigImageViewer/images/home_pressed.png | Bin 0 -> 5138 bytes .../bigImageViewer/images/home_rest.png | Bin 0 -> 5061 bytes .../bigImageViewer/images/next_grouphover.png | Bin 0 -> 3004 bytes .../bigImageViewer/images/next_hover.png | Bin 0 -> 3433 bytes .../bigImageViewer/images/next_pressed.png | Bin 0 -> 3503 bytes .../bigImageViewer/images/next_rest.png | Bin 0 -> 3061 bytes .../images/previous_grouphover.png | Bin 0 -> 2987 bytes .../bigImageViewer/images/previous_hover.png | Bin 0 -> 3461 bytes .../images/previous_pressed.png | Bin 0 -> 3499 bytes .../bigImageViewer/images/previous_rest.png | Bin 0 -> 3064 bytes .../images/rotateleft_grouphover.png | Bin 0 -> 1902 bytes .../images/rotateleft_hover.png | Bin 0 -> 2289 bytes .../images/rotateleft_pressed.png | Bin 0 -> 2242 bytes .../bigImageViewer/images/rotateleft_rest.png | Bin 0 -> 1943 bytes .../images/rotateright_grouphover.png | Bin 0 -> 1970 bytes .../images/rotateright_hover.png | Bin 0 -> 2338 bytes .../images/rotateright_pressed.png | Bin 0 -> 2228 bytes .../images/rotateright_rest.png | Bin 0 -> 1961 bytes .../images/zoomin_grouphover.png | Bin 0 -> 4794 bytes .../bigImageViewer/images/zoomin_hover.png | Bin 0 -> 5126 bytes .../bigImageViewer/images/zoomin_pressed.png | Bin 0 -> 5172 bytes .../bigImageViewer/images/zoomin_rest.png | Bin 0 -> 5041 bytes .../images/zoomout_grouphover.png | Bin 0 -> 4596 bytes .../bigImageViewer/images/zoomout_hover.png | Bin 0 -> 4931 bytes .../bigImageViewer/images/zoomout_pressed.png | Bin 0 -> 5007 bytes .../bigImageViewer/images/zoomout_rest.png | Bin 0 -> 4811 bytes .../interface/dicomViewer/DicomViewer.js | 4 +- .../GoogleViewer.css} | 2 +- .../GoogleViewer.js} | 44 +++++++++--------- src/main/webapp/package.json | 1 + src/main/webapp/webpack.config.js | 2 +- 40 files changed, 67 insertions(+), 27 deletions(-) create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.css create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_grouphover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_hover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_pressed.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_rest.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/home_grouphover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/home_hover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/home_pressed.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/home_rest.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/next_grouphover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/next_hover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/next_pressed.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/next_rest.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/previous_grouphover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/previous_hover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/previous_pressed.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/previous_rest.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_grouphover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_hover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_pressed.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_rest.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_grouphover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_hover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_pressed.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_rest.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_grouphover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_hover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_pressed.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_rest.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_grouphover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_hover.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_pressed.png create mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_rest.png rename src/main/webapp/js/components/interface/{highResViewer/HighResViewer.css => googleViewer/GoogleViewer.css} (52%) rename src/main/webapp/js/components/interface/{highResViewer/HighResViewer.js => googleViewer/GoogleViewer.js} (73%) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 5c8477bc5..df3157543 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -66,7 +66,8 @@ define(function (require) { 'TEXTFIELD': 'controls/TextField', 'RAISEDBUTTON': 'controls/RaisedButton', 'DICOMVIEWER': 'interface/dicomViewer/DicomViewer', - 'HIGHRESVIEWER': 'interface/highResViewer/HighResViewer' + 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', + 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer' //'WIDGETCONTAINER': 'widgets/WidgetContainer' } diff --git a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.css b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.css new file mode 100644 index 000000000..71a52af9d --- /dev/null +++ b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.css @@ -0,0 +1,3 @@ +.bigImageViewer { + height: 100%; +} \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js new file mode 100644 index 000000000..9636595a1 --- /dev/null +++ b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js @@ -0,0 +1,35 @@ +define(function (require) { + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/interface/bigImageViewer/BigImageViewer.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + var React = require('react'); + + var OpenSeaDragon = require('openseadragon'); + var bigImageViewerComponent = React.createClass({ + + shouldComponentUpdate() { + return false; + }, + + componentDidMount: function () { + var viewer = OpenSeadragon({ + id: this.props.id + "_component", + // FIXME: I have copied the images inside the images component folder. More info https://github.com/openseadragon/openseadragon/issues/792 + prefixUrl: "geppetto/js/components/interface/bigImageViewer/images/", + tileSources: this.props.file + }) + }, + + render: function () { + return ( +
                +
                + ) + } + }); + return bigImageViewerComponent; +}); diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_grouphover.png new file mode 100644 index 0000000000000000000000000000000000000000..3ca4e1e3c366d44e9dbbde415f6addaef941b129 GIT binary patch literal 4907 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 R`k?>-002ovPDHLkV1ijn#q9t9 literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/home_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/home_grouphover.png new file mode 100644 index 0000000000000000000000000000000000000000..204e1cc94b5057e04a42f0d4483ba0c96c05e7cc GIT binary patch literal 4808 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 LNkvXXu0mjf%T$KU literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/next_pressed.png b/src/main/webapp/js/components/interface/bigImageViewer/images/next_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..95f169d65150b1e5162585bd0695e4617363afbe GIT binary patch literal 3503 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 literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/next_rest.png b/src/main/webapp/js/components/interface/bigImageViewer/images/next_rest.png new file mode 100644 index 0000000000000000000000000000000000000000..5ead544b5d7ef6de16e83f9d28aee515666fcb89 GIT binary patch literal 3061 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 DA#uj9 literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/previous_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/previous_grouphover.png new file mode 100644 index 0000000000000000000000000000000000000000..016e63957a87778ba2562ec7a91dee22248590d8 GIT binary patch literal 2987 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 literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/previous_hover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/previous_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..d4a5c1552c7a8a5e7ddae0d2a94f235e00e44c62 GIT binary patch literal 3461 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 Z{{Y0&G1asaZ{h#|002ovPDHLkV1h%en=b$W literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/previous_rest.png b/src/main/webapp/js/components/interface/bigImageViewer/images/previous_rest.png new file mode 100644 index 0000000000000000000000000000000000000000..9716dac67b7ccd4c68b427eeca7de3548eed0726 GIT binary patch literal 3064 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

                l|+AWN>*zn_}R11DNdj#<;>XLvZ=<+(RxFLRJE4$G7V^dP5{o zo)P{834@hzSQ?S#-Z#X7&`FHl zAq^W5j(&1`QmnbL49>9-_>cL(=Q2tMBtZL+9TE(B%1XXFZkohmHMody<4)&y4 zG+(V@GFM06EcGm2$xTyLA`uT23zW!cAqNxwfr=T&=+-U=(4-E`!hj79IO@XOIIA03 z*5)8M7ed%UvJfIf%n0To6HioZT)cGYR6`XOt-@4qjDGOMuBV-1QCxJn61wRoMP?b5 z?xDVSh$nla5(jZjemNxxnI|_W7qH@iRU}nIpI@7Uh`&E)2*wO!0*Dw(mnfG!PJO%E z_dNHfU{CTIw}fVxdWRM*SjppsBGHyDr&QAjQ8=b;HYifmWI$9Gh$3Y=K-(O^rVDSM zHX&qq@Wb0v0P=-p`Vw6xtfCTa`6tNnVv&S%*=$gGr&Ql6Owda-wu?Toeg4FWpQmMU zd!u5L&>goSAA}|xS%jn_5+KTe#w3)K4{~-~gVdw~yY3r-`Lgmt4yqpWKM#Lp101T@ zD3!P1%|nNt37V{Oa~sj@nOfg!KlbW@zs<{KPwyWcgDVLWj9b?OiTWTC!1Mv6!t%#- zuj+vnp)wr#>AUbrRsnuhCaf((sJo!wvL3EPqu|?V&!jHwJ#zF|Q8{t!vRp@p{;lS^&i)YZClsOhi|QI z2e^JxF1de72j?VM;MD5bK!PYyK#5NQ>RbiSA5FsxN5){)yio4Z7Y6PNI=xKLVC91b ztJV|G-04Y0e4y?9n|2=_F0L>Q3jKWxVe@CZAVTCrvHVBXQjI{~3&UUD8HasujmWy= z!kPv8ghtp@27@D6 zh!_fdW>GXZnMyxBaD4b^0Fs+^4?GJXGYFdxW`6(9CEs=hSuH-SS@n`mz&rW-14Azc z?k@+)YW>HjgPG1v0aQ4+8o_1JMc!W`d}Ve+e)Eg^rcg5`|G!00000 LNkvXXu0mjfWXDWt literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_pressed.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..b968ebf8ca3bff215d92ac0b41af76e317cff80b GIT binary patch literal 2242 zcmV;z2tD_SP)!e1yPbVK{QQzl@Ps9(LNLjQYopb zMpfxUC~7PE&^|*cB8)LqRPNW84-nhJbC>-u2phnfpxt**)uy zm$6OTkw2gH%%1;z{?C8;&Ty{lVzG;(%l~H;;2SHKTZPI!xC<=EEk?#i#2b0v=tngf zhb``nHXEJA2#NPVg!2&)*qjOk20tM`36o&4(PnTxvq6N!dmvIF5n_-C;e!VcezLv2 zeUok50@gng}8|ohu z1R<8sWX8?h3709wO1VdwO0@Ha0$e{n(qM6Tf|>MGlKl>gyrJq7b7J zLNN)pro$}i(58#Xr8Ce?3-w>ymFoEX7n*L|xN&^@_U+#zTUkPGK0>o(d|Bfs66+x% zb+w7dhkks(Foy;b$z@BRG&g_`(awZih>@wqwT*kI)qJQZ*q|li_?Z66-p2=sO*q_kVZjCofE6crdYabqC~zFv2x1LQw~T z>>{kVD2N>RA_sLuKyg%nt$V>qt7sbTT)hf@>~HOw>MXAhSZiIVgDPQWIW0wG>)}hY)tb z3BFX7Wi)qm!20Lq_|@(=Ce}an&{wu@-MWe%#l1LW*;y$K}y?(TA^U={OZo#^jPs?k%fX-sbzdsnXl9nskXNE(VNGP79!y=75wW) z2vl3OR>J0D#5g|g3T}D(< zVEn1S*(0%Q*RGFw-e~ZQ>i;np-6EMgF$5Sw_R*mkwIOJc9&;7 zfI#X$tb9~PeUlKlR+Xx&s-uLjzr-Ui%5@+rB|+8xt@8&A2WB6DG@RZgV(ZI z@bs6hQtg=1P`FKX{TlGuhf}n;H80-(hm2bk%#lW&QlH2;bYIs?@a&OY&a>}pFS7w0 z)SEcdG-0_;DKE5i8akCKZPJ06v(UOp$MU)n2ig!3Fh}^7@u!soR=K`867uYC?IdnH<&x#ZJB+%}1G#VKWd!87_z_lp8_;?yS zHcvvecpUk25^wY+OLK3H(|_4gM4~MOs}MplB|^`aH$k^dhrSb}i2^<2dIk+i^0E{H zRarSQ(G~nuAHr%ruNZrN9TMGv~^cD`(wMs7Z)Rkn1RA8Dt_PkY$U!vo2DAFzM1~imM!L z1@-0TI&;?8{YzAck^@<>5%J~{q-GJkB-alP4qo=@9yf?gMWNY;4<9}`Gc!}nu3Iz8 z*LZcpX5O(8q88wJ%%#%eYHleg1<@&aFt3))eQNU*B+~)!P`jYEA=S`x#lpl#*Gx=L zPiuYs{TEqJvVDYHp0#hcTA_`PkDt!1SgjyxQU7sDki0SG5FrPm*CeU@d!I}9A`agB zdktby8#}kA;F70!zDy<@qwS^pGc6B9se=zNO=I}Q)@*O8l8YW=TVz8zuNgvLU*GSU z$Wp6i#C&A*3l}e5rPz7(I=`Z61j(Gv(-x88^5O)^Ey3F!Z>!pCHN5-70knlRIJvSV zuW?d^!8NO~FoL#*fq{V@^33%rk!8;5+EoE;u`d;SdwX9V9UVRXc6xpI@=QanKvIsQ z#l1yl;WbBZ8fBS7OL*JkSs*Qng5_W!+fX<*zCJ`G&Yd`M!t=^B6IcbNkIjYjj8IW{ zgc?HK#*G_)8jVIDd$94=#Md7gZd76}qH;Oy=X5qQMMW4zDslzVH^2Eyd-`H(c|$Ij zJ4@HknbOPn^i090i5FFRvxGW3J9o$9@h^+KZEfkCP`|WkvME79S<=8PJ8g%4_9pY5My_dHT+<*CghFjnrji1bX?`ZzN_cM#UG+O9#tK)Dn`~MbT0PZ!H(dGzG Q-2eap07*qoM6N<$f~^ohNdN!< literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_rest.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_rest.png new file mode 100644 index 0000000000000000000000000000000000000000..ebbf081b8aa0a038f4ecdd594087150cba1e8ea8 GIT binary patch literal 1943 zcmV;I2Wa?-P)E zK~#9!%$Q3|6j>C8tE#IXG&Io7t3ePD1d_qmBm#zDT+G5za3*o%XpGU!Bsv>i>fpkS zChAf*CPv)h!oFMdQrlzKTHhF|ulzpn9 zz5vpN(E%X<k_Hf`U&{X3)4n3tZO?o3Th%@PDbRK`W4(a7@h@|@S}bp?aL=_gN~ zbeuhVb_}<|DvKrp`T|A=geEig_VykGz_(dhS(R3+)eZ=WUA$_nY=GkZe*ePU+}v9z z^;bhf!ymXEgoPMTL=R{cOd^oJzP_Jxa&mS8q?{lynM9$1@oWsQIK`h)gTcU);W=S` zety{Ha`n~J)ZD=%fwh3*FbP6q0?01~1qHh^GBOHiOgJ2-F$R9+5?~^Q3ctlLc?f_s z8Bt0IC*9{g9?!(bj~_d0YikL}&wxVOkO_LZ%YHX@cXuDm%gfsZkU|28L?UKPY!yY( zhKcOV9B#Ke2RUMg8fF?x3nbM-p%4K|2=4}5wNIZu{T}x%v`F%=DG{9E!i5W3Rpey7 zapOkW=FOWMolfT#S~;AjV6rpS$V;%@5!ZV4{lR z1cPy$*=)Asd)Q{PO>}m4E^4;A{IHln6>;m5M5=6(at2ts2bfM?Iz9HXP7#?jG# zRw?Rq=1axJB@w7YP9p%1#FG;1L^5+JDJcT{Rqx-w|I_8mmnT$PJ{~Gj2ZWR{q@|^8 zLI@0ih>%0<3NyB0X%=MImA+XSa(iZP@oKR%cj61#B^lSDbK`FuY9=FOX_ zw{N!;L4kBE#7OF3Az`8+g{dgwo&^irz!VYG^Xl#7F<+K$q#`1`0hp+W)B?r z!H3_tMAp;OW9skkbK+Sml@0)qI>ZzP@ErV3l7H0^6OW9T2xekp!i7BeG%_+0&CJZS z)8%kDJk`}(r1J9einnivM2IKl=H_tY=>GhzXY$Tio}(vk<&*jrXs zCZG%$0V!&tkr0Msg-t#Hu_}IQYRWS+GqVI3FH{O1K72$te*9QvV`G#5I7#aXmN;=gFIRu!%k9sjxSXh{P z?AUQjU0vM*O8pEa>#0+}<|7PMZEb(jPT_`zhBAtZip(5RaMn2;5dZ9he<>{XM}QLwr#n}%1RIJ2cZIue1LjiEo#fuk*w7rdXYpY9h497Kr5$EKuJSyZ;u(3RyuU(r*SBN+?`$oc$f}R0Q1Ac!&KG++RCe#py{@ho;C9N z_3I7_!{Fecfp!7q45>q(q!6u;R=mz3x`<{v+Q{#>wk?_TOrBq&AUl22sTY6?{1(d(q; z=jS_65Uo_$Xa^xL+_alYN=m5r;U7PKEa17BD_5?xVcasa0BgErC@oC<)Mh{_r%#_g z0=ZN7?b{ban<>3{^G2fGrnCv93X)U6Jrj1Cflg_t7NqK1QBe^iwH`fsBp^)w>({S8 zVEv3@K~*cN+g!yPD@CCbCr%t08ymCM*VjvEkYWe{hg~I~#-tEyvg8vQIfGN&$)SId zUc7k0cXf5~DF6PJmX-%h3BO7ab%Vtv?ev(X96frp@xzA?d6Ecs+U=3$+ATy{v?C5g4{F zhXm#T5x|b>N838iR0yoQ2VN(TSy5}xo;}+zc7x8Vj;o`i<8QX=uu6sh_I8l$y-CLm z+LKpR9-^A}rKImox@Qx6<=|D0SJfcqRMcw&RL}iCZt1^~`&HS5vIP<8+S002ovPDHLkV1j};jw1j7 literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_grouphover.png new file mode 100644 index 0000000000000000000000000000000000000000..86e8689c14b1ef1af96adc7aa3ba9531113a31c4 GIT binary patch literal 1970 zcmV;j2Tk~iP)I8Zm`N5E0RZkMfEy<$fpYu+`O4%0LV%NCDOavsIkacbp6~FuC@n3Gr>Cboob%W?+qRA6}=2e{rgYi@ggjwK!yDPZ2%JoNa}+J4|*~)GfN5z3T)FfZR}cL z!M9|m-!ui@;g)3)C=;@A02-^PsQ3`C%)^%A0g8dqqDBXj*4Ni}FE1~z2u7}Uc7km{qpkiFAEC`$@61C$VX`B1c+Ffo0~J6nwt2dN00cz!UBLgJS!`U2LeeF zFTGC;rlh1809S-(gLm%S8C8r#-Efy7uXJ|7SPF3lSm`M(EiHtzM&OLZ4AXrK3$Wzm zWF8C#d1K=bd}3mJ1I4AKWw26FSy?5WJrD>`q*+5lLsQk&)gR-PX(d@m$z}665@DU> zLeTE4uC6-RkqldMTo~mPxdAZ4!r?UrDe2h;8y+4qZr^Taa9+6q_?n>kz)(K47yiHh!uUNYcSr9%f{=MHnB#Vh)8b?TGxt)gEk6l(1{pTFw6ceiko- z=j3D?wY9Z^fKUX|Twq0DUQt|JT!B|EDHY6kU?d=tWqy9%AV97x%E=fag%cz9@AtCJ z>RMgh2Z$u$P&TIdXpW_&B?k^qOf#*#xL|y16T@IOJv~i~L`FtNBJdvz>EDEJm?!~8 zYwNF^WYDkT;v!>eYFf&@h*NdY^k2+~->BiNoKlAI?+gL-xX&X@!ee>px4Vh%B zCf_7>$nR)=pPtB<89^@-m6W_>wnKiQoI=qvGBO++TvJmEAfMP^(V3Zrs$j}Yt^AE4AP5Xg)X%tGnKLyt)q#z?16rxZa6|$l-ysQ*KVk(5nUxBm zc>Ve{g}i{2qM%^Ac=__B0E1M&WLOgrlItBm*>-K#{+Vj*glXT}TJNY1!8WB)F<- z!rP&Nk;#F9fdF=IlH?>E?G{y7969olpq=*i-gE0KBUu58%pfM|H%UZ5=t6BW2L}gD z{5hw3nsvR|l3+YCISTRm+y+BBP_DU()UrLEI6c9R#s+`BxnZ& zB875?-O%t=)HE%J{6+U|LptVpak)(WMeHQZD;GGV#! zpFbc?eqCK%6b@=*hcZ$jqoEj){zMDA&I%LseE06%)bX(!H*Q#vd+x%83!h=mShf1V3gSW!`t z%pdMWz!1K&kphT$1i(*Vr$@1{pe%6@yahn%iX{S;e(>PI;~4v%+k$FWUuS3MFN*$E z#Xv}b2>C^k@97}HxhbeXrKx*R>qqp{r0qGtt!ihcwav~=zJ*d1HbV>b4Pmb)PxiI| z+L=0{0b18Wcteug(up$8S=-dwQp?*I`JA(Dt?7O%_4}Q*|I1eh?g7DlK8H9S6I=NT z!T#m<8Metc8XZi$Z#4h!`%Hq5Mw?y2>xYTv|3`oU0EVS7rgVc{q5uE@07*qoM6N<$ Eg7A5stN;K2 literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_hover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..d22a728fe9b973f28df270e165fe57ee10353d1a GIT binary patch literal 2338 zcmV+-3ElRIP)Qpe82uIC>DzuLI~}^p64D* zFI;q&`W;Kvf8)9F;gR>h{M1wX)Z^TB-MacHkQ1r_C0YTs9Hu6a09aEpUf#X?jx`%M z{z#@$%YYC{9H)k)5GoeaFcM?z>$(ZPx^lVUzr6XzkH7cJ=Z~v@Rh4Q=wvro0ON1jP zXN742qAQsn9C-cLWZ|Ow2|~gum$O>ARQAe6&TG}_4utwiV-_XxIOB;_qMl5)sSuFT zg`vZDZ`-~@-BMDjgiuZ^pzB~}0g0VDbm$$?*0w5ME>D=bOwOK~;&wjk`=W+1lfe)o z;UVd}APCct-kzprdLDDqoo#ko`#e80b@BGkJ+MvPS5PX?1~dZ`$(ksL^^Zeuzax{W zeY*U_)@VrI_l|N3oI=e*O&>K z(dp{(Rt5lqX;Y->4z=zzJKR>CdY$60{lnSONT&=Ei^Uo*Z=A}uh z)rrt?oNH~JoLOx?GgDIMrOQ6?JRgy`KwXPhrX1(ovHt$6VMM$JNTBA_#$gH@Yc_5C zp?X(gj*ex<-ha=r7B5L?nCRH^7y%z+>JuUyJiomrtr(X10z>&CM*X0xK6KidcJy?d zi`C1RR_QyHHD#(?F*=f8*t+%G&+dKgbR^{)>5gbqnQzn+^}exNQu*9e<=k0cLnx*` zG_d0+5KjdLh^9MCk2}o6oW^tnnMN@}<4K>(y<0!%-l!!At{ru1l!nNLeTn;<_3x7Af*Q57a%oj|e#`F|7#(l?oP4U3xB`SzaPAE!QUGaSsFMh)BUS)iV~J zMg?b@!I`nZW-t6<5y|JT2U*tR!1yGCdL6^MPgdo6HYLi(OerofjH~J;ghW*M%v1gP z9>*;fS=4f&^tp?L>mniqQ|JgIsp-0)Mwi3tDTiK}f-oxqzI0CxzHwhxCN%*s{56eU z|62#_d|*s^CryyU6ypk}nkSJ;7gZa9s;H}KoTSMPAncH)!;={ zbmsCi4Ob>|W1~lE+OjpmvNV=R`nX;pgkuU-j8}EkXCCNk;~*v|_!k+Hbp)|C0e7oK zoR1qIuc#`(38q4=p^AiBaV)F^ny5RtIyiVH;(KJY$Td4O|M8zc_bazrmFGRdtd%P@ z6<&t9it+@*i2_3Rt}jmCT&@zp)Kl^I)I{?^s^OE9s#8}0(LJELhk}}|?)00jg~Y!CK4F$|dkgZ?Y}G!RKDkQ4`+xF(WPfa;k5H1^<~ zaKx>nOsNb<4j+CgG+B1DjhGF_dFHKy?=C16&%E)`7+mVGz`SzS)d?FffSU!aT8olB;mmDIHdELf!B^5Khb2e_>4CWMk(TD z8iqSqXp%}DPk-%^Ll-72C@;ht)l8kPOU4c$I1#&W#ixVKP|T zn=BOaxfkC&e)&Wgg<`0=J@8Ec#lt8CVDYctzx+{8l6CUh5Y24hi1bhY=Ix>1hvB!w z2vkCdN~1ztvv*e>sJ~O%S-nafoP= zyqQx7^k1IOpc|agL}6w-qxt{NXXZFGy3r+@=iyxa|670o01E4_WuV+H9{>OV07*qo IM6N<$g4cO(DgXcg literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_pressed.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..fc2ead64e506a0639e5af55332d2afdb74e4fe4e GIT binary patch literal 2228 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 U<@`%&ga7~l07*qoM6N<$f_Nk7;{X5v literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_hover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..3cab721f1bd5b0360dd1eac094c36a409b517738 GIT binary patch literal 5126 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 Hu0mjf@M4@K literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_grouphover.png new file mode 100644 index 0000000000000000000000000000000000000000..46d21b3e507e7363b4e8afd0e77343814ad63748 GIT binary patch literal 4596 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 literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index f5adfcb1b..0e7c6397b 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -66,7 +66,7 @@ define(function (require) { } window.addEventListener('resize', onWindowResize, false); - $("#" + this.props.containerid).on("dialogresizestop", function (event, ui) { + $("#" + this.props.id).on("dialogresizestop", function (event, ui) { camera.canvas = { width: ui.size.width - 260 - 30, height: ui.size.height - 30, @@ -238,7 +238,7 @@ define(function (require) { render: function () { return ( -

                +
                diff --git a/src/main/webapp/js/components/interface/highResViewer/HighResViewer.css b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.css similarity index 52% rename from src/main/webapp/js/components/interface/highResViewer/HighResViewer.css rename to src/main/webapp/js/components/interface/googleViewer/GoogleViewer.css index a35e97dd7..55ca93d3b 100644 --- a/src/main/webapp/js/components/interface/highResViewer/HighResViewer.css +++ b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.css @@ -1,3 +1,3 @@ -#highResViewer { +.googleViewer { height: 100%; } \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js similarity index 73% rename from src/main/webapp/js/components/interface/highResViewer/HighResViewer.js rename to src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js index dc0307073..c3ae9ccd9 100644 --- a/src/main/webapp/js/components/interface/highResViewer/HighResViewer.js +++ b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js @@ -3,32 +3,32 @@ define(function (require) { var link = document.createElement("link"); link.type = "text/css"; link.rel = "stylesheet"; - link.href = "geppetto/js/components/interface/highResViewer/HighResViewer.css"; + link.href = "geppetto/js/components/interface/googleViewer/GoogleViewer.css"; document.getElementsByTagName("head")[0].appendChild(link); var React = require('react'); - - // var GoogleMapLib = require('react-google-maps'); - // var GoogleMap = GoogleMapLib.GoogleMap; - var GoogleMapsLoader = require('google-maps'); // only for common js environments - - + var GoogleMapsLoader = require('google-maps'); + var googleViewerComponent = React.createClass({ - var highResViewerComponent = React.createClass({ - - + // getInitialState: function () { + // return {} + // }, + shouldComponentUpdate() { + return false; + }, componentDidMount: function () { - GoogleMapsLoader.KEY = 'AIzaSyAtAf8S4uU54ZogtLqbzc8pvQI6phGDL1Q'; + GoogleMapsLoader.KEY = 'AIzaSyAtAf8S4uU54ZogtLqbzc8pvQI6phGDL1Q'; + + var _this = this; GoogleMapsLoader.load(function(google) { - // var current_slice_path = 'https://s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/HM/Slice_' + addZeros(id, 4) + '/Result'; - var current_slice_path = 'https://s3-us-west-1.amazonaws.com/test-patient-hm/GoogleBrain/Subjects/HM/Slice_1207/Result'; - - var map = new google.maps.Map(document.getElementById('highResViewer'), { + var container = document.getElementById(_this.props.id + "_component"); + + var map = new google.maps.Map(container, { center: {lat: 0, lng: 0}, zoom: 1, streetViewControl: false, @@ -40,7 +40,7 @@ define(function (require) { var centreLat = 66.70383915858723; var centreLon = -48.1640625; - var hmMapType = new google.maps.ImageMapType({ + var imageMapType = new google.maps.ImageMapType({ // Normalizes the coords that tiles repeat across the x axis (horizontally) // like the standard Google map tiles. getNormalizedCoord: function(coord, zoom) { @@ -104,7 +104,7 @@ define(function (require) { } } tmp += f; - return current_slice_path + "/" + tmp + ".jpg"; + return _this.props.path + "/" + tmp + ".jpg"; }, center: new google.maps.LatLng(50, 50), tileSize: new google.maps.Size(256, 256), @@ -115,22 +115,22 @@ define(function (require) { // name: 'HM' }); - map.mapTypes.set('hm', hmMapType); - map.setMapTypeId('hm'); + map.mapTypes.set('imageMapType', imageMapType); + map.setMapTypeId('imageMapType'); }); GoogleMapsLoader.onLoad(function(google) { - console.log('I just loaded google maps api'); + //console.log('I just loaded google maps api'); }); }, render: function () { return ( -
                +
                ) } }); - return highResViewerComponent; + return googleViewerComponent; }); diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 1d1139184..70f138e91 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -54,6 +54,7 @@ "less": "^2.7.2", "less-loader": "^2.2.3", "mathjs": "^3.5.3", + "openseadragon": "^2.2.1", "pako": "^1.0.3", "pixi.js": "4.2.3", "plotly.js": "^1.20.5", diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index b8ea835fc..38d3ad738 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -149,7 +149,7 @@ module.exports = { loader: "json-loader" }, { - test: /\.(py|png|svg|gif|css|jpg|md|hbs|dcm|gz)$/, + test: /\.(py|png|jpeg|svg|gif|css|jpg|md|hbs|dcm|gz|xmi|dzi)$/, loader: 'ignore-loader' }, { test: /\.css$/, From 561b8ec63cca3d7768d3ca6cfa4efecd86f35af9 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sat, 15 Apr 2017 14:57:43 +0100 Subject: [PATCH 085/339] add toolbar and navigator --- .../components/interface/bigImageViewer/BigImageViewer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js index 9636595a1..52a93b542 100644 --- a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js +++ b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js @@ -20,13 +20,17 @@ define(function (require) { id: this.props.id + "_component", // FIXME: I have copied the images inside the images component folder. More info https://github.com/openseadragon/openseadragon/issues/792 prefixUrl: "geppetto/js/components/interface/bigImageViewer/images/", - tileSources: this.props.file + tileSources: this.props.file, + toolbar: "toolbarDiv", + showNavigator: this.props.showNavigator }) }, render: function () { return (
                +
                +
                ) } From 6c67cd5be18319b1c3e0aceb6d0cfe430095db1a Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 16 Apr 2017 00:56:54 +0100 Subject: [PATCH 086/339] adding carousel component --- .../webapp/js/components/ComponentFactory.js | 3 +- .../interface/carousel/Carousel.css | 0 .../components/interface/carousel/Carousel.js | 42 +++++++++++++++++++ src/main/webapp/package.json | 1 + 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/main/webapp/js/components/interface/carousel/Carousel.css create mode 100644 src/main/webapp/js/components/interface/carousel/Carousel.js diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index df3157543..45cbfadc4 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -67,7 +67,8 @@ define(function (require) { 'RAISEDBUTTON': 'controls/RaisedButton', 'DICOMVIEWER': 'interface/dicomViewer/DicomViewer', 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', - 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer' + 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer', + 'CAROUSEL': 'interface/carousel/Carousel' //'WIDGETCONTAINER': 'widgets/WidgetContainer' } diff --git a/src/main/webapp/js/components/interface/carousel/Carousel.css b/src/main/webapp/js/components/interface/carousel/Carousel.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/webapp/js/components/interface/carousel/Carousel.js b/src/main/webapp/js/components/interface/carousel/Carousel.js new file mode 100644 index 000000000..f793ae05e --- /dev/null +++ b/src/main/webapp/js/components/interface/carousel/Carousel.js @@ -0,0 +1,42 @@ +define(function (require) { + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/interface/bigImageViewer/Carousel.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + var React = require('react'); + + var Slider = require('react-slick'); + var carouselComponent = React.createClass({ + + getInitialState: function() { + var settings = { + infinite: true, + speed: 500, + slidesToShow: 1, + slidesToScroll: 1 + }; + + return { + settings: $.extend(settings, this.props.settings), + files: this.props.files + }; + }, + + + render: function () { + var items = this.state.files.map(function (path) { + return (
                ); + }); + + return ( + + {items} + + ) + } + }); + return carouselComponent; +}); diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 70f138e91..a5849e7ae 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -63,6 +63,7 @@ "react": "15.4.1", "react-dom": "15.4.1", "react-simpletabs": "^0.7.0", + "react-slick": "^0.14.8", "remarkable": "^1.7.1", "slick-carousel": "^1.6.0", "stats.js": "^0.17.0", From 4f2391dcb25887cbc99be2e4cd906932f64de5db Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 16 Apr 2017 01:21:43 +0100 Subject: [PATCH 087/339] remove css for now --- .../js/components/interface/carousel/Carousel.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/js/components/interface/carousel/Carousel.js b/src/main/webapp/js/components/interface/carousel/Carousel.js index f793ae05e..c8e5cc8fc 100644 --- a/src/main/webapp/js/components/interface/carousel/Carousel.js +++ b/src/main/webapp/js/components/interface/carousel/Carousel.js @@ -1,10 +1,10 @@ define(function (require) { - var link = document.createElement("link"); - link.type = "text/css"; - link.rel = "stylesheet"; - link.href = "geppetto/js/components/interface/bigImageViewer/Carousel.css"; - document.getElementsByTagName("head")[0].appendChild(link); + // var link = document.createElement("link"); + // link.type = "text/css"; + // link.rel = "stylesheet"; + // link.href = "geppetto/js/components/interface/carousel/Carousel.css"; + // document.getElementsByTagName("head")[0].appendChild(link); var React = require('react'); From 92c5e3da38a508307fe64e6f0422355f73d4dca1 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 16 Apr 2017 03:25:23 +0100 Subject: [PATCH 088/339] refactor google viewer removing hardcodes and passing parameters --- .../interface/googleViewer/GoogleViewer.js | 158 +++++++----------- 1 file changed, 64 insertions(+), 94 deletions(-) diff --git a/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js index c3ae9ccd9..90192593e 100644 --- a/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js +++ b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js @@ -11,116 +11,86 @@ define(function (require) { var GoogleMapsLoader = require('google-maps'); var googleViewerComponent = React.createClass({ - // getInitialState: function () { - // return {} - // }, + getInitialState: function () { + var _this = this; + var mapSettings = { + center: { lat: 0, lng: 0 }, + zoom: 1, + streetViewControl: false, + mapTypeControl: false + } + + var imageMapTypeSettings = { + getTileUrl: function (coord, zoom) { + var normalizedCoord = _this.getNormalizedCoord(coord, zoom); + if (!normalizedCoord) { + return null; + } + var bound = Math.pow(2, zoom); + return _this.state.path + + '/' + zoom + '/' + normalizedCoord.x + '/' + + (bound - normalizedCoord.y - 1) + '.jpg'; + }, + isPng: false, + maxZoom: 11, + minZoom: 0, + radius: 1738000 + } + + return { + mapSettings: $.extend(mapSettings, this.props.mapSettings), + imageMapTypeSettings: $.extend(imageMapTypeSettings, this.props.imageMapTypeSettings), + tileWidth: (this.props.tileWidth != undefined)?this.props.tileWidth:256 , + tileHeight: (this.props.tileHeight != undefined)?this.props.tileHeight:256, + path: this.props.path + }; + }, shouldComponentUpdate() { return false; }, - componentDidMount: function () { - GoogleMapsLoader.KEY = 'AIzaSyAtAf8S4uU54ZogtLqbzc8pvQI6phGDL1Q'; + // Normalizes the coords that tiles repeat across the x axis (horizontally) + // like the standard Google map tiles. + getNormalizedCoord: function (coord, zoom) { + var y = coord.y; + var x = coord.x; - var _this = this; + // tile range in one direction range is dependent on zoom level + // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc + var tileRange = 1 << zoom; - GoogleMapsLoader.load(function(google) { + // don't repeat across y-axis (vertically) + if (y < 0 || y >= tileRange) { + return null; + } + // repeat across x-axis + if (x < 0 || x >= tileRange) { + x = (x % tileRange + tileRange) % tileRange; + } + + return { x: x, y: y }; + }, + + componentDidMount: function () { + var _this = this; + GoogleMapsLoader.KEY = this.props.googleKey; + GoogleMapsLoader.load(function (google) { var container = document.getElementById(_this.props.id + "_component"); - var map = new google.maps.Map(container, { - center: {lat: 0, lng: 0}, - zoom: 1, - streetViewControl: false, - mapTypeControlOptions: { - mapTypeIds: ['hm'] - } - }); - - var centreLat = 66.70383915858723; - var centreLon = -48.1640625; - - var imageMapType = new google.maps.ImageMapType({ - // Normalizes the coords that tiles repeat across the x axis (horizontally) - // like the standard Google map tiles. - getNormalizedCoord: function(coord, zoom) { - var y = coord.y; - var x = coord.x; - - // tile range in one direction range is dependent on zoom level - // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc - var tileRange = 1 << zoom; - - // don't repeat across y-axis (vertically) - if (y < 0 || y >= tileRange) { - return null; - } - - // repeat across x-axis - if (x < 0 || x >= tileRange) { - x = (x % tileRange + tileRange) % tileRange; - } - - return {x: x, y: y}; - }, - - // getTileUrl: function(coord, zoom) { - // var normalizedCoord = this.getNormalizedCoord(coord, zoom); - // if (!normalizedCoord) { - // return null; - // } - // var bound = Math.pow(2, zoom); - // return '//mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' + - // '/' + zoom + '/' + normalizedCoord.x + '/' + - // (bound - normalizedCoord.y - 1) + '.jpg'; - // }, - getTileUrl: function(a, b) { - // pervent wrap around - if (a.y < 0 || a.y >= (1 << b)) { - return null; - } - if (a.x < 0 || a.x >= (1 << b)) { - return null; - } - var c = Math.pow(2, b); - var d = a.x; - var e = a.y; - var f = "t"; - for (var g = 0; g < b; g++) { - c = c / 2; - if (e < c) { - if (d < c) { f += "q" } - else { f += "r"; d -= c } - } else { - if (d < c) { f += "t"; e -= c } - else { f += "s"; d -= c; e -= c } - } - } - subdirs = 3; - tmp = ""; - if (f.length >= subdirs) { // subdivide into sub-directories - for (i = 0; i < subdirs; i++) { - tmp += f.charAt(i) + "/"; - } - } - tmp += f; - return _this.props.path + "/" + tmp + ".jpg"; - }, - center: new google.maps.LatLng(50, 50), - tileSize: new google.maps.Size(256, 256), - isPng: false, - maxZoom: 11, - minZoom: 0, - radius: 1738000 - // name: 'HM' - }); + var map = new google.maps.Map(container, _this.state.mapSettings); + + // tileSize: new google.maps.Size(256, 256), + _this.state.imageMapTypeSettings['tileSize'] = new google.maps.Size(_this.state.tileWidth, _this.state.tileHeight); + var imageMapType = new google.maps.ImageMapType(_this.state.imageMapTypeSettings); map.mapTypes.set('imageMapType', imageMapType); map.setMapTypeId('imageMapType'); }); - GoogleMapsLoader.onLoad(function(google) { + GoogleMapsLoader.onLoad(function (google) { //console.log('I just loaded google maps api'); }); }, From 82a10a1ca889b24f79b0e44c277d7b4cf52e93dc Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 17 Apr 2017 09:49:35 -0700 Subject: [PATCH 089/339] creates unique id for components, to have access like widgets using console commands --- .../webapp/js/components/ComponentFactory.js | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index a6644be9b..c833d02ec 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -78,23 +78,44 @@ define(function (require) { return this.componentsMap; }, - addComponent: function(componentID, properties, container, callback){ + addComponent: function(componentType, properties, container, callback){ var that=this; - require(["./" + components[componentID]], function(loadedModule){ + require(["./" + components[componentType]], function(loadedModule){ var component = React.createFactory(loadedModule)(properties); var renderedComponent = that.renderComponent(component, container); if(callback!=undefined){ callback(renderedComponent); } - - // keep track of components in dictionary + + //create id for the component being rendered + var componentID = that.createComponentID(componentType,1); + //assign unique id to component + renderedComponent.id = componentID; + + // keep track of components in dictionary by id that.componentsMap[componentID] = renderedComponent; + + //create autocomplete tags for the component + window[componentID] = renderedComponent; + GEPPETTO.Console.updateTags(componentID, renderedComponent); return renderedComponent; }); }, + + /**Creates unique ID's for the components being created*/ + createComponentID : function(componentType,index){ + var componentID = componentType.charAt(0).toUpperCase() + + componentType.slice(1).toLowerCase()+ index.toString(); + + if(componentID in this.componentsMap){ + return this.createComponentID(componentType, ++index); + } + + return componentID; + }, renderComponent: function(component, container){ //Let's create a dialog From 67ac2159dbee1a24e6d941d660b937964f76790b Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 18 Apr 2017 12:15:13 +0100 Subject: [PATCH 090/339] formatting --- src/main/webapp/js/components/ComponentFactory.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index c833d02ec..154bceb28 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -107,8 +107,7 @@ define(function (require) { /**Creates unique ID's for the components being created*/ createComponentID : function(componentType,index){ - var componentID = componentType.charAt(0).toUpperCase() - + componentType.slice(1).toLowerCase()+ index.toString(); + var componentID = componentType.charAt(0).toUpperCase() + componentType.slice(1).toLowerCase() + index.toString(); if(componentID in this.componentsMap){ return this.createComponentID(componentType, ++index); From 49ab8147bffdf7203d3df782d221033cfb32cd61 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 18 Apr 2017 16:00:32 +0100 Subject: [PATCH 091/339] starting 3d canvas component --- .../webapp/js/components/ComponentFactory.js | 3 +- .../components/interface/3dCanvas/Canvas.css | 3 ++ .../components/interface/3dCanvas/Canvas.js | 32 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/main/webapp/js/components/interface/3dCanvas/Canvas.css create mode 100644 src/main/webapp/js/components/interface/3dCanvas/Canvas.js diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 45cbfadc4..5da6f4e25 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -68,7 +68,8 @@ define(function (require) { 'DICOMVIEWER': 'interface/dicomViewer/DicomViewer', 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer', - 'CAROUSEL': 'interface/carousel/Carousel' + 'CAROUSEL': 'interface/carousel/Carousel', + '3DCANVAS': 'interface/3dCanvas/Canvas', //'WIDGETCONTAINER': 'widgets/WidgetContainer' } diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.css b/src/main/webapp/js/components/interface/3dCanvas/Canvas.css new file mode 100644 index 000000000..5a5c99d26 --- /dev/null +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.css @@ -0,0 +1,3 @@ +.canvas{ + color:white; +} \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js new file mode 100644 index 000000000..9ef781d43 --- /dev/null +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -0,0 +1,32 @@ +define(function (require) { + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/interface/3dCanvas/Canvas.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + var React = require('react'); + + var THREE = require('three'); + var canvasComponent = React.createClass({ + + shouldComponentUpdate() { + return false; + }, + + componentDidMount: function () { + //Dialog id = this.props.id + // Component id = this.props.id + "_component" + $("#"+this.props.id + "_component").text('Matteo pon tus cositas aqui'); + }, + + render: function () { + return ( +
                +
                + ) + } + }); + return canvasComponent; +}); From 05df12bc19e15b0812ae9f920f31990cb1ae4e0a Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 18 Apr 2017 11:46:43 -0700 Subject: [PATCH 092/339] reset plot options --- src/main/webapp/js/components/widgets/plot/Plot.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index b7ceb253c..8205350c6 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -497,9 +497,9 @@ define(function (require) { self.plotOptions.yaxis.titlefont.size = defaultOptions.yaxis.titlefont.size; self.plotOptions.legend.font.size = defaultOptions.legend.font.size; self.plotOptions.legend.font.color = defaultOptions.legend.font.color; - self.plotOptions.legend.bgcolor = defaultOptions.margin.r; - self.plotOptions.margin.l= defaultOptions.margin.l; - + self.plotOptions.legend.bgcolor = defaultOptions.legend.bgcolor; + self.plotOptions.margin.l= oldMarginLeft; + self.plotOptions.margin.r=defaultOptions.margin.r; Plotly.relayout(self.plotDiv,self.plotOptions); }; setTimeout(reset, 100); From 8a1c2724453a748497f638166b66bca75c9a7cab Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 18 Apr 2017 18:27:16 +0100 Subject: [PATCH 093/339] formatting --- .../webapp/js/components/widgets/plot/Plot.js | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 8205350c6..767abea70 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -442,8 +442,7 @@ define(function (require) { downloadImage: function (imageType) { var self = this; - //play around with settings to make background temporarily white - //and marging wider + // play around with settings to make background temporarily white and margins wider this.plotOptions.paper_bgcolor = "rgb(255,255,255)"; this.plotOptions.xaxis.linecolor = "rgb(0,0,0)"; this.plotOptions.yaxis.linecolor = "rgb(0,0,0)"; @@ -480,28 +479,28 @@ define(function (require) { height: height, width: width }); - - var reset = function(){ - var defaultOptions = self.defaultOptions(); - //reset background and margin - self.plotOptions.paper_bgcolor = defaultOptions.paper_bgcolor; - self.plotOptions.xaxis.linecolor =defaultOptions.xaxis.linecolor; - self.plotOptions.yaxis.linecolor = defaultOptions.xaxis.linecolor; - self.plotOptions.xaxis.tickfont.color = defaultOptions.xaxis.tickfont.color; - self.plotOptions.yaxis.tickfont.color = defaultOptions.yaxis.tickfont.color; - self.plotOptions.yaxis.titlefont.color = defaultOptions.yaxis.titlefont.color; - self.plotOptions.xaxis.titlefont.color = defaultOptions.xaxis.titlefont.color; - self.plotOptions.xaxis.tickfont.size = defaultOptions.xaxis.tickfont.size; - self.plotOptions.yaxis.tickfont.size = defaultOptions.yaxis.tickfont.size; - self.plotOptions.xaxis.titlefont.size = defaultOptions.xaxis.titlefont.size; - self.plotOptions.yaxis.titlefont.size = defaultOptions.yaxis.titlefont.size; - self.plotOptions.legend.font.size = defaultOptions.legend.font.size; - self.plotOptions.legend.font.color = defaultOptions.legend.font.color; - self.plotOptions.legend.bgcolor = defaultOptions.legend.bgcolor; - self.plotOptions.margin.l= oldMarginLeft; - self.plotOptions.margin.r=defaultOptions.margin.r; - Plotly.relayout(self.plotDiv,self.plotOptions); - }; + + //reset background and margin to defaults + var reset = function(){ + var defaultOptions = self.defaultOptions(); + self.plotOptions.paper_bgcolor = defaultOptions.paper_bgcolor; + self.plotOptions.xaxis.linecolor =defaultOptions.xaxis.linecolor; + self.plotOptions.yaxis.linecolor = defaultOptions.xaxis.linecolor; + self.plotOptions.xaxis.tickfont.color = defaultOptions.xaxis.tickfont.color; + self.plotOptions.yaxis.tickfont.color = defaultOptions.yaxis.tickfont.color; + self.plotOptions.yaxis.titlefont.color = defaultOptions.yaxis.titlefont.color; + self.plotOptions.xaxis.titlefont.color = defaultOptions.xaxis.titlefont.color; + self.plotOptions.xaxis.tickfont.size = defaultOptions.xaxis.tickfont.size; + self.plotOptions.yaxis.tickfont.size = defaultOptions.yaxis.tickfont.size; + self.plotOptions.xaxis.titlefont.size = defaultOptions.xaxis.titlefont.size; + self.plotOptions.yaxis.titlefont.size = defaultOptions.yaxis.titlefont.size; + self.plotOptions.legend.font.size = defaultOptions.legend.font.size; + self.plotOptions.legend.font.color = defaultOptions.legend.font.color; + self.plotOptions.legend.bgcolor = defaultOptions.legend.bgcolor; + self.plotOptions.margin.l= oldMarginLeft; + self.plotOptions.margin.r=defaultOptions.margin.r; + Plotly.relayout(self.plotDiv,self.plotOptions); + }; setTimeout(reset, 100); }, From 7aec3ae1a26985f7efb5dea3f43b31aa88435d47 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 18 Apr 2017 18:46:08 +0100 Subject: [PATCH 094/339] Factoring out 3d Canvas, work in progress --- .../components/interface/3dCanvas/Canvas.js | 318 +++++++- .../interface/3dCanvas/SceneFactory.js | 736 ++++++++++++++++++ 2 files changed, 1034 insertions(+), 20 deletions(-) create mode 100644 src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 9ef781d43..743f9c4fa 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -1,25 +1,303 @@ -define(function (require) { +define(function(require) { - var link = document.createElement("link"); - link.type = "text/css"; - link.rel = "stylesheet"; - link.href = "geppetto/js/components/interface/3dCanvas/Canvas.css"; - document.getElementsByTagName("head")[0].appendChild(link); + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/interface/3dCanvas/Canvas.css"; + document.getElementsByTagName("head")[0].appendChild(link); - var React = require('react'); + var React = require('react'); - var THREE = require('three'); - var canvasComponent = React.createClass({ + var THREE = require('three'); + var isWebglEnabled = require('detector-webgl'); + require('./TrackballControls'); + require('./OBJLoader'); + var THREEx = require('./THREEx.KeyboardState'); + THREE.ColladaLoader = require('imports?THREE=three!exports?THREE.ColladaLoader!../../../../node_modules\/three\/examples\/js\/loaders\/ColladaLoader'); + THREE.ConvolutionShader = require('imports?THREE=three!exports?THREE.ConvolutionShader!../../../../node_modules\/three\/examples\/js\/shaders\/ConvolutionShader'); + THREE.CopyShader = require('imports?THREE=three!exports?THREE.CopyShader!../../../../node_modules\/three\/examples\/js\/shaders\/CopyShader'); + THREE.FilmShader = require('imports?THREE=three!exports?THREE.FilmShader!../../../../node_modules\/three\/examples\/js\/shaders\/FilmShader'); + THREE.FocusShader = require('imports?THREE=three!exports?THREE.FocusShader!../../../../node_modules\/three\/examples\/js\/shaders\/FocusShader'); + THREE.EffectComposer = require('imports?THREE=three!exports?THREE.EffectComposer!../../../../node_modules\/three\/examples\/js\/postprocessing\/EffectComposer'); + THREE.MaskPass = require('imports?THREE=three!exports?THREE.MaskPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/MaskPass'); + THREE.RenderPass = require('imports?THREE=three!exports?THREE.RenderPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/RenderPass'); + THREE.BloomPass = require('imports?THREE=three!exports?THREE.BloomPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/BloomPass'); + THREE.ShaderPass = require('imports?THREE=three!exports?THREE.ShaderPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/ShaderPass'); + THREE.FilmPass = require('imports?THREE=three!exports?THREE.FilmPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/FilmPass'); + var SceneFactory = require('./SceneFactory'); - shouldComponentUpdate() { - return false; - }, + var canvasComponent = React.createClass({ + factory: new SceneFactory(this), + camera: null, + container: null, + controls: null, + scene: null, + meshes: {}, + splitMeshes: {}, + connectionLines: {}, + renderer: null, + clock: new THREE.Clock(), + stats: null, + projector: null, + keyboard: new THREEx.KeyboardState(), + needsUpdate: false, + metadata: {}, + customUpdate: null, + mouseClickListener: null, + rotationMode: false, + mouse: { + x: 0, + y: 0 + }, + visualModelMap: null, + idCounter: 0, + sceneCenter: new THREE.Vector3(), + cameraPosition: new THREE.Vector3(), + canvasCreated: false, + listenersCreated: false, + selected: [], + pickingEnabled: true, // flag to enable disable 3d picking + backgroundColor: 0x101010, - componentDidMount: function () { - //Dialog id = this.props.id - // Component id = this.props.id + "_component" - $("#"+this.props.id + "_component").text('Matteo pon tus cositas aqui'); - }, + + setupScene: function() { + this.scene = new THREE.Scene(); + this.visualModelMap = {}; + this.meshes = {}; + this.splitMeshes = {}; + this.connectionLines = {}; + }, + + /** + * Sets up the camera that is used to view the objects in the 3D Scene. + */ + setupCamera: function() { + // Camera + var SCREEN_WIDTH = $(this.container).width(); + var SCREEN_HEIGHT = $(this.container).height(); + var VIEW_ANGLE = 60; + var ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT; + var NEAR = 10; + var FAR = 2000000; + this.camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR); + this.scene.add(this.camera); + this.camera.position.set(this.cameraPosition.x, this.cameraPosition.y, this.cameraPosition.z); + this.camera.up = new THREE.Vector3(0, 1, 0); + this.camera.direction = new THREE.Vector3(0, 0, 1); + this.camera.lookAt(this.sceneCenter); + }, + + /** + * Set up the WebGL Renderer + */ + setupRenderer: function() { + // Reuse a single WebGL renderer. + // NOTE: Recreating the renderer causes camera displacement on Chrome OSX. + if (!this.canvasCreated) { + this.renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: true + }); + + } + + this.configureRenderer(); + this.canvasCreated = true; + }, + + configureRenderer: function(shaders) { + + if (shaders == undefined) { + shaders = false; + } + + var color = new THREE.Color(this.backgroundColor); + //this.renderer.setClearColor(color, 1); + var width = $(this.container).width(); + var height = $(this.container).height(); + this.renderer.setPixelRatio(window.devicePixelRatio); + this.renderer.setSize(width, height); + this.renderer.autoClear = false; + this.container.appendChild(this.renderer.domElement); + + var renderModel = new THREE.RenderPass(this.scene, this.camera); + + this.composer = new THREE.EffectComposer(this.renderer); + + if (shaders) { + var effectBloom = new THREE.BloomPass(0.75); + var effectFilm = new THREE.FilmPass(0.5, 0.5, 1448, false); + var effectFocus = new THREE.ShaderPass(THREE.FocusShader); + + effectFocus.uniforms["screenWidth"].value = window.innerWidth; + effectFocus.uniforms["screenHeight"].value = window.innerHeight; + + effectFocus.renderToScreen = true; + + this.composer.addPass(renderModel); + this.composer.addPass(effectBloom); + this.composer.addPass(effectFilm); + this.composer.addPass(effectFocus); + } else { + //standard + var copyPass = new THREE.ShaderPass(THREE.CopyShader); + copyPass.renderToScreen = true; + this.composer.addPass(renderModel); + this.composer.addPass(copyPass); + } + + }, + + /** + * Light up the scene + */ + setupLights: function() { + // Lights + this.camera.add(new THREE.PointLight(0xffffff, 1.5)); + + }, + + /** + * Sets up the controls used by the camera to make it able to zoom and + * pan. + */ + setupControls: function() { + // Controls + this.controls = new THREE.TrackballControls(this.camera, this.renderer.domElement); + this.controls.noZoom = false; + this.controls.noPan = false; + }, + + /** + * Set up the listeners use to detect mouse movement and windoe resizing + */ + setupListeners: function() { + if (!this.listenersCreated) { + // when the mouse moves, call the given function + this.renderer.domElement.addEventListener('mousedown', function(event) { + if (event.button == 0) //only for left click + { + if (this.pickingEnabled) { + var intersects = GEPPETTO.getIntersectedObjects(); + + if (intersects.length > 0) { + var selected = ""; + var geometryIdentifier = ""; + + // sort intersects + var compare = function(a, b) { + if (a.distance < b.distance) + return -1; + if (a.distance > b.distance) + return 1; + return 0; + }; + + intersects.sort(compare); + + var selectedIntersect; + // Iterate and get the first visible item (they are now ordered by proximity) + for (var i = 0; i < intersects.length; i++) { + // figure out if the entity is visible + var instancePath = ""; + if (intersects[i].object.hasOwnProperty("instancePath")) { + instancePath = intersects[i].object.instancePath; + geometryIdentifier = intersects[i].object.geometryIdentifier; + } else { + //weak assumption: if the object doesn't have an instancePath its parent will + instancePath = intersects[i].object.parent.instancePath; + geometryIdentifier = intersects[i].object.parent.geometryIdentifier; + } + if (instancePath != null || undefined) { + var visible = eval(instancePath + '.visible'); + if (intersects.length == 1 || i == intersects.length) { + //if there's only one element intersected we select it regardless of its opacity + if (visible) { + selected = instancePath; + selectedIntersect = intersects[i]; + break; + } + } else { + //if there are more than one element intersected and opacity of the current one is less than 1 + //we skip it to realize a "pick through" + var opacity = this.meshes[instancePath].defaultOpacity; + if ((opacity == 1 && visible) || GEPPETTO.isKeyPressed("ctrl")) { + selected = instancePath; + selectedIntersect = intersects[i]; + break; + } else if (visible && opacity < 1 && opacity > 0) { + //if only transparent objects intersected select first or the next down if + //one is already selected in order to enable "burrow through" sample. + if (selected == "" && !eval(instancePath + '.selected')) { + selected = instancePath; + selectedIntersect = intersects[i]; + } else { + if (eval(instancePath + '.selected') && i != intersects.length - 1) { + selected = ""; + } + } + } + } + } + } + + + if (selected != "") { + if (this.meshes.hasOwnProperty(selected) || this.splitMeshes.hasOwnProperty(selected)) { + if (!GEPPETTO.isKeyPressed("shift")) { + GEPPETTO.G.unSelectAll(); + } + + var selectedIntersectCoordinates = [selectedIntersect.point.x, selectedIntersect.point.y, selectedIntersect.point.z] + if (geometryIdentifier == undefined) { + geometryIdentifier = ""; + } + GEPPETTO.Console.executeCommand(selected + '.select(' + false + ', ' + '"' + geometryIdentifier + '", [' + selectedIntersectCoordinates + '])'); + } + } + } else if (GEPPETTO.isKeyPressed("ctrl")) { + GEPPETTO.G.unSelectAll(); + } + } + } + }, false); + + var that=this; + this.renderer.domElement.addEventListener('mousemove', function(event) { + that.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + that.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + }, false); + + this.container.addEventListener('resize', function() { + var container = $(that.container); + var width = container.width(); + var height = container.height(); + + that.camera.aspect = (width) / (height); + that.camera.updateProjectionMatrix(); + that.renderer.setSize(width, height); + that.composer.setSize(width, height); + }, false); + + this.listenersCreated = true; + } + }, + + + shouldComponentUpdate() { + return false; + }, + + componentDidMount: function() { + this.container = $("#" + this.props.id + "_component").get(0); + this.setupScene(); + this.setupCamera(); + this.setupRenderer(); + this.setupLights(); + this.setupControls(); + this.setupListeners(); + this.initialised = true; + }, render: function () { return ( @@ -27,6 +305,6 @@ define(function (require) {
                ) } - }); - return canvasComponent; -}); + }); + return canvasComponent; +}); \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js b/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js new file mode 100644 index 000000000..a40fb106d --- /dev/null +++ b/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js @@ -0,0 +1,736 @@ +define(['jquery'], function () { + + function SceneFactory(viewer) { + + this.viewer = viewer; + this.linesUserInput=false; + this.linesUserPreference=undefined; + this.linesThreshold=2000; + this.aboveLinesThreshold=false; + this.wireframe=false; + this.isAnimated=false; + } + + SceneFactory.prototype = { + + constructor: SceneFactory, + + + setLinesUserInput:function(askUser){ + this.linesUserInput=askUser; + }, + + + buildScene: function (instances) { + this.traverseInstances(instances); + this.viewer.scene.updateMatrixWorld(true); + }, + + + updateSceneWithNewInstances: function (instances) { + var updateCamera=false; + if(Object.keys(Gthis.viewer.meshes).length === 0){ + updateCamera=true; + } + for (var g = 0; g < instances.length; g++) { + // add instance to scene + this.checkVisualInstance(instances[g]); + } + if(updateCamera){ + G.resetCamera(); + } + }, + + /** + * Traverse the instances building a visual object when needed + * + * @param instances - + * skeleton with instances and visual entities + */ + traverseInstances: function (instances, lines, thickness) { + for (var j = 0; j < instances.length; j++) { + this.checkVisualInstance(instances[j], lines, thickness); + } + }, + + /** + * Check if we need to create a visual object for a given instance and keeps iterating + * + * @param instances - + * skeleton with instances and visual entities + */ + checkVisualInstance: function (instance, lines, thickness) { + if (instance.hasCapability(GEPPETTO.Resources.VISUAL_CAPABILITY)) { + //since the visualcapability propagates up through the parents we can avoid visiting things that don't have it + if ((instance.getType().getMetaType() != GEPPETTO.Resources.ARRAY_TYPE_NODE) && instance.getVisualType()) { + this.buildVisualInstance(instance, lines, thickness); + } + // this block keeps traversing the instances + if (instance.getMetaType() == GEPPETTO.Resources.INSTANCE_NODE) { + this.traverseInstances(instance.getChildren(), lines, thickness); + } else if (instance.getMetaType() == GEPPETTO.Resources.ARRAY_INSTANCE_NODE) { + this.traverseInstances(instance, lines, thickness); + } + } + }, + + + buildVisualInstance: function (instance, lines, thickness) { + var meshes = this.generate3DObjects(instance, lines, thickness); + this.init3DObject(meshes, instance); + }, + + /** + * Initializes a group of meshes that were created and adds them to the 3D scene + * + * @param {Object} + * meshes - The meshes that need to be initialized + */ + init3DObject: function (meshes, instance) { + var instancePath = instance.getInstancePath(); + var position = instance.getPosition(); + for (var m in meshes) { + var mesh = meshes[m]; + + mesh.instancePath = instancePath; + // if the model file is specifying a position for the loaded meshes then we translate them here + if (position != null) { + var p = new THREE.Vector3(position.x, position.y, position.z); + mesh.position.set(p.x, p.y, p.z); + mesh.matrixAutoUpdate = false; + mesh.applyMatrix(new THREE.Matrix4().makeTranslation(p.x, p.y, p.z)); + mesh.geometry.verticesNeedUpdate = true; + mesh.updateMatrix(); + // mesh.geometry.translate(position.x, position.y,position.z); + } + this.viewer.scene.add(mesh); + this.viewer.meshes[instancePath] = mesh; + this.viewer.meshes[instancePath].visible = true; + this.viewer.meshes[instancePath].ghosted = false; + this.viewer.meshes[instancePath].defaultOpacity = 1; + this.viewer.meshes[instancePath].selected = false; + this.viewer.meshes[instancePath].input = false; + this.viewer.meshes[instancePath].output = false; + + //Split anything that was splitted before + if (instancePath in this.viewer.splitMeshes) { + var splitMeshes = this.viewer.splitMeshes; + var elements = {}; + for (var splitMesh in splitMeshes) { + if (splitMeshes[splitMesh].instancePath == instancePath && splitMesh != instancePath) { + visualObject = splitMesh.substring(instancePath.length + 1); + elements[visualObject] = ""; + } + } + if (Object.keys(elements).length > 0) { + this.splitGroups(instance, elements); + } + } + } + }, + + /** + * + * @param instance + * @param lines + * @param thickness + * @returns {Array} + */ + generate3DObjects: function (instance, lines, thickness) { + var previous3DObject = this.viewer.meshes[instance.getInstancePath()]; + var color = undefined; + if (previous3DObject) { + color=previous3DObject.material.defaultColor; + // if an object already exists for this aspect we remove it. This could happen in case we are changing how an aspect + // is visualized, e.g. lines over tubes representation + this.viewer.scene.remove(previous3DObject); + var splitMeshes = this.viewer.splitMeshes; + for (var m in splitMeshes) { + if (m.indexOf(instance.getInstancePath()) != -1) { + this.viewer.scene.remove(splitMeshes[m]); + //splitMeshes[m] = null; + } + } + + } + var that = this; + //TODO This can be optimised, no need to create both + var materials = + { + "mesh": that.getMeshPhongMaterial(color), + "line": that.getLineMaterial(thickness,color) + }; + var instanceObjects = []; + var threeDeeObjList = this.walkVisTreeGen3DObjs(instance, materials, lines); + + // only merge if there are more than one object + if (threeDeeObjList.length > 1) { + var mergedObjs = this.merge3DObjects(threeDeeObjList, materials); + // investigate need to obj.dispose for obj in threeDeeObjList + if (mergedObjs != null) { + mergedObjs.instancePath = instance.getInstancePath(); + instanceObjects.push(mergedObjs); + } else { + for (var obj in threeDeeObjList) { + threeDeeObjList[obj].instancePath = instance.getInstancePath(); + instanceObjects.push(threeDeeObjList[obj]); + } + } + } + else if(threeDeeObjList.length==1) + { + // only one object in list, add it to local array and set + instanceObjects.push(threeDeeObjList[0]); + instanceObjects[0].instancePath = instance.getInstancePath(); + } + + return instanceObjects; + }, + + /** + * + * @param instance + * @param materials + * @param lines + * @returns {Array} + */ + walkVisTreeGen3DObjs: function (instance, materials, lines) { + var threeDeeObj = null; + var threeDeeObjList = []; + var visualType = instance.getVisualType(); + if (visualType == undefined) { + return threeDeeObjList; + } + else { + if ($.isArray(visualType)) { + //TODO if there is more than one visual type we need to display all of them + visualType = visualType[0]; + } + } + if (visualType.getMetaType() == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { + for (var v in visualType.getVariables()) { + var visualValue = visualType.getVariables()[v].getWrappedObj().initialValues[0].value; + threeDeeObj = this.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getVariables()[v].getId(), materials, lines); + if (threeDeeObj) { + threeDeeObjList.push(threeDeeObj); + } + } + } else { + var visualValue = visualType.getWrappedObj().defaultValue; + threeDeeObj = this.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getId(), materials, lines); + if (threeDeeObj) { + threeDeeObjList.push(threeDeeObj); + } + } + + return threeDeeObjList; + }, + + + /** + * + * @param objArray + * @param materials + * @returns {*} + */ + merge3DObjects: function (objArray, materials) { + var mergedMeshesPaths = []; + var ret = null; + var mergedLines; + var mergedMeshes; + objArray.forEach(function (obj) { + if (obj instanceof THREE.Line) { + if (mergedLines === undefined) { + mergedLines = new THREE.Geometry() + } + mergedLines.vertices.push(obj.geometry.vertices[0]); + mergedLines.vertices.push(obj.geometry.vertices[1]); + } + else if (obj.geometry.type == "Geometry") { + // This catches both Collada an OBJ + if (objArray.length > 1) { + throw Error("Merging of multiple OBJs or Colladas not supported"); + } + else { + ret = obj; + } + } + else { + if (mergedMeshes === undefined) { + mergedMeshes = new THREE.Geometry() + } + obj.geometry.dynamic = true; + obj.geometry.verticesNeedUpdate = true; + obj.updateMatrix(); + mergedMeshes.merge(obj.geometry, obj.matrix); + } + mergedMeshesPaths.push(obj.instancePath); + + }); + + if (mergedLines === undefined) { + // There are no line geometries, we just create a mesh for the merge of the solid geometries + // and apply the mesh material + ret = new THREE.Mesh(mergedMeshes, materials["mesh"]); + } else { + ret = new THREE.LineSegments(mergedLines, materials["line"]); + if (mergedMeshes != undefined) { + // we merge into a single mesh both types of geometries (from lines and 3D objects) + var tempmesh = new THREE.Mesh(mergedMeshes, materials["mesh"]); + ret.geometry.merge(tempmesh.geometry, tempmesh.matrix); + } + } + + if (ret != null && !Array.isArray(ret)) { + ret.mergedMeshesPaths = mergedMeshesPaths; + } + + return ret; + + }, + + + /** + * + * @param instance + * @param node + * @param id + * @param materials + * @param lines + * @returns {*} + */ + visualizationTreeNodeTo3DObj: function (instance, node, id, materials, lines) { + var threeObject = null; + + if (lines === undefined) { + // Unless it's being forced we use a threshold to decide whether to use lines or cylinders + if (!this.aboveLinesThreshold) { + //Unless we are already above the threshold... + this.aboveLinesThreshold = this.complexity > this.linesThreshold; + + if (this.aboveLinesThreshold) { + + if(this.linesUserInput && this.linesUserPreference==undefined){ + + //we need to ask the user + this.linesUserPreference = confirm("The model you are loading has a complex morphology, would you like to render it using lines instead of 3D shapes? Be careful, choosing to use 3D shapes might crash your browser!"); + + if (this.linesUserPreference) { + this.setAllGeometriesType(GEPPETTO.Resources.GeometryTypes.LINES); + } + else{ + } + } + else{ + this.setAllGeometriesType(GEPPETTO.Resources.GeometryTypes.LINES); + } + } + } + + if(this.aboveLinesThreshold && this.linesUserInput){ + lines = this.linesUserPreference; + } + else{ + lines = this.aboveLinesThreshold; + } + } + + var material = lines ? materials["line"] : materials["mesh"]; + + switch (node.eClass) { + case GEPPETTO.Resources.PARTICLE: + threeObject = this.createParticle(node); + break; + + case GEPPETTO.Resources.CYLINDER: + if (lines) { + threeObject = this.create3DLineFromNode(node, material); + } else { + threeObject = this.create3DCylinderFromNode(node, material); + } + break; + + case GEPPETTO.Resources.SPHERE: + if (lines) { + threeObject = this.create3DLineFromNode(node, material); + } else { + threeObject = this.create3DSphereFromNode(node, material); + } + break; + case GEPPETTO.Resources.COLLADA: + threeObject = this.loadColladaModelFromNode(node); + break; + case GEPPETTO.Resources.OBJ: + threeObject = this.loadThreeOBJModelFromNode(node); + break; + } + if (threeObject) { + threeObject.visible = true; + // TODO: this is empty for collada and obj nodes + var instancePath = instance.getInstancePath() + "." + id; + threeObject.instancePath = instancePath; + threeObject.highlighted = false; + + // TODO: shouldn't that be the vistree? why is it also done at the loadEntity level?? + this.viewer.visualModelMap[instancePath] = threeObject; + } + return threeObject; + }, + + /** + * + * @param node + * @returns {*} + */ + loadColladaModelFromNode: function (node) { + var loader = new THREE.ColladaLoader(); + loader.options.convertUpAxis = true; + var scene = null; + loader.parse(node.collada, function (collada) { + scene = collada.scene; + scene.traverse(function (child) { + if (child instanceof THREE.Mesh) { + child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; + child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.material.wireframe = this.wireframe; + child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.geometry.computeVertexNormals(); + } + if (child instanceof THREE.SkinnedMesh) { + child.material.skinning = true; + child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; + child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.material.wireframe = this.wireframe; + child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.geometry.computeVertexNormals(); + } + }); + }); + return scene; + }, + + /** + * + * @param node + * @returns {*} + */ + loadThreeOBJModelFromNode: function (node) { + var manager = new THREE.LoadingManager(); + manager.onProgress = function (item, loaded, total) { + console.log(item, loaded, total); + }; + var loader = new THREE.OBJLoader(manager); + var scene = loader.parse(node.obj); + + scene.traverse(function (child) { + if (child instanceof THREE.Mesh) { + child.material.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); + child.material.wireframe = this.wireframe; + child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; + child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.geometry.computeVertexNormals(); + } + }); + + return scene; + }, + + /** + * + * @param node + * @returns {THREE.Vector3|*} + */ + createParticle: function (node) { + threeObject = new THREE.Vector3(node.position.x, node.position.y, node.position.z); + threeObject.visible = true; + threeObject.instancePath = node.instancePath; + threeObject.highlighted = false; + // TODO: does that need to be done? + this.viewer.visualModelMap[node.instancePath] = threeObject; + + return threeObject; + + }, + + /** + * + * @param node + * @param material + * @returns {THREE.Line} + */ + create3DLineFromNode: function (node, material) { + if (node.eClass == GEPPETTO.Resources.CYLINDER) { + var bottomBasePos = new THREE.Vector3(node.position.x, node.position.y, node.position.z); + var topBasePos = new THREE.Vector3(node.distal.x, node.distal.y, node.distal.z); + + var axis = new THREE.Vector3(); + axis.subVectors(topBasePos, bottomBasePos); + var midPoint = new THREE.Vector3(); + midPoint.addVectors(bottomBasePos, topBasePos).multiplyScalar(0.5); + + var geometry = new THREE.Geometry(); + geometry.vertices.push(bottomBasePos); + geometry.vertices.push(topBasePos); + var threeObject = new THREE.Line(geometry, material); + threeObject.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 2)); + threeObject.lookAt(axis); + threeObject.position.fromArray(midPoint.toArray()); + + threeObject.geometry.verticesNeedUpdate = true; + } else if (node.eClass == GEPPETTO.Resources.SPHERE) { + var sphere = new THREE.SphereGeometry(node.radius, 20, 20); + threeObject = new THREE.Mesh(sphere, material); + threeObject.position.set(node.position.x, node.position.y, node.position.z); + threeObject.geometry.verticesNeedUpdate = true; + } + return threeObject; + }, + + /** + * + * @param cylNode + * @param material + * @returns {THREE.Mesh} + */ + create3DCylinderFromNode: function (cylNode, material) { + + var bottomBasePos = new THREE.Vector3(cylNode.position.x, cylNode.position.y, cylNode.position.z); + var topBasePos = new THREE.Vector3(cylNode.distal.x, cylNode.distal.y, cylNode.distal.z); + + var axis = new THREE.Vector3(); + axis.subVectors(topBasePos, bottomBasePos); + var midPoint = new THREE.Vector3(); + midPoint.addVectors(bottomBasePos, topBasePos).multiplyScalar(0.5); + + var c = new THREE.CylinderGeometry(cylNode.topRadius, cylNode.bottomRadius, axis.length(), 20, 1, false); + c.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 2)); + var threeObject = new THREE.Mesh(c, material); + + threeObject.lookAt(axis); + threeObject.position.fromArray(midPoint.toArray()); + + threeObject.geometry.verticesNeedUpdate = true; + return threeObject; + }, + + /** + * Modify the origin and radius of a sphere + * @returns {THREE.Mesh} + */ + modify3DSphere:function(object,x,y,z,radius){ + // Impossible to change the radius of a Sphere. + // Removing old object and creating a new one + this.viewer.scene.remove(object); + return this.add3DSphere(x,y,z,radius); + }, + + /** + * Add a 3D sphere to the scene at the given coordinates (4) points. + * It could be any geometry really. + * @returns {THREE.Mesh} + */ + add3DSphere: function (x,y,z,radius) { + if (this.aboveLinesThreshold) { + radius = 1; + } + + var material= new THREE.MeshBasicMaterial({side:THREE.DoubleSide}); + material.nowireframe=true; + material.opacity= 0.6; + material.transparent=true; + material.color.setHex("0xff0000"); + + var sphereNode ={radius:radius,position:{x:x, y:y, z:z}} + var mesh = this.create3DSphereFromNode(sphereNode, material) + mesh.renderOrder=1; + mesh.clickThrough=true; + this.viewer.scene.add(mesh); + return mesh; + }, + + + /** + * Add a 3D plane to the scene at the given coordinates (4) points. + * It could be any geometry really. + * @returns {THREE.Mesh} + */ + add3DPlane: function (x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4, textureURL) { + + var geometry=new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3(x1,y1,z1),//vertex0 + new THREE.Vector3(x2,y2,z2),//1 + new THREE.Vector3(x3,y3,z3),//2 + new THREE.Vector3(x4,y4,z4)//3 + ); + geometry.faces.push( + new THREE.Face3(2,1,0),//use vertices of rank 2,1,0 + new THREE.Face3(3,1,2)//vertices[3],1,2... + ); + geometry.computeBoundingBox(); + + var max = geometry.boundingBox.max, + min = geometry.boundingBox.min; + var offset = new THREE.Vector2(0 - min.x, 0 - min.y); + var range = new THREE.Vector2(max.x - min.x, max.y - min.y); + var faces = geometry.faces; + + geometry.faceVertexUvs[0] = []; + + for (var i = 0; i < faces.length ; i++) { + + var v1 = geometry.vertices[faces[i].a], + v2 = geometry.vertices[faces[i].b], + v3 = geometry.vertices[faces[i].c]; + + geometry.faceVertexUvs[0].push([ + new THREE.Vector2((v1.x + offset.x)/range.x ,(v1.y + offset.y)/range.y), + new THREE.Vector2((v2.x + offset.x)/range.x ,(v2.y + offset.y)/range.y), + new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y) + ]); + } + geometry.uvsNeedUpdate = true; + geometry.dynamic = true; + + var material= new THREE.MeshBasicMaterial({side:THREE.DoubleSide}); + material.nowireframe=true; + if(textureURL!=undefined){ + var loader = new THREE.TextureLoader(); + // load a resource + loader.load( + // resource URL + textureURL, + // Function when resource is loaded + function ( texture ) { + //texture.minFilter = THREE.LinearFilter; + material.map=texture; + texture.flipY = false; + material.opacity= 0.3; + material.transparent=true; + material.needsUpdate = true; + + }, + // Function called when download progresses + function ( xhr ) { + console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); + }, + // Function called when download errors + function ( xhr ) { + console.log( 'An error happened' ); + } + ); + + } + else{ + material.opacity= 0.3; + material.transparent=true; + material.color.setHex("0xb0b0b0"); + } + + var mesh = new THREE.Mesh(geometry, material); + mesh.renderOrder=1; + mesh.clickThrough=true; + this.viewer.scene.add(mesh); + return mesh; + }, + + /** + * Modify the coordinates (4) points of an existing plane. + * @returns {THREE.Mesh} + */ + modify3DPlane:function(object,x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4){ + object.geometry.vertices[0].set(x1,y1,z1); + object.geometry.vertices[1].set(x2,y2,z2); + object.geometry.vertices[2].set(x3,y3,z3); + object.geometry.vertices[3].set(x4,y4,z4); + object.geometry.verticesNeedUpdate = true; + return object; + }, + + + /** + * + * @param sphereNode + * @param material + * @returns {THREE.Mesh|*} + */ + create3DSphereFromNode: function (sphereNode, material) { + + var sphere = new THREE.SphereGeometry(sphereNode.radius, 20, 20); + // sphere.applyMatrix(new THREE.Matrix4().makeScale(-1,1,1)); + var threeObject = new THREE.Mesh(sphere, material); + threeObject.position.set(sphereNode.position.x, sphereNode.position.y, sphereNode.position.z); + + threeObject.geometry.verticesNeedUpdate = true; + return threeObject; + }, + + /** + * + * @param thickness + * @returns {THREE.LineBasicMaterial} + */ + getLineMaterial: function (thickness, color) { + var options = {}; + if (thickness) { + options.linewidth = thickness; + } + if (color == undefined) { + color = GEPPETTO.Resources.COLORS.DEFAULT; + } + var material = new THREE.LineBasicMaterial(options); + material.color.setHex(color); + material.defaultColor = color; + material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + return material; + }, + + /** + * + * @param color + * @returns {THREE.MeshPhongMaterial} + */ + getMeshPhongMaterial: function (color) { + if (color == undefined) { + color = GEPPETTO.Resources.COLORS.DEFAULT; + } + var material = new THREE.MeshPhongMaterial( + { + opacity: 1, + shininess: 10, + shading: THREE.SmoothShading + }); + + material.color.setHex(color); + material.defaultColor = color; + material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + return material; + }, + + /** + * + * @returns {THREE.PointsMaterial} + */ + getParticleMaterial: function () { + var textureLoader = new THREE.TextureLoader(); + var pMaterial = new THREE.PointsMaterial( + { + size: 5, + map: textureLoader.load("geppetto/images/particle.png"), + blending: THREE.AdditiveBlending, + depthTest: false, + transparent: true + }); + pMaterial.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); + pMaterial.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; + pMaterial.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + pMaterial.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + return pMaterial; + } + +}; + + return SceneFactory; +}); + From 7a6df71cf3a538da7157160e4a499155ff826cbe Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 19 Apr 2017 04:36:34 +0100 Subject: [PATCH 095/339] use regular extension for widgets --- .../webapp/js/components/widgets/NewWidget.js | 230 +++++++++--------- 1 file changed, 113 insertions(+), 117 deletions(-) diff --git a/src/main/webapp/js/components/widgets/NewWidget.js b/src/main/webapp/js/components/widgets/NewWidget.js index 48c523cce..c92df9190 100644 --- a/src/main/webapp/js/components/widgets/NewWidget.js +++ b/src/main/webapp/js/components/widgets/NewWidget.js @@ -16,11 +16,11 @@ define(function (require) { /** * Creates base view for widget */ - var Widget = React.createClass({ + return class Widget extends WrappedComponent { - getInitialState: function () { - - return { + constructor(props) { + super(props); + this.state = $.extend(this.state, { dialog: null, visible: true, destroyed: false, @@ -36,22 +36,8 @@ define(function (require) { defaultSize: { height: 300, width: 350 }, defaultPosition: { left: "50%", top: "50%" } - }; - }, - - - /** - * Initializes the widget - * - * @param {String} id - id of widget - * @param {String} name - name of widget - * @param {String} visibility - visibility of widget window - */ - initialize: function (options) { - this.id = options.id; - this.name = options.name; - this.visible = options.visible; - }, + }); + } /** * Destroy the widget, remove it from DOM @@ -59,11 +45,11 @@ define(function (require) { * @command destroy() * @returns {String} - Action Message */ - destroy: function () { + destroy () { this.$el.remove(); this.destroyed = true; return this.name + " destroyed"; - }, + } /** *ff @@ -72,13 +58,13 @@ define(function (require) { * @command hide() * @returns {String} - Action Message */ - hide: function () { + hide() { this.$el.dialog('close').dialogExtend(); this.visible = false; return "Hiding " + this.name + " widget"; - }, + } /** * Opens widget dialog @@ -86,7 +72,7 @@ define(function (require) { * @command show() * @returns {String} - Action Message */ - show: function () { + show() { this.$el.dialog('open').dialogExtend(); this.visible = true; @@ -94,7 +80,7 @@ define(function (require) { $(".ui-dialog-titlebar-close").blur(); return this; - }, + } /** * Gets the name of the widget @@ -102,30 +88,30 @@ define(function (require) { * @command getName() * @returns {String} - Name of widget */ - getName: function () { + getName() { return this.name; - }, + } /** * Sets the name of the widget * @command setName(name) * @param {String} name - Name of widget */ - setName: function (name) { + setName(name) { this.name = name; // set name to widget window this.$el.dialog("option", "title", this.name).dialogExtend(); return this; - }, + } /** * @command setPosition(left,top) * @param {Integer} left -Left position of the widget * @param {Integer} top - Top position of the widget */ - setPosition: function (left, top) { + setPosition(left, top) { this.position.left = left; this.position.top = top; @@ -136,7 +122,7 @@ define(function (require) { of: $(window) }).dialogExtend(); return this; - }, + } /** * Sets the size of the widget @@ -144,67 +130,67 @@ define(function (require) { * @param {Integer} h - Height of the widget * @param {Integer} w - Width of the widget */ - setSize: function (h, w) { + setSize(h, w) { this.size.height = h; this.size.width = w; this.$el.dialog({ height: this.size.height, width: this.size.width }).dialogExtend(); this.$el.trigger('resizeEnd'); return this; - }, + } /** * @command setMinHeight(h) * @param {Integer} h - Minimum Height of the widget */ - setMinHeight: function (h) { + setMinHeight(h) { this.$el.dialog('option', 'minHeight', h).dialogExtend(); return this; - }, + } /** * @command setMinWidth(w) * @param {Integer} w - Minimum Width of the widget */ - setMinWidth: function (w) { + setMinWidth(w) { this.$el.dialog('option', 'minWidth', w).dialogExtend(); return this; - }, + } /** * @command setMinSize(h,w) * @param {Integer} h - Minimum Height of the widget * @param {Integer} w - Minimum Width of the widget */ - setMinSize: function (h, w) { + setMinSize(h, w) { this.setMinHeight(h); this.setMinWidth(w); return this; - }, + } /** * @command setResizable(true|false) * @param {Boolean} true|false - enables / disables resizability */ - setResizable: function (resize) { + setResizable(resize) { this.$el.dialog('option', 'resizable', resize).dialogExtend(); return this; - }, + } /** * @command setAutoWidth() */ - setAutoWidth: function () { + setAutoWidth() { this.$el.dialog('option', 'width', 'auto').dialogExtend(); return this; - }, + } /** * @command setAutoHeigth() */ - setAutoHeight: function () { + setAutoHeight() { this.$el.dialog('option', 'height', 'auto').dialogExtend(); return this; - }, + } /** @@ -212,18 +198,18 @@ define(function (require) { * @command getPosition() * @returns {Object} - Position of the widget */ - getPosition: function () { + getPosition() { return this.position; - }, + } /** * Returns the size of the widget * @command getSize() * @returns {Object} - Size of the widget */ - getSize: function () { + getSize() { return this.size; - }, + } /** * Gets the ID of the widget @@ -231,9 +217,9 @@ define(function (require) { * @command getId() * @returns {String} - ID of widget */ - getId: function () { + getId() { return this.id; - }, + } /** * Returns whether widget is visible or not @@ -241,9 +227,9 @@ define(function (require) { * @command isVisible() * @returns {Boolean} - Widget visibility state */ - isVisible: function () { + isVisible() { return this.visible; - }, + } /** * Search obj for the value of node within using path. @@ -251,7 +237,7 @@ define(function (require) { * search within the obj to find the value of "tree.v", returning object * containing {value : val, unit : unit, scale : scale}. */ - getState: function (tree, state) { + getState(tree, state) { var paths = state.split('.') , current = tree , i; @@ -281,9 +267,9 @@ define(function (require) { } } return current; - }, + } - getItems: function (history, name) { + getItems(history, name) { var data = []; for (var i = 0; i < history.length; i++) { var action = this.getId() + "[" + this.getId() + "." + name + "[" + i + "].method].apply(" + this.getId() + ", " + this.getId() + "." + name + "[" + i + "].arguments)"; @@ -295,9 +281,9 @@ define(function (require) { }) } return data; - }, + } - showHistoryMenu: function (event) { + showHistoryMenu(event) { var that = this; if (this.controller.history.length > 0) { @@ -313,9 +299,9 @@ define(function (require) { event.preventDefault(); } return false; - }, + } - showContextMenu: function (event, data) { + showContextMenu(event, data) { var handlers = GEPPETTO.MenuManager.getCommandsProvidersFor(data.getMetaType()); if (handlers.length > 0) { @@ -338,21 +324,21 @@ define(function (require) { } return false; - }, + } /** * hides / shows the title bar */ - showTitleBar: function (show) { + showTitleBar(show) { if (show) { this.$el.parent().find(".ui-dialog-titlebar").show(); } else { this.$el.parent().find(".ui-dialog-titlebar").hide(); } return this; - }, + } - updateNavigationHistoryBar: function () { + updateNavigationHistoryBar() { var disabled = "arrow-disabled"; if (this.getItems(this.controller.history, "controller.history").length <= 1) { if (!$("#" + this.id + "-left-nav").hasClass(disabled)) { @@ -365,9 +351,9 @@ define(function (require) { $("#" + this.id + "-right-nav").removeClass(disabled); } } - }, + } - showHistoryNavigationBar: function (show) { + showHistoryNavigationBar(show) { var leftNav = $("#" + this.id + "-left-nav"); var rightNav = $("#" + this.id + "-right-nav"); @@ -404,26 +390,26 @@ define(function (require) { this.executedAction = 0; } } - }, + } /** * hides / shows the exit button */ - showCloseButton: function (show) { + showCloseButton(show) { if (show) { this.$el.parent().find(".ui-dialog-titlebar-close").show(); } else { this.$el.parent().find(".ui-dialog-titlebar-close").hide(); } - }, + } - addButtonToTitleBar: function (button) { + addButtonToTitleBar(button) { var dialogParent = this.$el.parent(); dialogParent.find("div.ui-dialog-titlebar").prepend(button); $(button).addClass("widget-title-bar-button"); - }, + } - addHelpButton: function () { + addHelpButton() { var that = this; this.addButtonToTitleBar($("
                ").click(function () { GEPPETTO.ComponentFactory.addComponent('MDMODAL', { @@ -432,14 +418,14 @@ define(function (require) { show: true }, document.getElementById("modal-region")); })); - }, + } /** * Makes the widget draggable or not * * @param draggable */ - setDraggable: function (draggable) { + setDraggable(draggable) { if (draggable) { this.$el.parent().draggable({ disabled: false }); // NOTE: this will wipe any class applied to the widget... @@ -448,14 +434,14 @@ define(function (require) { this.$el.parent().draggable({ disabled: true }); this.setClass('noStyleDisableDrag'); } - }, + } /** * Set background as transparent * * @param isTransparent */ - setTrasparentBackground: function (isTransparent) { + setTrasparentBackground(isTransparent) { if (isTransparent) { this.$el.parent().addClass('transparent-back'); this.previousMaxTransparency = true; @@ -463,26 +449,26 @@ define(function (require) { this.$el.parent().removeClass('transparent-back'); } return this; - }, + } /** * Inject CSS for custom behaviour */ - setClass: function (className) { + setClass(className) { this.$el.dialog({ dialogClass: className }).dialogExtend(); - }, + } /** * Perform a jquery ui effect to the widget */ - perfomEffect: function (effect, options, speed) { + perfomEffect(effect, options, speed) { this.$el.parent().effect(effect, options, speed) - }, + } /** * Perform a shake effect to the widget */ - shake: function (options, speed) { + shake(options, speed) { if (options === undefined) { options = { distance: 5, times: 3 } } @@ -491,13 +477,20 @@ define(function (require) { } this.$el.parent().effect('shake', options, speed) - }, + } shouldComponentUpdate() { return false; - }, + } + + componentDidMount() { + try { + super.componentDidMount(); + } + catch(err) { + console.log('Component does not implement component did mount method') + } - componentDidMount: function () { var that = this; //create the dialog window for the widget @@ -510,7 +503,7 @@ define(function (require) { top: 10, height: 300, width: 350, - close: function (event, ui) { + close(event, ui) { if (event.originalEvent && $(event.originalEvent.target).closest(".ui-dialog-titlebar-close").length) { that.destroy(); @@ -529,27 +522,27 @@ define(function (require) { "collapse": "fa fa-chevron-circle-up", "restore": "fa fa-window-restore", }, - "load": function (evt, dlg) { + "load"(evt, dlg) { var icons = $("#" + that.id).parent().find(".ui-icon"); for (var i = 0; i < icons.length; i++) { //remove text from span added by vendor library $(icons[i]).text(""); } }, - "beforeMinimize": function (evt, dlg) { + "beforeMinimize"(evt, dlg) { var label = that.name; label = label.substring(0, 6); that.$el.dialog({ title: label }); }, - "beforeMaximize": function (evt, dlg) { + "beforeMaximize"(evt, dlg) { var divheight = that.size.height; var divwidth = that.size.width; that.previousMaxSize = { width: divwidth, height: divheight }; }, - "minimize": function (evt, dlg) { + "minimize"(evt, dlg) { that.$el.dialog({ title: that.name }); }, - "maximize": function (evt, dlg) { + "maximize"(evt, dlg) { that.setTrasparentBackground(false); $(this).trigger('resizeEnd'); var divheight = $(window).height(); @@ -557,7 +550,7 @@ define(function (require) { that.$el.dialog({ height: divheight, width: divwidth }); that.maximize = true; }, - "restore": function (evt, dlg) { + "restore"(evt, dlg) { if (that.maximize) { that.setSize(that.previousMaxSize.height, that.previousMaxSize.width); $(this).trigger('restored', [that.id]); @@ -567,7 +560,7 @@ define(function (require) { that.maximize = false; that.collapsed = false; }, - "collapse": function (evt, dlg) { + "collapse"(evt, dlg) { that.collapsed = true; } }); @@ -617,49 +610,38 @@ define(function (require) { that.maximize = true; } }); - }, - - /** - * Renders the widget dialog window - */ - render: function () { - return ( -
                - -
                - ) - }, + } /** * Register event with widget * * @command registerEvent(event) */ - registerEvent: function (event, callback) { + registerEvent(event, callback) { this.registeredEvents.push({ id: event, callback: callback }); - }, + } /** * Unregister event with widget * * @command unregisterEvent(event) */ - unregisterEvent: function (event) { + unregisterEvent(event) { this.registeredEvents = _.reject(this.registeredEvents, function (el) { return el.id === event }); - }, + } - getHelp: function () { + getHelp() { return '### Inline help not yet available for this widget! \n\n' + 'Try the online documentation instead.'; - }, + } - setController: function (controller) { + setController(controller) { this.controller = controller; - }, + } - showHistoryIcon: function (show) { + showHistoryIcon(show) { var that = this; if (show && this.$el.parent().find(".history-icon").length == 0) { this.addButtonToTitleBar($("
                ").click(function (event) { @@ -671,8 +653,22 @@ define(function (require) { this.$el.parent().find(".history-icon").remove(); } } - }); - return Widget; + /** + * Renders the widget dialog window + */ + render() { + /*return ( +
                + this._component = c}/> +
                + )*/ + return
                {super.render()}
                + } + }; } }) From ab0668ace5a1234b526d2a869e2918a40cddcabb Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 19 Apr 2017 10:04:40 -0700 Subject: [PATCH 096/339] stores views as json in downloaded project instead of string, works for persistence but missing for non persistence default data projects. fixes bug with axes labels not coming up when plotting from CTRL panel --- .../widgets/plot/controllers/PlotsController.js | 9 +++++++-- src/main/webapp/js/geppettoProject/ProjectFactory.js | 5 ++--- .../webapp/js/geppettoProject/model/ExperimentNode.js | 8 +++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index f01a4d956..5bc1bfe73 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -191,6 +191,7 @@ define(function(require) { var widget = G.addWidget(0); widget.plotData(inst).setName(path); this.updateLegend(widget, inst, projectId, experimentId); + widget.updateAxis(path); } } else { var cb = function(){ @@ -199,7 +200,9 @@ define(function(require) { plotWidget.plotData(i); self.updateLegend(plotWidget, i, projectId, experimentId); } else { - G.addWidget(0).plotData(i).setName(path); + var plot = G.addWidget(0); + plot.plotData(i).setName(path); + plot.updateAxis(path); } }; // trigger get experiment data with projectId, experimentId and path, and callback to plot @@ -217,7 +220,9 @@ define(function(require) { plotWidget.plotXYData(i, t); self.updateLegend(plotWidget, i, projectId, experimentId); } else { - G.addWidget(0).plotXYData(i, t).setName(path); + var plot = G.addWidget(0); + plot.plotXYData(i, t).setName(path); + plot.updateAxis(path); } }; diff --git a/src/main/webapp/js/geppettoProject/ProjectFactory.js b/src/main/webapp/js/geppettoProject/ProjectFactory.js index 8089b5c7d..650cda905 100644 --- a/src/main/webapp/js/geppettoProject/ProjectFactory.js +++ b/src/main/webapp/js/geppettoProject/ProjectFactory.js @@ -23,7 +23,7 @@ define(function (require) { name: project.name, type: project.type, id: project.id, - view: (project.view != undefined) ? project.view.viewString : undefined, + view: (project.view != undefined) ? project.view.view : undefined, _metaType: GEPPETTO.Resources.PROJECT_NODE }); @@ -57,10 +57,9 @@ define(function (require) { lastModified: node.lastModified, status: node.status, script: node.script, - view: (node.view != undefined) ? node.view.viewString : undefined, + view: (node.view != undefined) ? node.view: undefined, _metaType: GEPPETTO.Resources.EXPERIMENT_NODE, }); - if(node.details!=null || undefined){ var details = JSON.parse(node.details); diff --git a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js index b56f8aa9e..dede4650a 100644 --- a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js +++ b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js @@ -483,11 +483,13 @@ define(['backbone'], function(require) { * @returns {exports.view|{}} */ getView: function(){ - var viewsString = this.get('view'); + var viewObject = this.get('view'); var views = undefined; - if(viewsString != undefined){ - views = JSON.parse(viewsString); + if(viewObject != undefined){ + if(viewObject.view!=undefined){ + views = viewObject.view.members; + } } return views; From c79a04f9b591a223b2252cfbc09d12afc692892e Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 19 Apr 2017 10:15:00 -0700 Subject: [PATCH 097/339] more bug fixing for plot widget axes --- .../js/components/widgets/plot/controllers/PlotsController.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index 5bc1bfe73..95e75bca2 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -187,6 +187,7 @@ define(function(require) { //plotWidget.updateLegends(" [" + window.Project.getActiveExperiment().name + "]"); plotWidget.plotData(inst); this.updateLegend(plotWidget, inst, projectId, experimentId); + plotWidget.updateAxis(inst.getInstancePath()); } else { var widget = G.addWidget(0); widget.plotData(inst).setName(path); @@ -199,6 +200,7 @@ define(function(require) { if(plotWidget != undefined){ plotWidget.plotData(i); self.updateLegend(plotWidget, i, projectId, experimentId); + plotWidget.updateAxis(i.getInstancePath()); } else { var plot = G.addWidget(0); plot.plotData(i).setName(path); @@ -222,7 +224,6 @@ define(function(require) { } else { var plot = G.addWidget(0); plot.plotXYData(i, t).setName(path); - plot.updateAxis(path); } }; From 0ae4aab78cdbc31d88c266fc4816d790286fec85 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 19 Apr 2017 11:21:40 -0700 Subject: [PATCH 098/339] reduces time plot widget background is white during download of image, almost instantenous --- src/main/webapp/js/components/widgets/plot/Plot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 767abea70..ba14c0c6b 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -501,7 +501,7 @@ define(function (require) { self.plotOptions.margin.r=defaultOptions.margin.r; Plotly.relayout(self.plotDiv,self.plotOptions); }; - setTimeout(reset, 100); + setTimeout(reset, 10); }, /** From aa40eda23548c1aa250638674410d34bb684cd1d Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 19 Apr 2017 11:21:41 -0700 Subject: [PATCH 099/339] reduces time plot widget background is white during download of image, almost instantenous From e9356a3adf9c07f7a4a4b46edf1bf175737fff1f Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Wed, 19 Apr 2017 20:30:15 +0100 Subject: [PATCH 100/339] More work on canvas extraction --- .../components/interface/3dCanvas/Canvas.css | 5 +- .../components/interface/3dCanvas/Canvas.js | 375 +++++++++++++++--- .../interface/3dCanvas/SceneFactory.js | 5 +- .../interface/3dCanvas/TrackballControls.js | 11 +- 4 files changed, 336 insertions(+), 60 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.css b/src/main/webapp/js/components/interface/3dCanvas/Canvas.css index 5a5c99d26..9226995b4 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.css +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.css @@ -1,3 +1,4 @@ -.canvas{ - color:white; +.canvas { + height: 100%; + width: 100%; } \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 743f9c4fa..e90007e57 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -1,4 +1,4 @@ -define(function(require) { +define(function (require) { var link = document.createElement("link"); link.type = "text/css"; @@ -9,7 +9,7 @@ define(function(require) { var React = require('react'); var THREE = require('three'); - var isWebglEnabled = require('detector-webgl'); + var isWebglEnabled = require('detector-webgl'); require('./TrackballControls'); require('./OBJLoader'); var THREEx = require('./THREEx.KeyboardState'); @@ -23,11 +23,11 @@ define(function(require) { THREE.RenderPass = require('imports?THREE=three!exports?THREE.RenderPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/RenderPass'); THREE.BloomPass = require('imports?THREE=three!exports?THREE.BloomPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/BloomPass'); THREE.ShaderPass = require('imports?THREE=three!exports?THREE.ShaderPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/ShaderPass'); - THREE.FilmPass = require('imports?THREE=three!exports?THREE.FilmPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/FilmPass'); - var SceneFactory = require('./SceneFactory'); + THREE.FilmPass = require('imports?THREE=three!exports?THREE.FilmPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/FilmPass'); + var SceneFactory = require('./SceneFactory'); var canvasComponent = React.createClass({ - factory: new SceneFactory(this), + factory: null, camera: null, container: null, controls: null, @@ -60,7 +60,7 @@ define(function(require) { backgroundColor: 0x101010, - setupScene: function() { + setupScene: function () { this.scene = new THREE.Scene(); this.visualModelMap = {}; this.meshes = {}; @@ -71,7 +71,7 @@ define(function(require) { /** * Sets up the camera that is used to view the objects in the 3D Scene. */ - setupCamera: function() { + setupCamera: function () { // Camera var SCREEN_WIDTH = $(this.container).width(); var SCREEN_HEIGHT = $(this.container).height(); @@ -90,7 +90,7 @@ define(function(require) { /** * Set up the WebGL Renderer */ - setupRenderer: function() { + setupRenderer: function () { // Reuse a single WebGL renderer. // NOTE: Recreating the renderer causes camera displacement on Chrome OSX. if (!this.canvasCreated) { @@ -105,14 +105,14 @@ define(function(require) { this.canvasCreated = true; }, - configureRenderer: function(shaders) { + configureRenderer: function (shaders) { if (shaders == undefined) { shaders = false; } var color = new THREE.Color(this.backgroundColor); - //this.renderer.setClearColor(color, 1); + this.renderer.setClearColor(color, 1); var width = $(this.container).width(); var height = $(this.container).height(); this.renderer.setPixelRatio(window.devicePixelRatio); @@ -151,7 +151,7 @@ define(function(require) { /** * Light up the scene */ - setupLights: function() { + setupLights: function () { // Lights this.camera.add(new THREE.PointLight(0xffffff, 1.5)); @@ -161,7 +161,7 @@ define(function(require) { * Sets up the controls used by the camera to make it able to zoom and * pan. */ - setupControls: function() { + setupControls: function () { // Controls this.controls = new THREE.TrackballControls(this.camera, this.renderer.domElement); this.controls.noZoom = false; @@ -171,21 +171,22 @@ define(function(require) { /** * Set up the listeners use to detect mouse movement and windoe resizing */ - setupListeners: function() { + setupListeners: function () { if (!this.listenersCreated) { + var that = this; // when the mouse moves, call the given function - this.renderer.domElement.addEventListener('mousedown', function(event) { + this.renderer.domElement.addEventListener('mousedown', function (event) { if (event.button == 0) //only for left click { - if (this.pickingEnabled) { - var intersects = GEPPETTO.getIntersectedObjects(); + if (that.pickingEnabled) { + var intersects = that.getIntersectedObjects(); if (intersects.length > 0) { var selected = ""; var geometryIdentifier = ""; // sort intersects - var compare = function(a, b) { + var compare = function (a, b) { if (a.distance < b.distance) return -1; if (a.distance > b.distance) @@ -243,7 +244,7 @@ define(function(require) { if (selected != "") { - if (this.meshes.hasOwnProperty(selected) || this.splitMeshes.hasOwnProperty(selected)) { + if (that.meshes.hasOwnProperty(selected) || that.splitMeshes.hasOwnProperty(selected)) { if (!GEPPETTO.isKeyPressed("shift")) { GEPPETTO.G.unSelectAll(); } @@ -262,24 +263,294 @@ define(function(require) { } }, false); - var that=this; - this.renderer.domElement.addEventListener('mousemove', function(event) { - that.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - that.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - }, false); + this.renderer.domElement.addEventListener('mousemove', function (event) { + that.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + that.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + }, false); + var dialog=$("[aria-describedby='"+this.props.id+"']"); + dialog.resize(function () { + var container = $(that.container); + var width = dialog.width(); + var height = dialog.height(); + container.height(height); + container.width(width); + that.camera.aspect = (width) / (height); + that.camera.updateProjectionMatrix(); + that.renderer.setSize(width, height); + that.composer.setSize(width, height); + }); - this.container.addEventListener('resize', function() { - var container = $(that.container); - var width = container.width(); - var height = container.height(); + this.listenersCreated = true; + } + }, - that.camera.aspect = (width) / (height); - that.camera.updateProjectionMatrix(); - that.renderer.setSize(width, height); - that.composer.setSize(width, height); - }, false); - this.listenersCreated = true; + /** + * Reset camera for scene. + */ + resetCamera: function () { + this.controls.reset(); + + var aabbMin = null; + var aabbMax = null; + + this.scene.traverse(function (child) { + if (child.hasOwnProperty("geometry")) { + child.geometry.computeBoundingBox(); + + var bb = child.geometry.boundingBox; + bb.translate(child.localToWorld(new THREE.Vector3())); + + // If min and max vectors are null, first values become + // default min and max + if (aabbMin == null && aabbMax == null) { + aabbMin = bb.min; + aabbMax = bb.max; + } + + // Compare other meshes, particles BB's to find min and max + else { + aabbMin.x = Math.min(aabbMin.x, bb.min.x); + aabbMin.y = Math.min(aabbMin.y, bb.min.y); + aabbMin.z = Math.min(aabbMin.z, bb.min.z); + aabbMax.x = Math.max(aabbMax.x, bb.max.x); + aabbMax.y = Math.max(aabbMax.y, bb.max.y); + aabbMax.z = Math.max(aabbMax.z, bb.max.z); + } + } + }); + + if (aabbMin != null && aabbMax != null) { + // Compute world AABB center + this.sceneCenter.x = (aabbMax.x + aabbMin.x) * 0.5; + this.sceneCenter.y = (aabbMax.y + aabbMin.y) * 0.5; + this.sceneCenter.z = (aabbMax.z + aabbMin.z) * 0.5; + + this.updateCamera(aabbMax, aabbMin); + } + }, + + /** + * Update camera with new position and place to lookat + */ + updateCamera: function (aabbMax, aabbMin) { + // Compute world AABB "radius" + var diag = new THREE.Vector3(); + diag = diag.subVectors(aabbMax, aabbMin); + var radius = diag.length() * 0.5; + + this.pointCameraTo(this.sceneCenter); + + // Compute offset needed to move the camera back that much needed to center AABB + var offset = radius / Math.sin(Math.PI / 180.0 * this.camera.fov * 0.5); + + var dir = this.camera.direction.clone(); + dir.multiplyScalar(offset); + + // Store camera position + this.camera.position.addVectors(dir, this.controls.target); + this.camera.updateProjectionMatrix(); + }, + + boundingBox: function (obj) { + if (obj instanceof THREE.Mesh) { + + var geometry = obj.geometry; + geometry.computeBoundingBox(); + return geometry.boundingBox; + + } + + if (obj instanceof THREE.Object3D) { + + var bb = new THREE.Box3(); + for (var i = 0; i < obj.children.length; i++) { + bb.union(this.boundingBox(obj.children[i])); + } + return bb; + } + }, + + shapeCenterOfGravity: function (obj) { + return this.boundingBox(obj).center(); + }, + + /** */ + pointCameraTo: function (node) { + // Refocus camera to the center of the new object + var COG; + if (node instanceof THREE.Vector3) { + COG = node; + } else { + COG = this.shapeCenterOfGravity(node); + } + var v = new THREE.Vector3(); + v.subVectors(COG, this.controls.target); + this.camera.position.addVectors( + this.camera.position, v); + + // retrieve camera orientation + + this.camera.lookAt(COG); + this.controls.target.set(COG.x, COG.y, COG.z); + }, + + /** + * Status of scene, populated or not + * + * @returns {Boolean} True or false depending whether scene is populated + * or not + */ + isScenePopulated: function () { + return !(_.isEmpty(this.visualModelMap)); + }, + + /** + * Has canvas been created? + * + * @returns {Boolean] True or false if canvas has been created or not + */ + isCanvasCreated: function () { + return this.canvasCreated; + }, + + /** + * Sets up the HUD display with the scene stat's fps. + */ + setupStats: function () { + // Stats + if ($("#stats").length == 0) { + if (VARS != null) { + this.stats = new Stats(); + this.stats.domElement.style.float = 'right'; + this.stats.domElement.style.position = 'absolute'; + this.stats.domElement.style.top = '60px'; + this.stats.domElement.style.right = '5px'; + this.stats.domElement.style.zIndex = 100; + $('#controls').append(this.stats.domElement); + } + } + }, + + /** + * Displays HUD for FPS stats + */ + toggleStats: function (mode) { + if (mode) { + if ($("#stats").length == 0) { + this.setupStats(); + } else { + $("#stats").show(); + } + } else { + $("#stats").hide(); + } + }, + + + /** + * Adds debug axis to the scene + */ + setupAxis: function () { + // To use enter the axis length + this.scene.add(new THREE.AxisHelper(200)); + }, + + /** + * Renders objects in the scene + */ + renderCanvas: function () { + this.renderer.clear(); + this.composer.render(0.01); + }, + + /** + * Returns intersected objects from mouse click + * + * @returns {Array} a list of objects intersected by the current mouse coordinates + */ + getIntersectedObjects: function () { + // create a Ray with origin at the mouse position and direction into th scene (camera direction) + var vector = new THREE.Vector3(this.mouse.x, this.mouse.y, 1); + vector.unproject(this.camera); + + var raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize()); + + var visibleChildren = []; + this.scene.traverse(function (child) { + if (child.visible && !(child.clickThrough == true)) { + if (child.geometry != null && child.geometry != undefined) { + child.geometry.computeBoundingBox(); + visibleChildren.push(child); + } + } + }); + + // returns an array containing all objects in the scene with which the ray intersects + return raycaster.intersectObjects(visibleChildren); + }, + + + /** + * @param x + * @param y + */ + incrementCameraPan: function (x, y) { + this.controls.incrementPanEnd(x, y); + }, + + /** + * @param x + * @param y + * @param z + */ + incrementCameraRotate: function (x, y, z) { + this.controls.incrementRotationEnd(x, y, z); + }, + + /** + * @param z + */ + incrementCameraZoom: function (z) { + this.controls.incrementZoomEnd(z); + }, + + /** + * @param x + * @param y + * @param z + */ + setCameraPosition: function (x, y, z) { + this.controls.setPosition(x, y, z); + }, + + /** + * @param rx + * @param ry + * @param rz + * @param radius + */ + setCameraRotation: function (rx, ry, rz, radius) { + this.controls.setRotation(rx, ry, rz, radius); + }, + + + animate: function () { + this.debugUpdate = this.needsUpdate; + // so that we log only the cycles when we are updating the scene + + this.controls.update(); + + this.isAnimated = true; + requestAnimationFrame(this.animate); + this.renderCanvas(); + + if (this.stats) { + this.stats.update(); + } + + if (this.debugUpdate) { + GEPPETTO.log(GEPPETTO.Resources.UPDATE_FRAME_END); } }, @@ -288,23 +559,31 @@ define(function(require) { return false; }, - componentDidMount: function() { - this.container = $("#" + this.props.id + "_component").get(0); - this.setupScene(); - this.setupCamera(); - this.setupRenderer(); - this.setupLights(); - this.setupControls(); - this.setupListeners(); - this.initialised = true; + componentDidMount: function () { + if (!isWebglEnabled) { + Detector.addGetWebGLMessage(); + } else { + this.factory = new SceneFactory(this); + var containerSelector = $("#" + this.props.id + "_component"); + containerSelector.height(containerSelector.parent().height()); + containerSelector.width(containerSelector.parent().width()); + this.container = containerSelector.get(0); + this.setupScene(); + this.setupCamera(); + this.setupRenderer(); + this.setupLights(); + this.setupControls(); + this.setupListeners(); + this.animate(); + } }, - render: function () { - return ( -
                -
                - ) - } + render: function () { + return ( +
                +
                + ) + } }); return canvasComponent; }); \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js b/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js index a40fb106d..04376e988 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js +++ b/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js @@ -1,5 +1,7 @@ define(['jquery'], function () { + var THREE = require('three'); + function SceneFactory(viewer) { this.viewer = viewer; @@ -24,6 +26,7 @@ define(['jquery'], function () { buildScene: function (instances) { this.traverseInstances(instances); this.viewer.scene.updateMatrixWorld(true); + this.viewer.resetCamera(); }, @@ -37,7 +40,7 @@ define(['jquery'], function () { this.checkVisualInstance(instances[g]); } if(updateCamera){ - G.resetCamera(); + this.viewer.resetCamera(); } }, diff --git a/src/main/webapp/js/components/interface/3dCanvas/TrackballControls.js b/src/main/webapp/js/components/interface/3dCanvas/TrackballControls.js index fac2aa3a7..fcfb0041a 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/TrackballControls.js +++ b/src/main/webapp/js/components/interface/3dCanvas/TrackballControls.js @@ -163,19 +163,12 @@ THREE.TrackballControls = function ( object, domElement ) { var p = _this.object.position.toArray(); - GEPPETTO.Console.executeImplicitCommand('G.setCameraPosition('+p[0].toFixed(places)+ - ','+p[1].toFixed(places)+ - ','+p[2].toFixed(places)+ - ')'); + GEPPETTO.Console.executeImplicitCommand('G.setCameraPosition('+p[0].toFixed(places)+','+p[1].toFixed(places)+','+p[2].toFixed(places)+')'); var u = _this.object.rotation.toArray(); var l = _eye.length(); - GEPPETTO.Console.executeImplicitCommand('G.setCameraRotation('+u[0].toFixed(places)+ - ','+u[1].toFixed(places)+ - ','+u[2].toFixed(places)+ - ','+l.toFixed(places)+ - ')'); + GEPPETTO.Console.executeImplicitCommand('G.setCameraRotation('+u[0].toFixed(places)+','+u[1].toFixed(places)+','+u[2].toFixed(places)+','+l.toFixed(places)+')'); _this.cameraByConsoleLock = true; _this.cameraChanged = false; From 0ef0771d48cfad6a6caa3513a0c9e68d3649fc71 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 19 Apr 2017 16:30:31 -0700 Subject: [PATCH 101/339] sets view for default too --- .../geppettoProject/ExperimentsController.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index 3edb55da4..a17e5e3b9 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -175,15 +175,7 @@ define(function (require) { setView: function (view) { var activeExperiment = (window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined); // go to server to persist only if experiment is persisted - if(Project.persisted && GEPPETTO.UserController.persistence){ - var parameters = {}; - var experimentId = activeExperiment ? Project.getActiveExperiment().getId() : -1; - parameters["experimentId"] = experimentId; - parameters["projectId"] = Project.getId(); - parameters["view"] = JSON.stringify(view); - - GEPPETTO.MessageSocket.send("set_experiment_view", parameters); - } else if(GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ + if(GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ // store view in local storage for this project/experiment/user if(!activeExperiment){ // no experiment active - save at project level @@ -193,6 +185,14 @@ define(function (require) { localStorage.setItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId()), JSON.stringify(view)); } } + + var parameters = {}; + var experimentId = activeExperiment ? Project.getActiveExperiment().getId() : -1; + parameters["experimentId"] = experimentId; + parameters["projectId"] = Project.getId(); + parameters["view"] = JSON.stringify(view); + + GEPPETTO.MessageSocket.send("set_experiment_view", parameters); }, watchVariables: function (variables, watch) { From 2620ae46ac63b63f84e214413356564cc12729db Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 20 Apr 2017 00:57:27 +0100 Subject: [PATCH 102/339] More work on 3d canvas --- .../components/interface/3dCanvas/Canvas.js | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index e90007e57..d87af857e 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -112,7 +112,7 @@ define(function (require) { } var color = new THREE.Color(this.backgroundColor); - this.renderer.setClearColor(color, 1); + //this.renderer.setClearColor(color, 1); var width = $(this.container).width(); var height = $(this.container).height(); this.renderer.setPixelRatio(window.devicePixelRatio); @@ -263,23 +263,26 @@ define(function (require) { } }, false); - this.renderer.domElement.addEventListener('mousemove', function (event) { - that.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - that.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - }, false); - var dialog=$("[aria-describedby='"+this.props.id+"']"); - dialog.resize(function () { - var container = $(that.container); - var width = dialog.width(); - var height = dialog.height(); - container.height(height); - container.width(width); - that.camera.aspect = (width) / (height); + + + $("#" + this.props.id).on("dialogresizestop", function (event, ui) { + var height=ui.size.height-40; + var width=ui.size.width-30; + $(that.container).height(height); + $(that.container).width(width); + that.camera.aspect = width / height; that.camera.updateProjectionMatrix(); that.renderer.setSize(width, height); that.composer.setSize(width, height); + }); + this.renderer.domElement.addEventListener('mousemove', function (event) { + that.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + that.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + }, false); + + this.listenersCreated = true; } }, @@ -565,8 +568,8 @@ define(function (require) { } else { this.factory = new SceneFactory(this); var containerSelector = $("#" + this.props.id + "_component"); - containerSelector.height(containerSelector.parent().height()); - containerSelector.width(containerSelector.parent().width()); + containerSelector.height(containerSelector.parent().height()-40); + containerSelector.width(containerSelector.parent().width()-30); this.container = containerSelector.get(0); this.setupScene(); this.setupCamera(); From ab4a25ce6a74ab9e577bcf25209d00864c4b6e73 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 19 Apr 2017 17:42:13 -0700 Subject: [PATCH 103/339] bug fix --- .../geppettoProject/ExperimentsController.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index a17e5e3b9..48c384504 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -174,8 +174,11 @@ define(function (require) { */ setView: function (view) { var activeExperiment = (window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined); + var setView = false; // go to server to persist only if experiment is persisted - if(GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ + if(Project.persisted && GEPPETTO.UserController.persistence){ + setView =true; + } else if(GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ // store view in local storage for this project/experiment/user if(!activeExperiment){ // no experiment active - save at project level @@ -184,15 +187,18 @@ define(function (require) { // experiment active - save at experiment level localStorage.setItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId()), JSON.stringify(view)); } + setView =true; } - var parameters = {}; - var experimentId = activeExperiment ? Project.getActiveExperiment().getId() : -1; - parameters["experimentId"] = experimentId; - parameters["projectId"] = Project.getId(); - parameters["view"] = JSON.stringify(view); - - GEPPETTO.MessageSocket.send("set_experiment_view", parameters); + if(setView){ + var parameters = {}; + var experimentId = activeExperiment ? Project.getActiveExperiment().getId() : -1; + parameters["experimentId"] = experimentId; + parameters["projectId"] = Project.getId(); + parameters["view"] = JSON.stringify(view); + + GEPPETTO.MessageSocket.send("set_experiment_view", parameters); + } }, watchVariables: function (variables, watch) { From 5584a2142758a7a34668507b25799507fe496fe5 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Thu, 20 Apr 2017 02:38:52 +0100 Subject: [PATCH 104/339] add camera controls to canvas widget, change this.id for this.props.id which fixes a bunch of stuff --- .../components/interface/3dCanvas/Canvas.js | 4 ++ .../webapp/js/components/widgets/NewWidget.js | 50 +++++++++---------- .../components/controls/camera-controls.less | 28 ++++++----- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index d87af857e..3a7c2d273 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -579,11 +579,15 @@ define(function (require) { this.setupListeners(); this.animate(); } + + //Camera controls initialization + GEPPETTO.ComponentFactory.addComponent('CAMERACONTROLS', {}, document.getElementById(this.props.id).getElementsByClassName("camera-controls")[0]); }, render: function () { return (
                +
                ) } diff --git a/src/main/webapp/js/components/widgets/NewWidget.js b/src/main/webapp/js/components/widgets/NewWidget.js index c92df9190..5200af5dd 100644 --- a/src/main/webapp/js/components/widgets/NewWidget.js +++ b/src/main/webapp/js/components/widgets/NewWidget.js @@ -218,7 +218,7 @@ define(function (require) { * @returns {String} - ID of widget */ getId() { - return this.id; + return this.props.id; } /** @@ -341,21 +341,21 @@ define(function (require) { updateNavigationHistoryBar() { var disabled = "arrow-disabled"; if (this.getItems(this.controller.history, "controller.history").length <= 1) { - if (!$("#" + this.id + "-left-nav").hasClass(disabled)) { - $("#" + this.id + "-left-nav").addClass(disabled); - $("#" + this.id + "-right-nav").addClass(disabled); + if (!$("#" + this.props.id + "-left-nav").hasClass(disabled)) { + $("#" + this.props.id + "-left-nav").addClass(disabled); + $("#" + this.props.id + "-right-nav").addClass(disabled); } } else { - if ($("#" + this.id + "-left-nav").hasClass(disabled)) { - $("#" + this.id + "-left-nav").removeClass(disabled); - $("#" + this.id + "-right-nav").removeClass(disabled); + if ($("#" + this.props.id + "-left-nav").hasClass(disabled)) { + $("#" + this.props.id + "-left-nav").removeClass(disabled); + $("#" + this.props.id + "-right-nav").removeClass(disabled); } } } showHistoryNavigationBar(show) { - var leftNav = $("#" + this.id + "-left-nav"); - var rightNav = $("#" + this.id + "-right-nav"); + var leftNav = $("#" + this.props.id + "-left-nav"); + var rightNav = $("#" + this.props.id + "-right-nav"); if (show) { if ((leftNav.length == 0) && (rightNav.length == 0)) { @@ -366,16 +366,16 @@ define(function (require) { } var that = this; - var button = $("
                " + - "
                ").click(function (event) { + var button = $("
                " + + "
                ").click(function (event) { var historyItems = that.getItems(that.controller.history, "controller.history"); var item; - if (event.target.id == (that.id + "-left-nav") || (that.id + "-right-nav")) { + if (event.target.id == (that.props.id + "-left-nav") || (that.props.id + "-right-nav")) { that.executedAction = historyItems.length - 1; } item = historyItems[that.executedAction].action[0]; GEPPETTO.Console.executeImplicitCommand(item); - $("#" + that.id).parent().find(".ui-dialog-title").html(historyItems[that.executedAction].label); + $("#" + that.props.id).parent().find(".ui-dialog-title").html(historyItems[that.executedAction].label); event.stopPropagation(); }); @@ -413,7 +413,7 @@ define(function (require) { var that = this; this.addButtonToTitleBar($("
                ").click(function () { GEPPETTO.ComponentFactory.addComponent('MDMODAL', { - title: that.id.slice(0, -1) + ' help', + title: that.props.id.slice(0, -1) + ' help', content: that.getHelp(), show: true }, document.getElementById("modal-region")); @@ -522,27 +522,27 @@ define(function (require) { "collapse": "fa fa-chevron-circle-up", "restore": "fa fa-window-restore", }, - "load"(evt, dlg) { - var icons = $("#" + that.id).parent().find(".ui-icon"); + "load" : function(evt, dlg) { + var icons = $("#" + that.props.id).parent().find(".ui-icon"); for (var i = 0; i < icons.length; i++) { //remove text from span added by vendor library $(icons[i]).text(""); } }, - "beforeMinimize"(evt, dlg) { + "beforeMinimize" : function(evt, dlg) { var label = that.name; label = label.substring(0, 6); that.$el.dialog({ title: label }); }, - "beforeMaximize"(evt, dlg) { + "beforeMaximize" : function(evt, dlg) { var divheight = that.size.height; var divwidth = that.size.width; that.previousMaxSize = { width: divwidth, height: divheight }; }, - "minimize"(evt, dlg) { + "minimize" : function(evt, dlg) { that.$el.dialog({ title: that.name }); }, - "maximize"(evt, dlg) { + "maximize" : function(evt, dlg) { that.setTrasparentBackground(false); $(this).trigger('resizeEnd'); var divheight = $(window).height(); @@ -550,17 +550,17 @@ define(function (require) { that.$el.dialog({ height: divheight, width: divwidth }); that.maximize = true; }, - "restore"(evt, dlg) { + "restore" : function(evt, dlg) { if (that.maximize) { that.setSize(that.previousMaxSize.height, that.previousMaxSize.width); - $(this).trigger('restored', [that.id]); + $(this).trigger('restored', [that.props.id]); } that.setTrasparentBackground(that.previousMaxTransparency); $(this).trigger('resizeEnd'); that.maximize = false; that.collapsed = false; }, - "collapse"(evt, dlg) { + "collapse" : function(evt, dlg) { that.collapsed = true; } }); @@ -577,7 +577,7 @@ define(function (require) { //remove the jQuery UI icon dialogParent.find("button.ui-dialog-titlebar-close").html(""); - dialogParent.find("button").append(""); + dialogParent.find(".ui-dialog-titlebar").find("button").append(""); //Take focus away from close button @@ -598,7 +598,7 @@ define(function (require) { if (registeredItem != null || registeredItem != undefined) { var label = registeredItem["label"]; that.title = label; - $("#" + that.id).parent().find(".ui-dialog-title").html(that.title); + $("#" + that.props.id).parent().find(".ui-dialog-title").html(that.title); } }); diff --git a/src/main/webapp/style/less/components/controls/camera-controls.less b/src/main/webapp/style/less/components/controls/camera-controls.less index 2b52eec7e..fa606aefc 100644 --- a/src/main/webapp/style/less/components/controls/camera-controls.less +++ b/src/main/webapp/style/less/components/controls/camera-controls.less @@ -7,6 +7,8 @@ .position-toolbar { + position: relative; + .rotate90 { .rotate(90deg) } @@ -23,59 +25,59 @@ .squareB { width: 24px; height: 24px; - position: fixed; + position: absolute; padding: 1px; } .pan-left { left: 10px; - top: 87px; + top: 37px; } .pan-top { left: 37px; - top: 60px; + top: 10px; } .pan-home { left: 37px; - top: 87px; + top: 37px; } .pan-right { left: 64px; - top: 87px; + top: 37px; } .pan-bottom { left: 37px; - top: 114px; + top: 64px; } .rotate-left { left: 10px; - top: 182px; + top: 132px; } .rotate-top { left: 37px; - top: 155px; + top: 105px; } .rotate-home { left: 37px; - top: 182px; + top: 132px; } .rotate-right { left: 64px; - top: 182px; + top: 132px; } .rotate-bottom { left: 37px; - top: 209px; + top: 159px; } .zoom-out { left: 37px; - top: 278px; + top: 228px; } .zoom-in{ left: 37px; - top: 250px; + top: 200px; } } \ No newline at end of file From 6092c78dccc7c1c966bf8f25b26d0cc66bf3aeec Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Thu, 20 Apr 2017 12:38:32 +0100 Subject: [PATCH 105/339] making things in a react way --- src/main/webapp/js/components/interface/3dCanvas/Canvas.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 3a7c2d273..c5193854a 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -26,6 +26,8 @@ define(function (require) { THREE.FilmPass = require('imports?THREE=three!exports?THREE.FilmPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/FilmPass'); var SceneFactory = require('./SceneFactory'); + var CameraControls = require('../cameraControls/CameraControls'); + var canvasComponent = React.createClass({ factory: null, camera: null, @@ -579,15 +581,12 @@ define(function (require) { this.setupListeners(); this.animate(); } - - //Camera controls initialization - GEPPETTO.ComponentFactory.addComponent('CAMERACONTROLS', {}, document.getElementById(this.props.id).getElementsByClassName("camera-controls")[0]); }, render: function () { return (
                -
                +
                ) } From d2837a1e0c87605b625ead40e59ca29c2535c987 Mon Sep 17 00:00:00 2001 From: johnidol Date: Thu, 20 Apr 2017 13:14:31 +0100 Subject: [PATCH 106/339] formatting --- src/main/webapp/js/geppettoProject/ExperimentsController.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index 48c384504..3dfb2a3ea 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -175,9 +175,10 @@ define(function (require) { setView: function (view) { var activeExperiment = (window.Project.getActiveExperiment() != null && window.Project.getActiveExperiment() != undefined); var setView = false; + // go to server to persist only if experiment is persisted if(Project.persisted && GEPPETTO.UserController.persistence){ - setView =true; + setView = true; } else if(GEPPETTO.Main.localStorageEnabled && (typeof(Storage) !== "undefined")){ // store view in local storage for this project/experiment/user if(!activeExperiment){ @@ -187,7 +188,7 @@ define(function (require) { // experiment active - save at experiment level localStorage.setItem("{0}_{1}_view".format(Project.getId(), window.Project.getActiveExperiment().getId()), JSON.stringify(view)); } - setView =true; + setView = true; } if(setView){ From 6e8ecef0648c5dfe79d3cebc81b66d38829fa343 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 20 Apr 2017 13:15:03 +0100 Subject: [PATCH 107/339] Camera controls linked to proper viewer. Still to be improved. --- .../components/interface/3dCanvas/Canvas.js | 3 +-- .../cameraControls/CameraControls.js | 24 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index c5193854a..90533c24b 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -61,7 +61,6 @@ define(function (require) { pickingEnabled: true, // flag to enable disable 3d picking backgroundColor: 0x101010, - setupScene: function () { this.scene = new THREE.Scene(); this.visualModelMap = {}; @@ -586,7 +585,7 @@ define(function (require) { render: function () { return (
                - +
                ) } diff --git a/src/main/webapp/js/components/interface/cameraControls/CameraControls.js b/src/main/webapp/js/components/interface/cameraControls/CameraControls.js index c6fdb730d..18629f887 100644 --- a/src/main/webapp/js/components/interface/cameraControls/CameraControls.js +++ b/src/main/webapp/js/components/interface/cameraControls/CameraControls.js @@ -38,51 +38,51 @@ define(function(require) { var CameraControls = React.createClass({ panLeft: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraPan(-0.01, 0)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraPan(-0.01, 0)'); }, panRight: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraPan(0.01, 0)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraPan(0.01, 0)'); }, panUp: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraPan(0, -0.01)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraPan(0, -0.01)'); }, panDown: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraPan(0, 0.01)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraPan(0, 0.01)'); }, rotateUp: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraRotate(0, 0.01)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraRotate(0, 0.01)'); }, rotateDown: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraRotate(0, -0.01)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraRotate(0, -0.01)'); }, rotateLeft: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraRotate(-0.01, 0)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraRotate(-0.01, 0)'); }, rotateRight: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraRotate(0.01, 0)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraRotate(0.01, 0)'); }, rotate: function() { - GEPPETTO.Console.executeImplicitCommand('G.autoRotate()'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.autoRotate()'); }, cameraHome: function() { - GEPPETTO.Console.executeImplicitCommand('G.resetCamera()'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.resetCamera()'); }, zoomIn: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraZoom(-0.01)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraZoom(-0.01)'); }, zoomOut: function() { - GEPPETTO.Console.executeImplicitCommand('G.incrementCameraZoom(+0.01)'); + GEPPETTO.Console.executeImplicitCommand(this.props.viewer+'.incrementCameraZoom(+0.01)'); }, componentDidMount: function() { From 92e1b690b1684750c849da439dbc515ecd3556f1 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Fri, 21 Apr 2017 11:59:25 -0700 Subject: [PATCH 108/339] sets size for tutorials based on what's on .json file --- .../interface/tutorial/TutorialModule.js | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index 1cafe2994..02e4cfc6d 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -139,7 +139,21 @@ define(function (require) { open : function(started){ var p=$(this.__container).parent(); p.show(); - p.effect("shake", {distance:5, times: 3}, 500); + + var self = this; + var callback = function () { + var width =self.getActiveTutorial()["width"]; + var height =self.getActiveTutorial()["height"]; + if(height!=undefined){ + p.height(height+"px"); + } + if(width!=undefined){ + p.width(width+"px"); + } + }; + + p.effect("shake", {distance:5, times: 3}, 500, callback); + }, setTutorial : function(tutorialURL){ @@ -225,7 +239,7 @@ define(function (require) { dialog.find("div.ui-dialog-titlebar").prepend(button); $(button).addClass("widget-title-bar-button"); $(this.__container).css("overflow","scroll"); - $(this.__container).css("height","93%"); + //$(this.__container).css("height","93%"); } }, @@ -317,11 +331,14 @@ define(function (require) { var lastStepLabel = (this.state.currentStep == activeTutorial.steps.length-1)?"Restart":""; var cookieClass=this.state.currentStep==0?"checkbox-inline cookieTutorial":"hide"; - if(activeTutorial.height!=undefined){ - dialog.height(activeTutorial.height+"px"); + var width =this.getActiveTutorial()["width"]; + var height =this.getActiveTutorial()["height"]; + + if(height!=undefined){ + dialog.height(height+"px"); } - if(activeTutorial.width!=undefined){ - dialog.width(activeTutorial.width+"px"); + if(width!=undefined){ + dialog.width(width+"px"); } return
                From e988d8987c378310bae6b9e46c11b2a36799f04d Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 24 Apr 2017 13:12:37 -0700 Subject: [PATCH 109/339] resizes properly when switching tutorials --- .../webapp/extensions/extensionsConfiguration.json | 4 ++-- .../js/components/interface/tutorial/TutorialModule.js | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/extensions/extensionsConfiguration.json b/src/main/webapp/extensions/extensionsConfiguration.json index 16982d7eb..f4b59e66a 100644 --- a/src/main/webapp/extensions/extensionsConfiguration.json +++ b/src/main/webapp/extensions/extensionsConfiguration.json @@ -1,6 +1,6 @@ { - "geppetto-default/ComponentsInitialization": true, - "geppetto-osb/ComponentsInitialization": false, + "geppetto-default/ComponentsInitialization": false, + "geppetto-osb/ComponentsInitialization": true, "geppetto-vfb/ComponentsInitialization": false, "geppetto-neuron/ComponentsInitialization": false } diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index 02e4cfc6d..ee8c46869 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -146,9 +146,11 @@ define(function (require) { var height =self.getActiveTutorial()["height"]; if(height!=undefined){ p.height(height+"px"); + $(self.__container).css("height",height+"px"); } if(width!=undefined){ p.width(width+"px"); + $(self.__container).css("width",width+"px"); } }; @@ -330,15 +332,17 @@ define(function (require) { var lastStep = this.state.currentStep == activeTutorial.steps.length-1; var lastStepLabel = (this.state.currentStep == activeTutorial.steps.length-1)?"Restart":""; var cookieClass=this.state.currentStep==0?"checkbox-inline cookieTutorial":"hide"; - + var width =this.getActiveTutorial()["width"]; - var height =this.getActiveTutorial()["height"]; - + var height =this.getActiveTutorial()["height"]; + if(height!=undefined){ dialog.height(height+"px"); + $(this.__container).css("height",height+"px"); } if(width!=undefined){ dialog.width(width+"px"); + $(this.__container).css("width",width+"px"); } return
                From 3d0f84a4ac5ae0754ff6894fbaac7fc14d34c6f5 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 25 Apr 2017 10:50:23 -0700 Subject: [PATCH 110/339] fix script url path --- .../controllers/ConnectionHandler.java | 984 +++++++----------- 1 file changed, 380 insertions(+), 604 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index 59bf1fa6f..6a7cde0f4 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -63,14 +63,14 @@ import com.google.gson.JsonParseException; /** - * Class that handles the Web Socket connections the servlet is receiving. FIXME: REMOVE ALL THE MANUAL CONSTRUCTION OF JSON STRINGS, USE GSON INSTEAD + * Class that handles the Web Socket connections the servlet is receiving. + * FIXME: REMOVE ALL THE MANUAL CONSTRUCTION OF JSON STRINGS, USE GSON INSTEAD * * @author Jesus R. Martinez (jesus@metacell.us) * @author matteocantarelli * */ -public class ConnectionHandler implements IGeppettoManagerCallbackListener -{ +public class ConnectionHandler implements IGeppettoManagerCallbackListener { private static Log logger = LogFactory.getLog(ConnectionHandler.class); @@ -85,15 +85,17 @@ public class ConnectionHandler implements IGeppettoManagerCallbackListener private IGeppettoProject geppettoProject; private String urlBase = ""; + /** * @param websocketConnection * @param geppettoManager */ - protected ConnectionHandler(WebsocketConnection websocketConnection, IGeppettoManager geppettoManager) - { + protected ConnectionHandler(WebsocketConnection websocketConnection, IGeppettoManager geppettoManager) { this.websocketConnection = websocketConnection; - // FIXME This is extremely ugly, a session based geppetto manager is autowired in the websocketconnection - // but a session bean cannot travel outside a conenction thread so a new one is instantiated and initialised + // FIXME This is extremely ugly, a session based geppetto manager is + // autowired in the websocketconnection + // but a session bean cannot travel outside a conenction thread so a new + // one is instantiated and initialised this.geppettoManager = new GeppettoManager(geppettoManager); this.geppettoManager.setSimulationListener(this); } @@ -102,23 +104,16 @@ protected ConnectionHandler(WebsocketConnection websocketConnection, IGeppettoMa * @param requestID * @param projectId */ - public void loadProjectFromId(String requestID, long projectId, long experimentId) - { + public void loadProjectFromId(String requestID, long projectId, long experimentId) { IGeppettoDataManager dataManager = DataManagerHelper.getDataManager(); - try - { + try { IGeppettoProject geppettoProject = dataManager.getGeppettoProjectById(projectId); - if(geppettoProject == null) - { + if (geppettoProject == null) { websocketConnection.sendMessage(requestID, OutboundMessages.ERROR_LOADING_PROJECT, "Project not found"); + } else { + loadGeppettoProject(requestID, geppettoProject, experimentId, this.urlBase); } - else - { - loadGeppettoProject(requestID, geppettoProject, experimentId,this.urlBase); - } - } - catch(NumberFormatException e) - { + } catch (NumberFormatException e) { websocketConnection.sendMessage(requestID, OutboundMessages.ERROR_LOADING_PROJECT, ""); } } @@ -127,21 +122,19 @@ public void loadProjectFromId(String requestID, long projectId, long experimentI * @param requestID * @param projectContent */ - public void loadProjectFromContent(String requestID, String projectContent) - { - IGeppettoProject geppettoProject = DataManagerHelper.getDataManager().getProjectFromJson(getGson(), projectContent); - loadGeppettoProject(requestID, geppettoProject, -1l,this.urlBase); + public void loadProjectFromContent(String requestID, String projectContent) { + IGeppettoProject geppettoProject = DataManagerHelper.getDataManager().getProjectFromJson(getGson(), + projectContent); + loadGeppettoProject(requestID, geppettoProject, -1l, this.urlBase); } /** * @param requestID * @param urlString */ - public void loadProjectFromURL(String requestID, String urlString) - { + public void loadProjectFromURL(String requestID, String urlString) { URL url; - try - { + try { url = URLReader.getURL(urlString); int index = url.toString().lastIndexOf('/'); String urlBase = url.toString().substring(0, index); @@ -149,9 +142,7 @@ public void loadProjectFromURL(String requestID, String urlString) IGeppettoProject geppettoProject = DataManagerHelper.getDataManager().getProjectFromJson(getGson(), reader); this.urlBase = urlBase; loadGeppettoProject(requestID, geppettoProject, -1l, this.urlBase); - } - catch(IOException e) - { + } catch (IOException e) { error(e, "Could not load geppetto project"); } } @@ -160,48 +151,44 @@ public void loadProjectFromURL(String requestID, String urlString) * @param requestID * @param geppettoProject */ - public void loadGeppettoProject(String requestID, IGeppettoProject geppettoProject, long experimentId, String urlBase) - { - try - { - geppettoManager.loadProject(requestID, geppettoProject,urlBase); + public void loadGeppettoProject(String requestID, IGeppettoProject geppettoProject, long experimentId, + String urlBase) { + try { + geppettoManager.loadProject(requestID, geppettoProject, urlBase); boolean readOnly = true; - if(geppettoProject.isVolatile()){ + if (geppettoProject.isVolatile()) { readOnly = false; - }else{ + } else { List userProjects = geppettoManager.getUser().getGeppettoProjects(); - for(IGeppettoProject p : userProjects){ - if(p.getId() == geppettoProject.getId()){ + for (IGeppettoProject p : userProjects) { + if (p.getId() == geppettoProject.getId()) { readOnly = false; } } } - - + // serialize project prior to sending it to client Gson gson = new Gson(); String projectJSON = gson.toJson(geppettoProject); boolean persisted = geppettoProject.isVolatile(); - String update = "{\"persisted\":" + !persisted + ",\"project\":" + projectJSON + ",\"isReadOnly\":" + readOnly +"}"; + String update = "{\"persisted\":" + !persisted + ",\"project\":" + projectJSON + ",\"isReadOnly\":" + + readOnly + "}"; setConnectionProject(geppettoProject); websocketConnection.sendMessage(requestID, OutboundMessages.PROJECT_LOADED, update); - String geppettoModelJSON = GeppettoSerializer.serializeToJSON(((GeppettoManager) geppettoManager).getRuntimeProject(geppettoProject).getGeppettoModel(), true); + String geppettoModelJSON = GeppettoSerializer.serializeToJSON( + ((GeppettoManager) geppettoManager).getRuntimeProject(geppettoProject).getGeppettoModel(), true); websocketConnection.sendMessage(requestID, OutboundMessages.GEPPETTO_MODEL_LOADED, geppettoModelJSON); - if(experimentId != -1) - { + if (experimentId != -1) { loadExperiment(requestID, experimentId, geppettoProject.getId()); - } - else if(geppettoProject.getActiveExperimentId() != -1) - { + } else if (geppettoProject.getActiveExperimentId() != -1) { loadExperiment(requestID, geppettoProject.getActiveExperimentId(), geppettoProject.getId()); } - } - catch(GeppettoInitializationException | GeppettoExecutionException | GeppettoAccessException | IOException e) - { + } catch (GeppettoInitializationException | GeppettoExecutionException | GeppettoAccessException + | IOException e) { error(e, "Could not load geppetto project"); } @@ -210,21 +197,17 @@ else if(geppettoProject.getActiveExperimentId() != -1) /** * @param projectId */ - public void newExperiment(String requestID, long projectId) - { + public void newExperiment(String requestID, long projectId) { IGeppettoProject project = retrieveGeppettoProject(projectId); - try - { + try { IExperiment experiment = geppettoManager.newExperiment(requestID, project); Gson gson = new Gson(); String json = gson.toJson(experiment); websocketConnection.sendMessage(requestID, OutboundMessages.EXPERIMENT_CREATED, json); - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error creating a new experiment"); } @@ -233,13 +216,11 @@ public void newExperiment(String requestID, long projectId) /** * @param projectId */ - public void cloneExperiment(String requestID, long projectId, long experimentID) - { + public void cloneExperiment(String requestID, long projectId, long experimentID) { IGeppettoProject project = retrieveGeppettoProject(projectId); - try - { + try { IExperiment originalExperiment = retrieveExperiment(experimentID, project); IExperiment cloneExperiment = geppettoManager.cloneExperiment(requestID, project, originalExperiment); @@ -247,9 +228,7 @@ public void cloneExperiment(String requestID, long projectId, long experimentID) String json = gson.toJson(cloneExperiment); websocketConnection.sendMessage(requestID, OutboundMessages.EXPERIMENT_CREATED, json); - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error creating a new experiment"); } @@ -260,34 +239,28 @@ public void cloneExperiment(String requestID, long projectId, long experimentID) * @param experimentID * @param projectId */ - public void loadExperiment(String requestID, long experimentID, long projectId) - { + public void loadExperiment(String requestID, long experimentID, long projectId) { long start = System.currentTimeMillis(); websocketConnection.sendMessage(requestID, OutboundMessages.EXPERIMENT_LOADING, ""); - try - { + try { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); // run the matched experiment - if(experiment != null) - { + if (experiment != null) { ExperimentState experimentState = geppettoManager.loadExperiment(requestID, experiment); - websocketConnection.sendMessage(requestID, OutboundMessages.EXPERIMENT_LOADED, GeppettoSerializer.serializeToJSON(experimentState)); - logger.info("The experiment " + experimentID + " was loaded and the runtime tree was sent to the client"); - - } - else - { - error(null, "Error loading experiment, the experiment " + experimentID + " was not found in project " + projectId); + websocketConnection.sendMessage(requestID, OutboundMessages.EXPERIMENT_LOADED, + GeppettoSerializer.serializeToJSON(experimentState)); + logger.info( + "The experiment " + experimentID + " was loaded and the runtime tree was sent to the client"); + + } else { + error(null, "Error loading experiment, the experiment " + experimentID + " was not found in project " + + projectId); } - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error loading experiment"); - } - catch(IOException e) - { + } catch (IOException e) { error(e, "Error loading experiment"); } logger.debug("Loading experiment took " + (System.currentTimeMillis() - start) + "ms"); @@ -296,39 +269,31 @@ public void loadExperiment(String requestID, long experimentID, long projectId) /** * Run the Experiment */ - public void runExperiment(String requestID, long experimentID, long projectId) - { + public void runExperiment(String requestID, long experimentID, long projectId) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); - if(geppettoProject.isVolatile()) - { + if (geppettoProject.isVolatile()) { info(requestID, Resources.VOLATILE_PROJECT.toString()); return; - } - else - { - try - { + } else { + try { // run the matched experiment - if(experiment != null) - { - // TODO: If experiment is in ERROR state, user won't be able to run again. - // We reset it to DESIGN to allow user to run it for second time - if(experiment.getStatus() == ExperimentStatus.ERROR) - { + if (experiment != null) { + // TODO: If experiment is in ERROR state, user won't be able + // to run again. + // We reset it to DESIGN to allow user to run it for second + // time + if (experiment.getStatus() == ExperimentStatus.ERROR) { experiment.setStatus(ExperimentStatus.DESIGN); } geppettoManager.runExperiment(requestID, experiment); - } - else - { - error(null, "Error running experiment, the experiment " + experimentID + " was not found in project " + projectId); + } else { + error(null, "Error running experiment, the experiment " + experimentID + + " was not found in project " + projectId); } - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error running experiment"); } } @@ -341,31 +306,21 @@ public void runExperiment(String requestID, long experimentID, long projectId) * @param dataSourceId * @param variableId */ - public void fetchVariable(String requestID, Long projectId, String dataSourceId, String variableId) - { + public void fetchVariable(String requestID, Long projectId, String dataSourceId, String variableId) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); - try - { + try { GeppettoModel geppettoModel = geppettoManager.fetchVariable(dataSourceId, variableId, geppettoProject); String serializedModel = GeppettoSerializer.serializeToJSON(geppettoModel, true); websocketConnection.sendMessage(requestID, OutboundMessages.VARIABLE_FETCHED, serializedModel); - } - catch(GeppettoDataSourceException e) - { + } catch (GeppettoDataSourceException e) { error(e, "Error fetching variable " + variableId); - } - catch(IOException e) - { + } catch (IOException e) { error(e, "Error fetching variable " + variableId); - } - catch(GeppettoModelException e) - { + } catch (GeppettoModelException e) { error(e, "Error fetching variable " + variableId); - } - catch(GeppettoExecutionException e) - { + } catch (GeppettoExecutionException e) { error(e, "Error fetching variable " + variableId); } @@ -379,42 +334,33 @@ public void fetchVariable(String requestID, Long projectId, String dataSourceId, * @param variableId * @throws GeppettoExecutionException */ - public void resolveImportType(String requestID, Long projectId, List typePaths) - { + public void resolveImportType(String requestID, Long projectId, List typePaths) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); - try - { + try { GeppettoModel geppettoModel = geppettoManager.resolveImportType(typePaths, geppettoProject); - websocketConnection.sendMessage(requestID, OutboundMessages.IMPORT_TYPE_RESOLVED, GeppettoSerializer.serializeToJSON(geppettoModel, true)); - } - catch(IOException e) - { + websocketConnection.sendMessage(requestID, OutboundMessages.IMPORT_TYPE_RESOLVED, + GeppettoSerializer.serializeToJSON(geppettoModel, true)); + } catch (IOException e) { error(e, "Error importing type " + typePaths); - } - catch(GeppettoExecutionException e) - { + } catch (GeppettoExecutionException e) { error(e, "Error importing type " + typePaths); } } - /** * @param requestID * @param projectId * @param runnableQueryParameters */ - public void runQuery(String requestID, Long projectId, List runnableQueries) - { + public void runQuery(String requestID, Long projectId, List runnableQueries) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); QueryResults results; - try - { + try { results = geppettoManager.runQuery(runnableQueries, geppettoProject); - websocketConnection.sendMessage(requestID, OutboundMessages.RETURN_QUERY_RESULTS, GeppettoSerializer.serializeToJSON(results, true)); - } - catch(GeppettoDataSourceException | GeppettoModelException | GeppettoExecutionException | IOException e) - { + websocketConnection.sendMessage(requestID, OutboundMessages.RETURN_QUERY_RESULTS, + GeppettoSerializer.serializeToJSON(results, true)); + } catch (GeppettoDataSourceException | GeppettoModelException | GeppettoExecutionException | IOException e) { error(e, "Error running query"); } @@ -425,17 +371,13 @@ public void runQuery(String requestID, Long projectId, List runna * @param projectId * @param runnableQueries */ - public void runQueryCount(String requestID, Long projectId, List runnableQueries) - { + public void runQueryCount(String requestID, Long projectId, List runnableQueries) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); int count; - try - { + try { count = geppettoManager.runQueryCount(runnableQueries, geppettoProject); websocketConnection.sendMessage(requestID, OutboundMessages.RETURN_QUERY_COUNT, Integer.toString(count)); - } - catch(GeppettoDataSourceException | GeppettoModelException | GeppettoExecutionException e) - { + } catch (GeppettoDataSourceException | GeppettoModelException | GeppettoExecutionException e) { error(e, "Error running query count"); } } @@ -446,26 +388,21 @@ public void runQueryCount(String requestID, Long projectId, List * @param experimentId * @param path */ - public void resolveImportValue(String requestID, Long projectId, Long experimentId, String path) - { + public void resolveImportValue(String requestID, Long projectId, Long experimentId, String path) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentId, geppettoProject); - try - { + try { GeppettoModel geppettoModel = geppettoManager.resolveImportValue(path, experiment, geppettoProject); - websocketConnection.sendMessage(requestID, OutboundMessages.IMPORT_VALUE_RESOLVED, GeppettoSerializer.serializeToJSON(geppettoModel, true)); - } - catch(IOException e) - { + websocketConnection.sendMessage(requestID, OutboundMessages.IMPORT_VALUE_RESOLVED, + GeppettoSerializer.serializeToJSON(geppettoModel, true)); + } catch (IOException e) { error(e, "Error importing value " + path); - } - catch(GeppettoExecutionException e) - { + } catch (GeppettoExecutionException e) { error(e, "Error importing value " + path); } - + } - + /** * Adds watch lists with variables to be watched * @@ -474,25 +411,19 @@ public void resolveImportValue(String requestID, Long projectId, Long experiment * @throws GeppettoExecutionException * @throws GeppettoInitializationException */ - public void setWatchedVariables(String requestID, List variables, long experimentID, long projectId, boolean watch) throws GeppettoExecutionException, GeppettoInitializationException - { + public void setWatchedVariables(String requestID, List variables, long experimentID, long projectId, + boolean watch) throws GeppettoExecutionException, GeppettoInitializationException { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); - if(geppettoProject.isVolatile()) - { + if (geppettoProject.isVolatile()) { info(requestID, Resources.VOLATILE_PROJECT.toString()); return; - } - else - { - try - { + } else { + try { geppettoManager.setWatchedVariables(variables, experiment, geppettoProject, watch); - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error setting watched variables"); } @@ -500,15 +431,12 @@ public void setWatchedVariables(String requestID, List variables, long e ObjectMapper mapper = new ObjectMapper(); String serializedLists; - try - { + try { serializedLists = mapper.writer().writeValueAsString(variables); // send to the client the watch lists were added websocketConnection.sendMessage(requestID, OutboundMessages.WATCHED_VARIABLES_SET, serializedLists); - } - catch(JsonProcessingException e) - { + } catch (JsonProcessingException e) { error(e, "There was an error serializing the watched lists"); } } @@ -518,16 +446,13 @@ public void setWatchedVariables(String requestID, List variables, long e /** * @param requestID */ - public void getVersionNumber(String requestID) - { + public void getVersionNumber(String requestID) { Properties prop = new Properties(); - try - { + try { prop.load(ConnectionHandler.class.getResourceAsStream("/Geppetto.properties")); - websocketConnection.sendMessage(requestID, OutboundMessages.GEPPETTO_VERSION, prop.getProperty("Geppetto.version")); - } - catch(IOException e) - { + websocketConnection.sendMessage(requestID, OutboundMessages.GEPPETTO_VERSION, + prop.getProperty("Geppetto.version")); + } catch (IOException e) { error(e, "Unable to read GEPPETTO.properties file"); } } @@ -536,32 +461,25 @@ public void getVersionNumber(String requestID) * @param requestID * @param experimentId * @param projectId - * @param variables + * @param variables */ - public void getExperimentState(String requestID, long experimentId, long projectId, List variables) - { + public void getExperimentState(String requestID, long experimentId, long projectId, List variables) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentId, geppettoProject); - if(experiment != null) - { - try - { + if (experiment != null) { + try { ExperimentState experimentState = geppettoManager.getExperimentState(requestID, experiment, variables); - websocketConnection.sendMessage(requestID, OutboundMessages.GET_EXPERIMENT_STATE, GeppettoSerializer.serializeToJSON(experimentState)); - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + websocketConnection.sendMessage(requestID, OutboundMessages.GET_EXPERIMENT_STATE, + GeppettoSerializer.serializeToJSON(experimentState)); + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error playing the experiment " + experimentId); - } - catch(IOException e) - { + } catch (IOException e) { error(e, "Error playing the experiment " + experimentId); } - } - else - { - error(null, "Error playing experiment, the experiment " + experimentId + " was not found in project " + projectId); + } else { + error(null, "Error playing experiment, the experiment " + experimentId + " was not found in project " + + projectId); } } @@ -571,37 +489,35 @@ public void getExperimentState(String requestID, long experimentId, long project * @param format * */ - public void downloadModel(String requestID, String aspectInstancePath, String format, long experimentID, long projectId) - { + public void downloadModel(String requestID, String aspectInstancePath, String format, long experimentID, + long projectId) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); ModelFormat modelFormat = ServicesRegistry.getModelFormat(format); - try - { - - if(modelFormat == null && format != null) - { - // FIXME There is a method called ERROR for sending errors to the GUI, also the error code and the outbound message are different - // things, there's no need to have a separate message for each error + try { + + if (modelFormat == null && format != null) { + // FIXME There is a method called ERROR for sending errors to + // the GUI, also the error code and the outbound message are + // different + // things, there's no need to have a separate message for each + // error websocketConnection.sendMessage(requestID, OutboundMessages.ERROR_DOWNLOADING_MODEL, ""); - } - else - { + } else { // Convert model File file = geppettoManager.downloadModel(aspectInstancePath, modelFormat, experiment, geppettoProject); // Zip folder - Zipper zipper = new Zipper(PathConfiguration.createExperimentTmpPath(Scope.CONNECTION, projectId, experimentID, aspectInstancePath, file.getName() + ".zip")); + Zipper zipper = new Zipper(PathConfiguration.createExperimentTmpPath(Scope.CONNECTION, projectId, + experimentID, aspectInstancePath, file.getName() + ".zip")); Path path = zipper.getZipFromDirectory(file); // Send zip file to the client websocketConnection.sendBinaryMessage(requestID, path); websocketConnection.sendMessage(requestID, OutboundMessages.DOWNLOAD_MODEL, ""); } - } - catch(GeppettoExecutionException | IOException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | IOException | GeppettoAccessException e) { error(e, "Error downloading model for " + aspectInstancePath + " in format " + format); } } @@ -611,26 +527,22 @@ public void downloadModel(String requestID, String aspectInstancePath, String fo * @param aspectInstancePath * */ - public void getSupportedOuputs(String requestID, String aspectInstancePath, long experimentID, long projectId) - { + public void getSupportedOuputs(String requestID, String aspectInstancePath, long experimentID, long projectId) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); - try - { - List supportedOutputs = geppettoManager.getSupportedOuputs(aspectInstancePath, experiment, geppettoProject); + try { + List supportedOutputs = geppettoManager.getSupportedOuputs(aspectInstancePath, experiment, + geppettoProject); String supportedOutputsString = "["; - for(ModelFormat supportedOutput : supportedOutputs) - { + for (ModelFormat supportedOutput : supportedOutputs) { supportedOutputsString += "\"" + supportedOutput.getModelFormat() + "\","; } supportedOutputsString = supportedOutputsString.substring(0, supportedOutputsString.length() - 1); supportedOutputsString += "]"; websocketConnection.sendMessage(requestID, OutboundMessages.GET_SUPPORTED_OUTPUTS, supportedOutputsString); - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error getting supported outputs for " + aspectInstancePath); } } @@ -640,28 +552,26 @@ public void getSupportedOuputs(String requestID, String aspectInstancePath, long * @param urlString * @param visitor */ - public void sendScriptData(String requestID, String urlString, WebsocketConnection visitor) - { - try - { + public void sendScriptData(String requestID, String urlString, WebsocketConnection visitor) { + try { String line = null; StringBuilder sb = new StringBuilder(); - String urlPath = urlBase + urlString; + String urlPath = urlString; + if(!urlPath.startsWith("http")){ + urlPath = urlBase + urlString; + } URL url = URLReader.getURL(urlPath); BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); - while((line = br.readLine()) != null) - { + while ((line = br.readLine()) != null) { sb.append(line + "\n"); } String script = sb.toString(); websocketConnection.sendMessage(requestID, OutboundMessages.SCRIPT_FETCHED, script); - } - catch(IOException e) - { + } catch (IOException e) { error(e, "Error while reading the script at " + urlString); } } @@ -671,17 +581,14 @@ public void sendScriptData(String requestID, String urlString, WebsocketConnecti * @param url * @param visitor */ - public void sendDataSourceResults(String requestID, String dataSourceName, URL url, WebsocketConnection visitor) - { - try - { + public void sendDataSourceResults(String requestID, String dataSourceName, URL url, WebsocketConnection visitor) { + try { String line = null; StringBuilder sb = new StringBuilder(); BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); - while((line = br.readLine()) != null) - { + while ((line = br.readLine()) != null) { sb.append(line + "\n"); } String script = sb.toString(); @@ -693,9 +600,7 @@ public void sendDataSourceResults(String requestID, String dataSourceName, URL u String message = obj.toString(); websocketConnection.sendMessage(requestID, OutboundMessages.DATASOURCE_RESULTS_FETCHED, message); - } - catch(IOException e) - { + } catch (IOException e) { error(e, "Error while reading the script at " + url); } } @@ -707,54 +612,43 @@ public void sendDataSourceResults(String requestID, String dataSourceName, URL u * @param projectId * @param experimentID */ - public void setParameters(String requestID, Map modelParameters, long projectId, long experimentID) - { + public void setParameters(String requestID, Map modelParameters, long projectId, + long experimentID) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); - if(geppettoProject.isVolatile()) - { + if (geppettoProject.isVolatile()) { info(requestID, Resources.VOLATILE_PROJECT.toString()); return; - } - else - { - try - { - ExperimentState experimentState = geppettoManager.setModelParameters(modelParameters, experiment, geppettoProject); - websocketConnection.sendMessage(requestID, OutboundMessages.UPDATE_MODEL_TREE, GeppettoSerializer.serializeToJSON(experimentState)); - } - catch(GeppettoExecutionException | GeppettoAccessException | IOException e) - { + } else { + try { + ExperimentState experimentState = geppettoManager.setModelParameters(modelParameters, experiment, + geppettoProject); + websocketConnection.sendMessage(requestID, OutboundMessages.UPDATE_MODEL_TREE, + GeppettoSerializer.serializeToJSON(experimentState)); + } catch (GeppettoExecutionException | GeppettoAccessException | IOException e) { error(e, "There was an error setting parameters"); } } } - + /** * @param requestID * @param view * @param projectId * @param experimentID */ - public void setExperimentView(String requestID, String view, long projectId, long experimentID) - { + public void setExperimentView(String requestID, String view, long projectId, long experimentID) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); - if(!geppettoProject.isVolatile()) - { - try - { + if (!geppettoProject.isVolatile()) { + try { geppettoManager.setExperimentView(view, experiment, geppettoProject); - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "There was an error setting experiment view"); } - } - else - { + } else { String msg = "Set Experiment View: Cannot set view on volatile experiment (not persisted)"; error(new GeppettoExecutionException(msg), msg); } @@ -765,14 +659,11 @@ public void setExperimentView(String requestID, String view, long projectId, lon * @param geppettoProject * @return */ - private IExperiment retrieveExperiment(long experimentID, IGeppettoProject geppettoProject) - { + private IExperiment retrieveExperiment(long experimentID, IGeppettoProject geppettoProject) { IExperiment theExperiment = null; // Look for experiment that matches id passed - for(IExperiment e : geppettoProject.getExperiments()) - { - if(e.getId() == experimentID) - { + for (IExperiment e : geppettoProject.getExperiments()) { + if (e.getId() == experimentID) { // The experiment is found theExperiment = e; break; @@ -785,8 +676,7 @@ private IExperiment retrieveExperiment(long experimentID, IGeppettoProject geppe * @param projectId * @return */ - private IGeppettoProject retrieveGeppettoProject(long projectId) - { + private IGeppettoProject retrieveGeppettoProject(long projectId) { IGeppettoDataManager dataManager = DataManagerHelper.getDataManager(); return dataManager.getGeppettoProjectById(projectId); } @@ -797,16 +687,12 @@ private IGeppettoProject retrieveGeppettoProject(long projectId) * @return * @throws GeppettoExecutionException */ - public T fromJSON(final TypeReference type, String jsonPacket) throws GeppettoExecutionException - { + public T fromJSON(final TypeReference type, String jsonPacket) throws GeppettoExecutionException { T data = null; - try - { + try { data = new ObjectMapper().readValue(jsonPacket, type); - } - catch(IOException e) - { + } catch (IOException e) { error(e, "Error deserializing the JSON document"); } @@ -816,14 +702,12 @@ public T fromJSON(final TypeReference type, String jsonPacket) throws Gep /** * @return */ - private Gson getGson() - { + private Gson getGson() { GsonBuilder builder = new GsonBuilder(); - builder.registerTypeAdapter(Date.class, new JsonDeserializer() - { + builder.registerTypeAdapter(Date.class, new JsonDeserializer() { @Override - public Date deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context) throws JsonParseException - { + public Date deserialize(JsonElement json, java.lang.reflect.Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { return new Date(json.getAsJsonPrimitive().getAsLong()); } }); @@ -833,8 +717,7 @@ public Date deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDe /** * @return */ - public SimulationServerConfig getSimulationServerConfig() - { + public SimulationServerConfig getSimulationServerConfig() { return simulationServerConfig; } @@ -842,14 +725,12 @@ public SimulationServerConfig getSimulationServerConfig() * @param exception * @param errorMessage */ - private void error(Exception exception, String errorMessage) - { + private void error(Exception exception, String errorMessage) { String exceptionMessage = ""; - if(exception != null) - { + if (exception != null) { exceptionMessage = exception.getCause() == null ? exception.getMessage() : exception.toString(); } - Error error = new Error(GeppettoErrorCodes.EXCEPTION, errorMessage, exceptionMessage,0); + Error error = new Error(GeppettoErrorCodes.EXCEPTION, errorMessage, exceptionMessage, 0); logger.error(errorMessage, exception); websocketConnection.sendMessage(null, OutboundMessages.ERROR, getGson().toJson(error)); @@ -860,21 +741,18 @@ private void error(Exception exception, String errorMessage) * @param exception * @param errorMessage */ - private void info(String requestID, String message) - { + private void info(String requestID, String message) { logger.info(message); websocketConnection.sendMessage(requestID, OutboundMessages.INFO_MESSAGE, getGson().toJson(message)); } - private class Error - { - public Error(GeppettoErrorCodes errorCode, String errorMessage, String jsonExceptionMsg, long id) - { + private class Error { + public Error(GeppettoErrorCodes errorCode, String errorMessage, String jsonExceptionMsg, long id) { this.error_code = errorCode.toString(); message = errorMessage; exception = jsonExceptionMsg; - this.id = id; + this.id = id; } String error_code; @@ -887,34 +765,28 @@ public Error(GeppettoErrorCodes errorCode, String errorMessage, String jsonExcep * @param requestID * @param projectId */ - public void checkExperimentStatus(String requestID, String projectId) - { + public void checkExperimentStatus(String requestID, String projectId) { IGeppettoDataManager dataManager = DataManagerHelper.getDataManager(); - try - { + try { IGeppettoProject geppettoProject = dataManager.getGeppettoProjectById(Long.parseLong(projectId)); - if(geppettoProject != null) - { - List experiments = geppettoManager.checkExperimentsStatus(requestID, geppettoProject); + if (geppettoProject != null) { + List experiments = geppettoManager.checkExperimentsStatus(requestID, + geppettoProject); String status = "["; - for(IExperiment e : experiments) - { + for (IExperiment e : experiments) { // FIXME - status += "{\"projectID\":" + '"' + projectId + '"' + ",\"experimentID\":" + '"' + e.getId() + '"' + ",\"status\":" + '"' + e.getStatus().toString() + '"' + "},"; + status += "{\"projectID\":" + '"' + projectId + '"' + ",\"experimentID\":" + '"' + e.getId() + '"' + + ",\"status\":" + '"' + e.getStatus().toString() + '"' + "},"; } status = status.substring(0, status.length() - 1); status += "]"; websocketConnection.sendMessage(requestID, OutboundMessages.EXPERIMENT_STATUS, status); - } - else - { + } else { String msg = "Check Experiment: Cannot find project " + projectId; error(new GeppettoExecutionException(msg), msg); } - } - catch(NumberFormatException e) - { + } catch (NumberFormatException e) { error(e, "Check Experiment: Errror parsing project id"); } } @@ -924,99 +796,81 @@ public void checkExperimentStatus(String requestID, String projectId) * @param experimentId * @param projectId */ - public void deleteExperiment(String requestID, long experimentId, long projectId) - { + public void deleteExperiment(String requestID, long experimentId, long projectId) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentId, geppettoProject); - if(experiment != null) - { - try - { + if (experiment != null) { + try { geppettoManager.deleteExperiment(requestID, experiment); - String update = "{\"id\":" + '"' + experiment.getId() + '"' + ",\"name\":" + '"' + experiment.getName() + '"' + "}"; + String update = "{\"id\":" + '"' + experiment.getId() + '"' + ",\"name\":" + '"' + experiment.getName() + + '"' + "}"; websocketConnection.sendMessage(requestID, OutboundMessages.DELETE_EXPERIMENT, update); - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error while deleting the experiment"); } - } - else - { - error(null, "Error deleting experiment, the experiment " + experimentId + " was not found in project " + projectId); + } else { + error(null, "Error deleting experiment, the experiment " + experimentId + " was not found in project " + + projectId); } } - + /** * @param requestID * @param projectId */ - public void makeProjectPublic(String requestID, long projectId,boolean isPublic) - { + public void makeProjectPublic(String requestID, long projectId, boolean isPublic) { - try - { + try { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); - if(geppettoProject != null) - { + if (geppettoProject != null) { geppettoManager.makeProjectPublic(requestID, geppettoProject, isPublic); - String update = "{\"id\":" + '"' + geppettoProject.getId() + '"' + ",\"isPublic\":" + geppettoProject.isPublic() + "}"; + String update = "{\"id\":" + '"' + geppettoProject.getId() + '"' + ",\"isPublic\":" + + geppettoProject.isPublic() + "}"; websocketConnection.sendMessage(requestID, OutboundMessages.PROJECT_MADE_PUBLIC, update); - } - else - { + } else { error(null, "Error making project public " + projectId + "."); } - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error making project public"); } } - + /** * @param requestID * @param projectId */ - public void persistProject(String requestID, long projectId) - { + public void persistProject(String requestID, long projectId) { - try - { + try { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); - if(geppettoProject != null) - { + if (geppettoProject != null) { geppettoManager.persistProject(requestID, geppettoProject); - PersistedProject persistedProject = new PersistedProject(geppettoProject.getId(), geppettoProject.getActiveExperimentId()); - websocketConnection.sendMessage(requestID, OutboundMessages.PROJECT_PERSISTED, getGson().toJson(persistedProject)); - } - else - { + PersistedProject persistedProject = new PersistedProject(geppettoProject.getId(), + geppettoProject.getActiveExperimentId()); + websocketConnection.sendMessage(requestID, OutboundMessages.PROJECT_PERSISTED, + getGson().toJson(persistedProject)); + } else { error(null, "Error persisting project " + projectId + "."); } - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Error persisting project"); } } - class PersistedProject - { + class PersistedProject { long projectID; long activeExperimentID; - public PersistedProject(long projectID, long activeExperimentID) - { + public PersistedProject(long projectID, long activeExperimentID) { super(); this.projectID = projectID; this.activeExperimentID = activeExperimentID; @@ -1028,15 +882,11 @@ public PersistedProject(long projectID, long activeExperimentID) * @param requestID * @param key */ - public void linkDropBox(String requestID, String key) - { - try - { + public void linkDropBox(String requestID, String key) { + try { geppettoManager.linkDropBoxAccount(key); websocketConnection.sendMessage(requestID, OutboundMessages.DROPBOX_LINKED, null); - } - catch(Exception e) - { + } catch (Exception e) { error(e, "Unable to link dropbox account."); } } @@ -1045,15 +895,11 @@ public void linkDropBox(String requestID, String key) * @param requestID * @param key */ - public void unLinkDropBox(String requestID, String key) - { - try - { + public void unLinkDropBox(String requestID, String key) { + try { geppettoManager.unlinkDropBoxAccount(key); websocketConnection.sendMessage(null, OutboundMessages.DROPBOX_UNLINKED, null); - } - catch(Exception e) - { + } catch (Exception e) { error(e, "Unable to unlink dropbox account."); } } @@ -1064,18 +910,14 @@ public void unLinkDropBox(String requestID, String key) * @param experimentId * @param format */ - public void uploadModel(String aspectPath, long projectId, long experimentId, String format) - { + public void uploadModel(String aspectPath, long projectId, long experimentId, String format) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentId, geppettoProject); ModelFormat modelFormat = ServicesRegistry.getModelFormat(format); - try - { + try { geppettoManager.uploadModelToDropBox(aspectPath, experiment, geppettoProject, modelFormat); websocketConnection.sendMessage(null, OutboundMessages.MODEL_UPLOADED, null); - } - catch(Exception e) - { + } catch (Exception e) { error(e, "Unable to upload results for aspect : " + aspectPath); } } @@ -1086,18 +928,14 @@ public void uploadModel(String aspectPath, long projectId, long experimentId, St * @param experimentId * @param format */ - public void uploadResults(String aspectPath, long projectId, long experimentId, String format) - { + public void uploadResults(String aspectPath, long projectId, long experimentId, String format) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentId, geppettoProject); ResultsFormat resultsFormat = ServicesRegistry.getResultsFormat(format); - try - { + try { geppettoManager.uploadResultsToDropBox(aspectPath, experiment, geppettoProject, resultsFormat); websocketConnection.sendMessage(null, OutboundMessages.RESULTS_UPLOADED, null); - } - catch(GeppettoExecutionException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | GeppettoAccessException e) { error(e, "Unable to upload results for aspect : " + aspectPath); } } @@ -1109,40 +947,33 @@ public void uploadResults(String aspectPath, long projectId, long experimentId, * @param experimentId * @param format */ - public void downloadResults(String requestID, String aspectPath, long projectId, long experimentId, String format) - { + public void downloadResults(String requestID, String aspectPath, long projectId, long experimentId, String format) { IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentId, geppettoProject); ResultsFormat resultsFormat = ServicesRegistry.getResultsFormat(format); - try - { - if(resultsFormat == null) - { + try { + if (resultsFormat == null) { websocketConnection.sendMessage(requestID, OutboundMessages.ERROR_DOWNLOADING_RESULTS, ""); - } - else - { + } else { // Convert model URL url = geppettoManager.downloadResults(aspectPath, resultsFormat, experiment, geppettoProject); - if(url != null) - { + if (url != null) { // Zip folder - Zipper zipper = new Zipper(PathConfiguration.createExperimentTmpPath(Scope.CONNECTION, projectId, experimentId, aspectPath, URLReader.getFileName(url))); + Zipper zipper = new Zipper(PathConfiguration.createExperimentTmpPath(Scope.CONNECTION, projectId, + experimentId, aspectPath, URLReader.getFileName(url))); Path path = zipper.getZipFromFile(url); // Send zip file to the client websocketConnection.sendBinaryMessage(requestID, path); websocketConnection.sendMessage(requestID, OutboundMessages.DOWNLOAD_RESULTS, ""); - } - else - { - error(new GeppettoExecutionException("Results of type " + format + " not found in the current experiment"), "Error downloading results for " + aspectPath + " in format " + format); + } else { + error(new GeppettoExecutionException( + "Results of type " + format + " not found in the current experiment"), + "Error downloading results for " + aspectPath + " in format " + format); } } - } - catch(GeppettoExecutionException | IOException | GeppettoAccessException e) - { + } catch (GeppettoExecutionException | IOException | GeppettoAccessException e) { error(e, "Error downloading results for " + aspectPath + " in format " + format); } } @@ -1152,32 +983,23 @@ public void downloadResults(String requestID, String aspectPath, long projectId, * @param projectId * @param properties */ - public void saveProjectProperties(String requestID, long projectId, Map properties) - { - if(DataManagerHelper.getDataManager().isDefault()) - { + public void saveProjectProperties(String requestID, long projectId, Map properties) { + if (DataManagerHelper.getDataManager().isDefault()) { info(requestID, Resources.UNSUPPORTED_OPERATION.toString()); } IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); - if(geppettoProject.isVolatile()) - { + if (geppettoProject.isVolatile()) { info(requestID, Resources.VOLATILE_PROJECT.toString()); return; - } - else - { + } else { IGeppettoDataManager dataManager = DataManagerHelper.getDataManager(); - if(properties != null) - { - for(String p : properties.keySet()) - { - switch(p) - { - case "name": - { - geppettoProject.setName(properties.get(p)); - break; - } + if (properties != null) { + for (String p : properties.keySet()) { + switch (p) { + case "name": { + geppettoProject.setName(properties.get(p)); + break; + } } } } @@ -1192,144 +1014,119 @@ public void saveProjectProperties(String requestID, long projectId, Map properties) - { - if(DataManagerHelper.getDataManager().isDefault()) - { + public void saveExperimentProperties(String requestID, long projectId, long experimentId, + Map properties) { + if (DataManagerHelper.getDataManager().isDefault()) { info(requestID, Resources.UNSUPPORTED_OPERATION.toString()); } IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentId, geppettoProject); - if(geppettoProject.isVolatile()) - { + if (geppettoProject.isVolatile()) { info(requestID, Resources.VOLATILE_PROJECT.toString()); return; - } - else - { + } else { IGeppettoDataManager dataManager = DataManagerHelper.getDataManager(); - for(String p : properties.keySet()) - { - switch(p) - { - case "name": - { - experiment.setName(properties.get(p)); - dataManager.saveEntity(experiment); - break; - } - case "description": - { - experiment.setDescription(properties.get(p)); - dataManager.saveEntity(experiment); - break; - } - case "script": - { - experiment.setScript(properties.get(p)); - dataManager.saveEntity(experiment); - break; + for (String p : properties.keySet()) { + switch (p) { + case "name": { + experiment.setName(properties.get(p)); + dataManager.saveEntity(experiment); + break; + } + case "description": { + experiment.setDescription(properties.get(p)); + dataManager.saveEntity(experiment); + break; + } + case "script": { + experiment.setScript(properties.get(p)); + dataManager.saveEntity(experiment); + break; + } + case "timeStep": { + String aspectPath = properties.get("aspectInstancePath"); + for (IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) { + if (aspectConfiguration.getInstance().equals(aspectPath)) { + aspectConfiguration.getSimulatorConfiguration() + .setTimestep(Float.parseFloat(properties.get(p))); + dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); + break; + } } - case "timeStep": - { - String aspectPath = properties.get("aspectInstancePath"); - for(IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) - { - if(aspectConfiguration.getInstance().equals(aspectPath)) - { - aspectConfiguration.getSimulatorConfiguration().setTimestep(Float.parseFloat(properties.get(p))); - dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); - break; - } + break; + } + case "length": { + String aspectPath = properties.get("aspectInstancePath"); + for (IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) { + if (aspectConfiguration.getInstance().equals(aspectPath)) { + aspectConfiguration.getSimulatorConfiguration() + .setLength(Float.parseFloat(properties.get(p))); + dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); + break; } - break; } - case "length": - { - String aspectPath = properties.get("aspectInstancePath"); - for(IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) - { - if(aspectConfiguration.getInstance().equals(aspectPath)) - { - aspectConfiguration.getSimulatorConfiguration().setLength(Float.parseFloat(properties.get(p))); - dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); - break; - } + break; + } + case "simulatorId": { + String aspectPath = properties.get("aspectInstancePath"); + for (IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) { + if (aspectConfiguration.getInstance().equals(aspectPath)) { + aspectConfiguration.getSimulatorConfiguration().setSimulatorId(properties.get(p)); + dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); + break; } - break; } - case "simulatorId": - { - String aspectPath = properties.get("aspectInstancePath"); - for(IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) - { - if(aspectConfiguration.getInstance().equals(aspectPath)) - { - aspectConfiguration.getSimulatorConfiguration().setSimulatorId(properties.get(p)); - dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); - break; - } + break; + } + case "conversionServiceId": { + String aspectPath = properties.get("aspectInstancePath"); + for (IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) { + if (aspectConfiguration.getInstance().equals(aspectPath)) { + aspectConfiguration.getSimulatorConfiguration().setConversionServiceId(properties.get(p)); + dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); + break; } - break; } - case "conversionServiceId": - { + break; + } + case "aspectInstancePath": { + break; + } + default: { + if (p.startsWith("SP$")) { + // This is a simulator parameter String aspectPath = properties.get("aspectInstancePath"); - for(IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) - { - if(aspectConfiguration.getInstance().equals(aspectPath)) - { - aspectConfiguration.getSimulatorConfiguration().setConversionServiceId(properties.get(p)); + for (IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) { + if (aspectConfiguration.getInstance().equals(aspectPath)) { + + Map parameters = aspectConfiguration.getSimulatorConfiguration() + .getParameters(); + if (parameters == null) { + parameters = new HashMap(); + aspectConfiguration.getSimulatorConfiguration().setParameters(parameters); + } + parameters.put(p.substring(p.indexOf("$") + 1), properties.get(p)); dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); break; } } break; + } else { + String msg = "Cannot find parameter " + p + " in the experiment"; + error(new GeppettoExecutionException(msg), msg); } - case "aspectInstancePath": - { - break; - } - default: - { - if(p.startsWith("SP$")) - { - // This is a simulator parameter - String aspectPath = properties.get("aspectInstancePath"); - for(IAspectConfiguration aspectConfiguration : experiment.getAspectConfigurations()) - { - if(aspectConfiguration.getInstance().equals(aspectPath)) - { - - Map parameters = aspectConfiguration.getSimulatorConfiguration().getParameters(); - if(parameters == null) - { - parameters = new HashMap(); - aspectConfiguration.getSimulatorConfiguration().setParameters(parameters); - } - parameters.put(p.substring(p.indexOf("$") + 1), properties.get(p)); - dataManager.saveEntity(aspectConfiguration.getSimulatorConfiguration()); - break; - } - } - break; - } - else - { - String msg = "Cannot find parameter " + p + " in the experiment"; - error(new GeppettoExecutionException(msg), msg); - } - } + } } } - // send back id of experiment saved, and if the experiment modified was in ERROR state change + // send back id of experiment saved, and if the experiment modified + // was in ERROR state change // it back to DESIGN to allow re-running ExperimentStatus status = experiment.getStatus(); - if(status == ExperimentStatus.ERROR) - { + if (status == ExperimentStatus.ERROR) { experiment.setStatus(ExperimentStatus.DESIGN); } - String update = "{\"id\":" + '"' + experiment.getId() + '"' + ",\"status\":" + '"' + experiment.getStatus() + '"' + "}"; + String update = "{\"id\":" + '"' + experiment.getId() + '"' + ",\"status\":" + '"' + experiment.getStatus() + + '"' + "}"; websocketConnection.sendMessage(requestID, OutboundMessages.EXPERIMENT_PROPS_SAVED, update); } } @@ -1337,14 +1134,10 @@ public void saveExperimentProperties(String requestID, long projectId, long expe /** * */ - public void closeProject() - { - try - { + public void closeProject() { + try { geppettoManager.closeProject(null, geppettoProject); - } - catch(GeppettoExecutionException e) - { + } catch (GeppettoExecutionException e) { logger.error("Error while closing the project", e); } @@ -1356,17 +1149,13 @@ public void closeProject() * @param geppettoProject * @throws GeppettoExecutionException */ - public void setConnectionProject(IGeppettoProject geppettoProject) throws GeppettoExecutionException - { - if(this.geppettoProject != null) - { + public void setConnectionProject(IGeppettoProject geppettoProject) throws GeppettoExecutionException { + if (this.geppettoProject != null) { geppettoManager.closeProject(null, this.geppettoProject); } this.geppettoProject = geppettoProject; } - - /** * Sends to the client login user privileges * @@ -1375,39 +1164,31 @@ public void setConnectionProject(IGeppettoProject geppettoProject) throws Geppet /** * @param requestID */ - public void checkUserPrivileges(String requestID) - { + public void checkUserPrivileges(String requestID) { boolean hasPersistence = false; - try - { + try { hasPersistence = !AuthServiceCreator.getService().isDefault(); - } - catch(GeppettoInitializationException e) - { + } catch (GeppettoInitializationException e) { error(e, "Unable to determine whether persistence services are available or not"); } UserPrivilegesDT userPrivileges = new UserPrivilegesDT(); - if(this.geppettoManager.getUser() != null) - { + if (this.geppettoManager.getUser() != null) { userPrivileges.userName = this.geppettoManager.getUser().getLogin(); } userPrivileges.hasPersistence = hasPersistence; userPrivileges.loggedIn = this.geppettoManager.getUser() != null; - if(this.geppettoManager.getUser() != null) - { + if (this.geppettoManager.getUser() != null) { List privileges = this.geppettoManager.getUser().getUserGroup().getPrivileges(); - for(UserPrivileges up : privileges) - { + for (UserPrivileges up : privileges) { userPrivileges.privileges.add(up.toString()); } } websocketConnection.sendMessage(requestID, OutboundMessages.USER_PRIVILEGES, getGson().toJson(userPrivileges)); } - private class UserPrivilegesDT - { + private class UserPrivilegesDT { public String userName = ""; public boolean loggedIn = false; public boolean hasPersistence = false; @@ -1415,45 +1196,40 @@ private class UserPrivilegesDT } @Override - public void experimentError(String titleMessage, String logMessage, Exception exception, IExperiment experiment) - { + public void experimentError(String titleMessage, String logMessage, Exception exception, IExperiment experiment) { Error error = new Error(GeppettoErrorCodes.EXCEPTION, titleMessage, logMessage, experiment.getId()); logger.error(logMessage, exception); - + String jsonError = this.getGson().toJson(error); - - if(!geppettoProject.isVolatile()) - { + + if (!geppettoProject.isVolatile()) { IGeppettoDataManager dataManager = DataManagerHelper.getDataManager(); - //stores only first 2000 characters of error message - if(jsonError.length() < 10000){ + // stores only first 2000 characters of error message + if (jsonError.length() < 10000) { experiment.setDetails(jsonError); dataManager.saveEntity(experiment); } } - + websocketConnection.sendMessage(null, OutboundMessages.ERROR_RUNNING_EXPERIMENT, jsonError); } public void downloadProject(String requestID, long projectId) { - try - { - + try { + Path zipPath = this.geppettoManager.downloadProject(geppettoProject); - - if(zipPath != null) - { + + if (zipPath != null) { // Send zip file to the client websocketConnection.sendBinaryMessage(requestID, zipPath); - websocketConnection.sendMessage(requestID, OutboundMessages.DOWNLOAD_PROJECT,null); - - //clean temporary directory where files where written - FileUtils.cleanDirectory(zipPath.toFile().getParentFile()); - }else{ + websocketConnection.sendMessage(requestID, OutboundMessages.DOWNLOAD_PROJECT, null); + + // clean temporary directory where files where written + FileUtils.cleanDirectory(zipPath.toFile().getParentFile()); + } else { error(null, "Error downloading project"); } - } - catch (Exception e) { + } catch (Exception e) { error(e, "Error downloading project"); } } From 268325f9ab7de7e6e7874a1512364f8650bdf187 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 25 Apr 2017 11:01:01 -0700 Subject: [PATCH 111/339] removes volatile check on set view --- .../frontend/controllers/ConnectionHandler.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java index 6a7cde0f4..37a351843 100644 --- a/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java +++ b/src/main/java/org/geppetto/frontend/controllers/ConnectionHandler.java @@ -642,15 +642,10 @@ public void setExperimentView(String requestID, String view, long projectId, lon IGeppettoProject geppettoProject = retrieveGeppettoProject(projectId); IExperiment experiment = retrieveExperiment(experimentID, geppettoProject); - if (!geppettoProject.isVolatile()) { - try { - geppettoManager.setExperimentView(view, experiment, geppettoProject); - } catch (GeppettoExecutionException | GeppettoAccessException e) { - error(e, "There was an error setting experiment view"); - } - } else { - String msg = "Set Experiment View: Cannot set view on volatile experiment (not persisted)"; - error(new GeppettoExecutionException(msg), msg); + try { + geppettoManager.setExperimentView(view, experiment, geppettoProject); + } catch (GeppettoExecutionException | GeppettoAccessException e) { + error(e, "There was an error setting experiment view"); } } From 704d25f0fb662f51d8a8e21b4e72a7c28299f5ef Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 25 Apr 2017 11:58:19 -0700 Subject: [PATCH 112/339] adds hide tool tip method --- .../webapp/js/components/controls/button/Button.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/controls/button/Button.js b/src/main/webapp/js/components/controls/button/Button.js index 1685c877e..0f33e8a5a 100644 --- a/src/main/webapp/js/components/controls/button/Button.js +++ b/src/main/webapp/js/components/controls/button/Button.js @@ -56,12 +56,20 @@ define(function(require) { }); }, - showToolTip : function(tooltipLabel){ + showToolTip : function(tooltipLabel, tooltipPosition){ + var position = tooltipPosition; + if(position==undefined){ + position = this.props.configuration.tooltipPosition; + } // update contents of what's displayed on tooltip $("#"+this.props.configuration.id).uitooltip({content: tooltipLabel, - position: this.props.configuration.tooltipPosition}); + position: position}); $("#"+this.props.configuration.id).mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); }, + + hideToolTip : function(){ + $("#"+this.props.configuration.id).uitooltip('hide'); + }, getInitialState: function() { return { From d483846b87fdc78234420637867d93af42120261 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 25 Apr 2017 14:41:31 -0700 Subject: [PATCH 113/339] tooltip fix --- .../js/components/controls/button/Button.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/controls/button/Button.js b/src/main/webapp/js/components/controls/button/Button.js index 0f33e8a5a..0f7d8edc4 100644 --- a/src/main/webapp/js/components/controls/button/Button.js +++ b/src/main/webapp/js/components/controls/button/Button.js @@ -37,7 +37,7 @@ define(function(require) { var button = React.createClass({ attachTooltip: function(){ var self = this; - $("#"+this.props.configuration.id).uitooltip({ + $("#"+this.props.configuration.id).tooltip({ position: this.props.configuration.tooltipPosition, tooltipClass: "tooltip-persist", show: { @@ -61,14 +61,15 @@ define(function(require) { if(position==undefined){ position = this.props.configuration.tooltipPosition; } + $("#"+this.props.configuration.id).tooltip("option", "show"); // update contents of what's displayed on tooltip - $("#"+this.props.configuration.id).uitooltip({content: tooltipLabel, + $("#"+this.props.configuration.id).tooltip({content: tooltipLabel, position: position}); $("#"+this.props.configuration.id).mouseover().delay(2000).queue(function(){$(this).mouseout().dequeue();}); }, hideToolTip : function(){ - $("#"+this.props.configuration.id).uitooltip('hide'); + $("#"+this.props.configuration.id).tooltip("option", "hide"); }, getInitialState: function() { @@ -82,6 +83,18 @@ define(function(require) { componentDidMount: function() { this.attachTooltip(); this.props.configuration.eventHandler(this); + var self = this; + var focused = true; + + window.onfocus = function() { + if(!focused){ + self.hideToolTip(); + } + focused = true; + }; + window.onblur = function() { + focused = false; + }; }, render: function () { From 0f75e7c3f05296a250da8538d9184e55d3edb7d3 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 25 Apr 2017 18:13:54 -0700 Subject: [PATCH 114/339] makes control panel refresh when experiment properties change --- .../webapp/js/communication/MessageHandler.js | 14 ++++++++++ .../interface/controlPanel/controlpanel.js | 26 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 72317dcef..30afb0ddb 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -207,6 +207,16 @@ define(function(require) { messageHandler[messageTypes.PROJECT_PROPS_SAVED] = function(payload) { GEPPETTO.Console.log("Project saved succesfully"); + if(GEPPETTO.ControlPanel.isOpen()){ + GEPPETTO.ControlPanel.refresh(); + } + }; + + messageHandler[messageTypes.SET_PARAMETERS] = function(payload) { + GEPPETTO.Console.log("Set parameters succesfully"); + if(GEPPETTO.ControlPanel.isOpen()){ + GEPPETTO.ControlPanel.refresh(); + } }; messageHandler[messageTypes.EXPERIMENT_PROPS_SAVED] = function(payload) { @@ -219,6 +229,10 @@ define(function(require) { if (experiment.getStatus() != data.status) { experiment.setStatus(data.status); } + + if(GEPPETTO.ControlPanel.isOpen()){ + GEPPETTO.ControlPanel.refresh(); + } }; messageHandler[messageTypes.DROPBOX_LINKED] = function(payload) { diff --git a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js index 1e0fc6464..d4d10976d 100644 --- a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js +++ b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js @@ -636,6 +636,21 @@ define(function (require) { that.computeResult('visualInstancesFilterBtn'); } }); + GEPPETTO.on("control_panel_refresh", function(){ + // when control panel is open and we are using the filter component + // if no other main component is toggled show visual instances + if(that.state.stateVarsFilterToggled){ + // same logic as if viz instances filter was clicked + that.computeResult('stateVariablesFilterBtn'); + } + else if(that.state.paramsFilterToggled){ + // same logic as if viz instances filter was clicked + that.computeResult('parametersFilterBtn'); + }else if(that.state.visualFilterToggled){ + // same logic as if viz instances filter was clicked + that.computeResult('visualInstancesFilterBtn'); + } + }); }, computeResult: function(controlId){ @@ -1433,7 +1448,12 @@ define(function (require) { displayName: 'ControlPanel', refresh: function() { - this.forceUpdate(); + var self = this; + var callback = function(){ + self.forceUpdate(); + GEPPETTO.trigger("control_panel_refresh"); + }; + GEPPETTO.ProjectsController.refreshUserProjects(callback); }, getInitialState: function () { @@ -1839,6 +1859,10 @@ define(function (require) { } }, + isOpen : function(){ + return $("#controlpanel").is(':visible'); + }, + componentDidMount: function () { var escape = 27; var pKey = 80; From 8da5f75426c2568dd2c4e35df629f35be6c8f817 Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 26 Apr 2017 19:58:14 +0100 Subject: [PATCH 115/339] dirty mechanism for persisting views only when any of the tracked properties has changed --- .../js/common/GEPPETTO.ViewController.js | 20 +++++++-- .../webapp/js/components/widgets/Widget.js | 43 ++++++++++++++++++- .../components/widgets/buttonBar/ButtonBar.js | 6 +++ .../widgets/connectivity/Connectivity.js | 6 +++ .../webapp/js/components/widgets/plot/Plot.js | 21 ++++++++- .../js/components/widgets/popup/Popup.js | 19 ++++++++ .../controllers/StackViewerController.js | 3 +- .../widgets/treevisualiser/TreeVisualiser.js | 1 - .../variablevisualiser/VariableVisualiser.js | 7 +++ 9 files changed, 118 insertions(+), 8 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.ViewController.js b/src/main/webapp/js/common/GEPPETTO.ViewController.js index 0badea37c..601bf101f 100644 --- a/src/main/webapp/js/common/GEPPETTO.ViewController.js +++ b/src/main/webapp/js/common/GEPPETTO.ViewController.js @@ -90,19 +90,33 @@ define(function(require) for(var c in components){ // call getView API if the method is exposed and the component is not stateless if( + // check if state-view API is implemented by the component typeof components[c].getView == 'function' && + // check that component is not stateless components[c].stateless != undefined && - !components[c].stateless + !components[c].stateless && + // check if view is dirty so only dirty views get added + components[c].isDirty() ) { // build object literal with view state for all the widgets/components viewState.views[c] = components[c].getView(); + // reset view as clean so we don't keep retrieving the same view if nothing changed + components[c].setDirty(false); } } // set view on experiment or project - if(window.Project.getActiveExperiment()!=null && window.Project.getActiveExperiment()!=undefined){ + if( + window.Project.getActiveExperiment()!=null && + window.Project.getActiveExperiment()!=undefined && + // check if any views were added + Object.keys(viewState.views).length > 0 + ){ window.Project.getActiveExperiment().setView(viewState); - } else { + } else if ( + // check if any views were added + Object.keys(viewState.views).length > 0 + ){ window.Project.setView(viewState); } } diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index 9afaa2d57..fb6750d9d 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -40,6 +40,7 @@ define(function (require) { stateless: false, showTitleBar: true, transparentBackground: false, + dirtyView: false, defaultSize : function(){return {height: 300, width: 350}}, defaultPosition : function(){return {left: "50%", top: "50%"}}, @@ -61,6 +62,7 @@ define(function (require) { this.widgetType = options.widgetType; this.stateless = (options.stateless != undefined) ? options.stateless : false; this.registeredEvents = []; + this.dirtyView = false; var self = this; $(self.historyMenu.el).on('click', function (event) { @@ -152,7 +154,10 @@ define(function (require) { this.name = name; // set name to widget window - this.$el.dialog("option", "title", this.name).dialogExtend(); + this.$el.dialog("option", "title", this.name).dialogExtend(); + + // set flag to indicate something changed + this.dirtyView = true; return this; }, @@ -172,6 +177,10 @@ define(function (require) { at: "left top", of: $(window) }).dialogExtend(); + + // set flag to indicate something changed + this.dirtyView = true; + return this; }, @@ -186,6 +195,10 @@ define(function (require) { this.size.width = w; this.$el.dialog({height: this.size.height, width: this.size.width}).dialogExtend(); this.$el.trigger('resizeEnd'); + + // set flag to indicate something changed + this.dirtyView = true; + return this; }, @@ -272,6 +285,27 @@ define(function (require) { return this.id; }, + /** + * Did something change in the state of the widget? + * + * @command isDirty() + * @returns {boolean} - ID of widget + */ + isDirty: function () { + return this.dirtyView; + }, + + /** + * Explicitly sets status of view + * NOTE: we need to be able to control this from outside the component + * + * @command setDirty() + * @param {boolean} dirty + */ + setDirty: function (dirty) { + this.dirtyView = dirty; + }, + /** * Returns whether widget is visible or not * @@ -388,6 +422,10 @@ define(function (require) { } else { this.$el.parent().find(".ui-dialog-titlebar").hide(); } + + // set flag to indicate something changed + this.dirtyView = true; + return this; }, @@ -724,6 +762,9 @@ define(function (require) { if(view.transparentBackground != undefined){ this.setTrasparentBackground(view.transparentBackground); } + + // after setting view through setView, reset dirty flag + this.dirtyView = false; } }) }; diff --git a/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js b/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js index 7f76ef07a..35a3e13d2 100644 --- a/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js +++ b/src/main/webapp/js/components/widgets/buttonBar/ButtonBar.js @@ -174,6 +174,9 @@ define(function (require) { $('[data-toggle="tooltip"]').tooltip() }); + // track change in state of the widget + this.dirtyView = true; + return this; }, @@ -258,6 +261,9 @@ define(function (require) { if(view.componentSpecific != undefined){ this.renderBar(view.componentSpecific.barName, view.componentSpecific.barBody); } + + // after setting view through setView, reset dirty flag + this.dirtyView = false; } }); diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index 1d290e67b..32ac7f3a2 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -103,6 +103,9 @@ define(function (require) { this.createLayout(); } + // track change in state of the widget + this.dirtyView = true; + return this; }, @@ -448,6 +451,9 @@ define(function (require) { this.setData(obj, deserializedOptions); } + + // after setting view through setView, reset dirty flag + this.dirtyView = false; } }); }); diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index ba14c0c6b..eb318d482 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -396,8 +396,10 @@ define(function (require) { this.xVariable = window.time; if(plotable){ this.plotGeneric(); - } - + } + + // track change in state of the widget + this.dirtyView = true; return this; }, @@ -979,6 +981,10 @@ define(function (require) { //Set title to widget this.setName(plotTitle); } + + // track change in state of the widget + this.dirtyView = true; + return this; }, @@ -1050,6 +1056,10 @@ define(function (require) { this.plotly = Plotly.newPlot(this.plotDiv, this.datasets, this.plotOptions,{displayModeBar: false, doubleClick : false}); this.initialized = true; this.resize(); + + // track change in state of the widget + this.dirtyView = true; + return this; }, @@ -1124,6 +1134,10 @@ define(function (require) { this.plotly = Plotly.newPlot(this.plotDiv, this.datasets, this.plotOptions,{displayModeBar: false,doubleClick : false}); this.updateAxis(dataY.getInstancePath()); this.resize(); + + // track change in state of the widget + this.dirtyView = true; + return this; }, @@ -1196,6 +1210,9 @@ define(function (require) { } } } + + // after setting view through setView, reset dirty flag + this.dirtyView = false; } }); diff --git a/src/main/webapp/js/components/widgets/popup/Popup.js b/src/main/webapp/js/components/widgets/popup/Popup.js index 0214aa165..b71c8d545 100644 --- a/src/main/webapp/js/components/widgets/popup/Popup.js +++ b/src/main/webapp/js/components/widgets/popup/Popup.js @@ -108,6 +108,9 @@ define(function (require) { GEPPETTO.Console.debugLog("Hooked up custom handlers for " + this.id); } + // track change in state of the widget + this.dirtyView = true; + return this; }, @@ -176,6 +179,10 @@ define(function (require) { if(this.collapsed){ this.$el.dialogExtend("collapse"); } + + // track change in state of the widget + this.dirtyView = true; + return this; }, @@ -288,6 +295,10 @@ define(function (require) { // trigger routine that hooks up handlers hookupCustomHandlers(this.customHandlers, $("#" + this.id), this); + + // track change in state of the widget + this.dirtyView = true; + return this; }, @@ -321,6 +332,8 @@ define(function (require) { setButtonBarControls : function(controls){ this.buttonBarControls = controls; + // track change in state of the widget + this.dirtyView = true; }, setButtonBarConfiguration : function(configuration){ @@ -340,6 +353,9 @@ define(function (require) { $('.colorpicker-visible').addClass('colorpicker-hidden').removeClass('colorpicker-visible'); } }); + + // track change in state of the widget + this.dirtyView = true; }, destroy: function () { @@ -402,6 +418,9 @@ define(function (require) { this.setButtonBarConfiguration(view.componentSpecific.buttonBarConfig); } } + + // after setting view through setView, reset dirty flag + this.dirtyView = false; } }); }); diff --git a/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js b/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js index 9e445bf15..5cd20d968 100644 --- a/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js +++ b/src/main/webapp/js/components/widgets/stackViewer/controllers/StackViewerController.js @@ -55,7 +55,8 @@ define(function (require) { */ addStackViewerWidget: function (isStateless) { if(isStateless == undefined){ - isStateless = false; + // stateless by default + isStateless = true; } //look for a name and id for the new widget diff --git a/src/main/webapp/js/components/widgets/treevisualiser/TreeVisualiser.js b/src/main/webapp/js/components/widgets/treevisualiser/TreeVisualiser.js index 7d7f8078e..f7ba34379 100644 --- a/src/main/webapp/js/components/widgets/treevisualiser/TreeVisualiser.js +++ b/src/main/webapp/js/components/widgets/treevisualiser/TreeVisualiser.js @@ -1,4 +1,3 @@ - /** * Tree Visualiser Widget * diff --git a/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js b/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js index 376dbd114..9022fefdc 100644 --- a/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js +++ b/src/main/webapp/js/components/widgets/variablevisualiser/VariableVisualiser.js @@ -89,6 +89,10 @@ define(function (require) { this.setHeader(this.variable.name); this.updateVariable(0, false); + + // track change in state of the widget + this.dirtyView = true; + return "Variable visualisation added to widget"; }, @@ -174,6 +178,9 @@ define(function (require) { var variable = eval(view.data); this.setVariable(variable); } + + // after setting view through setView, reset dirty flag + this.dirtyView = false; } }); }); From 8139b75021823b9b5e1c6cb2a2dace35aaa0f95e Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 26 Apr 2017 12:13:34 -0700 Subject: [PATCH 116/339] fixing pathsh to new views path --- src/main/webapp/js/geppettoProject/ProjectFactory.js | 2 +- src/main/webapp/js/geppettoProject/model/ExperimentNode.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/geppettoProject/ProjectFactory.js b/src/main/webapp/js/geppettoProject/ProjectFactory.js index 650cda905..8ee0256f9 100644 --- a/src/main/webapp/js/geppettoProject/ProjectFactory.js +++ b/src/main/webapp/js/geppettoProject/ProjectFactory.js @@ -23,7 +23,7 @@ define(function (require) { name: project.name, type: project.type, id: project.id, - view: (project.view != undefined) ? project.view.view : undefined, + view: (project.view != undefined) ? project.view.viewStates : undefined, _metaType: GEPPETTO.Resources.PROJECT_NODE }); diff --git a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js index dede4650a..90be5d356 100644 --- a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js +++ b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js @@ -487,8 +487,8 @@ define(['backbone'], function(require) { var views = undefined; if(viewObject != undefined){ - if(viewObject.view!=undefined){ - views = viewObject.view.members; + if(viewObject.viewStates!=undefined){ + views = viewObject.viewStates.members; } } From 578ec710cf48ac12fb2b162a2c45ea0a73feb9a9 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 27 Apr 2017 00:18:00 -0700 Subject: [PATCH 117/339] tutorial don't shake on tutorial change --- .../js/components/interface/tutorial/TutorialModule.js | 8 ++++++-- src/main/webapp/js/geppettoProject/model/ProjectNode.js | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index ee8c46869..11b5479fc 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -152,9 +152,13 @@ define(function (require) { p.width(width+"px"); $(self.__container).css("width",width+"px"); } + $(self.__container).css("top","30%"); + $(self.__container).css("left","30%"); }; - - p.effect("shake", {distance:5, times: 3}, 500, callback); + + if(!started){ + p.effect("shake", {distance:5, times: 3}, 500, callback); + } }, diff --git a/src/main/webapp/js/geppettoProject/model/ProjectNode.js b/src/main/webapp/js/geppettoProject/model/ProjectNode.js index 860d223be..7d0406f4a 100644 --- a/src/main/webapp/js/geppettoProject/model/ProjectNode.js +++ b/src/main/webapp/js/geppettoProject/model/ProjectNode.js @@ -367,9 +367,12 @@ define(['backbone'], function (require) { var views = undefined; if(viewsString != undefined){ - views = JSON.parse(viewsString); + if(viewsString.members!=undefined){ + if(viewsString.members.views!=undefined){ + views = viewsString.members; + } + } } - return views; }, From d16c65ecaa65884509bb2ce42514df42b4d63d4b Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 27 Apr 2017 00:18:00 -0700 Subject: [PATCH 118/339] tutorial don't shake on tutorial change From 3cb9eb6a4598839260372e6b7c07e787b58423b1 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 27 Apr 2017 12:23:33 -0700 Subject: [PATCH 119/339] reverting way to read views --- src/main/webapp/js/geppettoProject/model/ExperimentNode.js | 2 +- src/main/webapp/js/geppettoProject/model/ProjectNode.js | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js index 90be5d356..76e720730 100644 --- a/src/main/webapp/js/geppettoProject/model/ExperimentNode.js +++ b/src/main/webapp/js/geppettoProject/model/ExperimentNode.js @@ -488,7 +488,7 @@ define(['backbone'], function(require) { if(viewObject != undefined){ if(viewObject.viewStates!=undefined){ - views = viewObject.viewStates.members; + views = JSON.parse(viewObject.viewStates); } } diff --git a/src/main/webapp/js/geppettoProject/model/ProjectNode.js b/src/main/webapp/js/geppettoProject/model/ProjectNode.js index 7d0406f4a..8a1b421a1 100644 --- a/src/main/webapp/js/geppettoProject/model/ProjectNode.js +++ b/src/main/webapp/js/geppettoProject/model/ProjectNode.js @@ -367,11 +367,7 @@ define(['backbone'], function (require) { var views = undefined; if(viewsString != undefined){ - if(viewsString.members!=undefined){ - if(viewsString.members.views!=undefined){ - views = viewsString.members; - } - } + views = JSON.parse(viewsString); } return views; }, From 51bae6f66fbb11171ddfa1949cb43da2fe6d109b Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 27 Apr 2017 14:13:47 -0700 Subject: [PATCH 120/339] always centers tutorials when opening --- .../interface/tutorial/TutorialModule.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index 11b5479fc..5c50c6a78 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -152,8 +152,6 @@ define(function (require) { p.width(width+"px"); $(self.__container).css("width",width+"px"); } - $(self.__container).css("top","30%"); - $(self.__container).css("left","30%"); }; if(!started){ @@ -245,8 +243,18 @@ define(function (require) { dialog.find("div.ui-dialog-titlebar").prepend(button); $(button).addClass("widget-title-bar-button"); $(this.__container).css("overflow","scroll"); - //$(this.__container).css("height","93%"); } + + + //centers the tutorials + var screenWidth = $( window ).width(); + var screenHeight = $( window ).height(); + + var left = (screenWidth/2) - ($(this.__container).parent().width()/2); + var top = (screenHeight/2) - ($(this.__container).parent().height()/2); + + $(this.__container).parent().css("top",top+"px"); + $(this.__container).parent().css("left",left+"px"); }, componentDidMount:function(){ From e00ded365cdbef09b2eb717c9a77d4c5fa4dd76b Mon Sep 17 00:00:00 2001 From: jrmartin Date: Fri, 28 Apr 2017 12:32:39 -0700 Subject: [PATCH 121/339] doesn't show all added tutorials when loading them at start up --- .../interface/tutorial/TutorialModule.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index 5c50c6a78..6f1739fd1 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -51,8 +51,7 @@ define(function (require) { $.cookie=require('js-cookie'); var Tutorial = React.createClass({ - - /** + /** * Stores cookie to avoid showing tutorial next time at startup */ dontShowAtStartup: function(val){ @@ -178,7 +177,7 @@ define(function (require) { dataType: 'json', url: tutorialURL, success: function (responseData, textStatus, jqXHR) { - self.loadTutorial(responseData); + self.loadTutorial(responseData, false); }, error: function (responseData, textStatus, errorThrown) { throw ("Error retrieving tutorial: " + responseData + " with error " + errorThrown); @@ -186,13 +185,15 @@ define(function (require) { }); }, - loadTutorial : function(tutorialData){ + loadTutorial : function(tutorialData,start){ this.state.tutorialData[tutorialData.name] = tutorialData; this.state.activeTutorial=tutorialData.name; this.state.currentStep=0; - this.forceUpdate(); if(!this.getCookie()){ - this.start(); + if(start){ + this.forceUpdate(); + this.start(); + } } }, @@ -269,7 +270,7 @@ define(function (require) { self.addTutorial(self.props.tutorialURL); } else if(self.props.tutorialData){ - self.loadTutorial(self.props.tutorialData); + self.loadTutorial(self.props.tutorialData,true); } self.dontShowTutorial = true; } From d5200093bb5367400d10e0ed0e240417479f9ddd Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sat, 29 Apr 2017 21:13:49 +0100 Subject: [PATCH 122/339] resize container on dialog maximise, restore, etc. --- .../components/interface/3dCanvas/Canvas.js | 27 ++++++++++--------- .../webapp/js/components/widgets/NewWidget.js | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 90533c24b..a195b3be0 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -264,18 +264,12 @@ define(function (require) { } }, false); - - - $("#" + this.props.id).on("dialogresizestop", function (event, ui) { - var height=ui.size.height-40; - var width=ui.size.width-30; - $(that.container).height(height); - $(that.container).width(width); + $("#" + this.props.id).on("dialogresizestop resizeEnd", function (event, ui) { + var [width, height] = that.setContainerDimensions() that.camera.aspect = width / height; that.camera.updateProjectionMatrix(); that.renderer.setSize(width, height); that.composer.setSize(width, height); - }); this.renderer.domElement.addEventListener('mousemove', function (event) { @@ -288,6 +282,17 @@ define(function (require) { } }, + /** + * Set container dimensions depending on parent dialog + */ + setContainerDimensions: function(){ + var containerSelector = $(this.container); + var height=containerSelector.parent().height()-40 + var width=containerSelector.parent().width()-30 + containerSelector.height(height); + containerSelector.width(width); + return [width, height]; + }, /** * Reset camera for scene. @@ -568,10 +573,8 @@ define(function (require) { Detector.addGetWebGLMessage(); } else { this.factory = new SceneFactory(this); - var containerSelector = $("#" + this.props.id + "_component"); - containerSelector.height(containerSelector.parent().height()-40); - containerSelector.width(containerSelector.parent().width()-30); - this.container = containerSelector.get(0); + this.container = $("#" + this.props.id + "_component").get(0); + this.setContainerDimensions(); this.setupScene(); this.setupCamera(); this.setupRenderer(); diff --git a/src/main/webapp/js/components/widgets/NewWidget.js b/src/main/webapp/js/components/widgets/NewWidget.js index 5200af5dd..fb3e88a92 100644 --- a/src/main/webapp/js/components/widgets/NewWidget.js +++ b/src/main/webapp/js/components/widgets/NewWidget.js @@ -544,10 +544,10 @@ define(function (require) { }, "maximize" : function(evt, dlg) { that.setTrasparentBackground(false); - $(this).trigger('resizeEnd'); var divheight = $(window).height(); var divwidth = $(window).width(); that.$el.dialog({ height: divheight, width: divwidth }); + $(this).trigger('resizeEnd'); that.maximize = true; }, "restore" : function(evt, dlg) { From af716879f63edb4ed3b62a1df661ff8cd5b4c135 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 30 Apr 2017 00:59:12 +0100 Subject: [PATCH 123/339] migrate widgets functionality to react container framework --- .../webapp/js/components/ComponentFactory.js | 134 +++++++++++++++++- .../components/interface/3dCanvas/Canvas.js | 1 + .../interface/jsConsole/GEPPETTO.Console.js | 5 +- .../webapp/js/components/widgets/NewWidget.js | 12 +- .../js/components/widgets/WidgetsListener.js | 58 ++++---- 5 files changed, 173 insertions(+), 37 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 5da6f4e25..c85ea410f 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -39,7 +39,8 @@ define(function (require) { var spinner=require('./interface/loadingSpinner/LoadingSpinner.js'); var addWidget = require('./widgets/NewWidget.js'); - + var _widgets = {}; + //All the components potentially instantiable go here var components = { 'FORM':'interface/form/Form', @@ -69,7 +70,7 @@ define(function (require) { 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer', 'CAROUSEL': 'interface/carousel/Carousel', - '3DCANVAS': 'interface/3dCanvas/Canvas', + 'CANVAS3D': 'interface/3dCanvas/Canvas', //'WIDGETCONTAINER': 'widgets/WidgetContainer' } @@ -91,11 +92,138 @@ define(function (require) { }, + camelize: function(str) { + return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) { + if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces + return index == 0 ? match.toUpperCase() : match.toLowerCase(); + }); + }, + + + /** + * Get an available id for an specific widget + * + * @module WidgetUtility + * @param {String} prefix + * @param {Array} widgetsList + * @returns {String} id - Available id for a widget + */ + getAvailableWidgetId: function (prefix, widgetsList) { + var index = 0; + var id = ""; + var available; + + do { + index++; + id = prefix + index; + available = true; + + for (var p in widgetsList) { + if (widgetsList[p].getId().toUpperCase() == id.toUpperCase()) { + available = false; + break; + } + } + } + while (available == false); + + return this.camelize(id); + }, + + /** + * Get the comments of a given widget file through an Ajax call. This is used to extract the comments on the methods + * and visualize them when using the help command. + * + * @param {String} file + */ + getFileComments: function (file) { + // var comments = ""; + // if (comments.length == 0) { + var comments = []; + //retrieve the script to get the comments for all the methods + $.ajax({ + async: false, + type: 'GET', + url: file, + dataType: "text", + //at success, read the file and extract the comments + success: function (data) { + var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; + comments = data.match(STRIP_COMMENTS); + }, + error: function () { + console.log('error fetching file with Ajax request'); + } + }); + + // comments = fetchedComments; + // } + return comments; + }, + addWidget: function(componentID, properties, callback){ + + if (!("id" in properties)){ + var widgetsList = []; + if(componentID in _widgets){ + widgetsList = _widgets[componentID].widgets; + } + else{ + _widgets[componentID] = {widgets: [], comments: this.getFileComments("geppetto/js/components/" + components[componentID] + ".js")} + } + properties["id"] = this.getAvailableWidgetId(componentID, widgetsList); + + + + } + + var that=this; require(["./" + components[componentID]], function(loadedModule){ var component = React.createFactory(addWidget(loadedModule))(properties); - var renderedComponent = that.renderComponent(component, document.getElementById('widgetContainer'), callback); + var renderedComponent = window[properties.id] = that.renderComponent(component, document.getElementById('widgetContainer'), callback); + _widgets[componentID].widgets.push(renderedComponent); + GEPPETTO.Console.updateHelpCommand(renderedComponent, properties.id, _widgets[componentID].comments); + GEPPETTO.Console.updateTags(properties.id, renderedComponent); + + //registers remove handler for widget + renderedComponent.$el.on("remove", function () { + + console.log('Pako'); + //remove tags and delete object upon destroying widget + GEPPETTO.Console.removeCommands(properties.id); + var widgetsList = _widgets[componentID].widgets; + for (var p in widgetsList) { + if (widgetsList[p].getId() == this.id) { + widgetsList.splice(p, 1); + break; + } + } + }); + + //register resize handler for widget + renderedComponent.$el.on("dialogresizestop", function (event, ui) { + + var height = ui.size.height; + var width = ui.size.width; + + GEPPETTO.Console.executeImplicitCommand(properties.id + ".setSize(" + height + "," + width + ")"); + + var left = ui.position.left; + var top = ui.position.top; + + window[properties.id].setPosition(left, top); + }); + + // //register drag handler for widget + renderedComponent.$el.on("dialogdragstop", function (event, ui) { + + var left = ui.position.left; + var top = ui.position.top; + + GEPPETTO.Console.executeImplicitCommand(properties.id + ".setPosition(" + left + "," + top + ")"); + }); + return renderedComponent; }); }, diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index a195b3be0..0457b413a 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -71,6 +71,7 @@ define(function (require) { /** * Sets up the camera that is used to view the objects in the 3D Scene. + * @command setupCamera() */ setupCamera: function () { // Camera diff --git a/src/main/webapp/js/components/interface/jsConsole/GEPPETTO.Console.js b/src/main/webapp/js/components/interface/jsConsole/GEPPETTO.Console.js index 6b02baf2e..e6845dcd9 100644 --- a/src/main/webapp/js/components/interface/jsConsole/GEPPETTO.Console.js +++ b/src/main/webapp/js/components/interface/jsConsole/GEPPETTO.Console.js @@ -591,7 +591,8 @@ define(function (require) { var commandsCount = commands.length; - var proto = object.__proto__; + //var proto = object.__proto__; + var proto = object; // find all functions of object Simulation for (var prop in proto) { if (typeof proto[prop] === "function" && proto.hasOwnProperty(prop)) { @@ -649,7 +650,7 @@ define(function (require) { } if (proto.__proto__ != null) { - GEPPETTO.Console.updateHelpCommand(proto, id, comments); + GEPPETTO.Console.updateHelpCommand(proto.__proto__, id, comments); } }, diff --git a/src/main/webapp/js/components/widgets/NewWidget.js b/src/main/webapp/js/components/widgets/NewWidget.js index fb3e88a92..b4d311af0 100644 --- a/src/main/webapp/js/components/widgets/NewWidget.js +++ b/src/main/webapp/js/components/widgets/NewWidget.js @@ -37,6 +37,11 @@ define(function (require) { defaultSize: { height: 300, width: 350 }, defaultPosition: { left: "50%", top: "50%" } }); + + } + + help () { + return GEPPETTO.Console.getObjectCommands(this.props.id); } /** @@ -610,6 +615,7 @@ define(function (require) { that.maximize = true; } }); + } /** @@ -637,9 +643,9 @@ define(function (require) { 'Try the online documentation instead.'; } - setController(controller) { - this.controller = controller; - } + // setController(controller) { + // this.controller = controller; + // } showHistoryIcon(show) { var that = this; diff --git a/src/main/webapp/js/components/widgets/WidgetsListener.js b/src/main/webapp/js/components/widgets/WidgetsListener.js index 7c3c2524a..3476dcfdd 100644 --- a/src/main/webapp/js/components/widgets/WidgetsListener.js +++ b/src/main/webapp/js/components/widgets/WidgetsListener.js @@ -54,43 +54,43 @@ define(function (require) { GEPPETTO.Console.debugLog('added new observer'); } - //registers remove handler for widget - $("#" + widgetID).on("remove", function () { - //remove tags and delete object upon destroying widget - GEPPETTO.Console.removeCommands(widgetID); - - var widgets = controller.getWidgets(); - - for (var p in widgets) { - if (widgets[p].getId() == this.id) { - widgets.splice(p, 1); - break; - } - } - }); + // //registers remove handler for widget + // $("#" + widgetID).on("remove", function () { + // //remove tags and delete object upon destroying widget + // GEPPETTO.Console.removeCommands(widgetID); + + // var widgets = controller.getWidgets(); + + // for (var p in widgets) { + // if (widgets[p].getId() == this.id) { + // widgets.splice(p, 1); + // break; + // } + // } + // }); - //register resize handler for widget - $("#" + widgetID).on("dialogresizestop", function (event, ui) { + // //register resize handler for widget + // $("#" + widgetID).on("dialogresizestop", function (event, ui) { - var height = ui.size.height; - var width = ui.size.width; + // var height = ui.size.height; + // var width = ui.size.width; - GEPPETTO.Console.executeImplicitCommand(widgetID + ".setSize(" + height + "," + width + ")"); + // GEPPETTO.Console.executeImplicitCommand(widgetID + ".setSize(" + height + "," + width + ")"); - var left = ui.position.left; - var top = ui.position.top; + // var left = ui.position.left; + // var top = ui.position.top; - window[widgetID].setPosition(left, top); - }); + // window[widgetID].setPosition(left, top); + // }); - //register drag handler for widget - $("#" + widgetID).on("dialogdragstop", function (event, ui) { + // //register drag handler for widget + // $("#" + widgetID).on("dialogdragstop", function (event, ui) { - var left = ui.position.left; - var top = ui.position.top; + // var left = ui.position.left; + // var top = ui.position.top; - GEPPETTO.Console.executeImplicitCommand(widgetID + ".setPosition(" + left + "," + top + ")"); - }); + // GEPPETTO.Console.executeImplicitCommand(widgetID + ".setPosition(" + left + "," + top + ")"); + // }); }, /** From 36764e86e565d9a21c90071c787c25af08b2d8d2 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 30 Apr 2017 13:31:42 +0100 Subject: [PATCH 124/339] more work on react container and refactor --- .../webapp/js/components/ComponentFactory.js | 239 +++-------------- .../webapp/js/components/WidgetController.js | 249 ++++++++++++++++++ .../webapp/js/components/WidgetFactory.js | 63 +++++ .../interface/jsConsole/GEPPETTO.Console.js | 41 +-- src/main/webapp/js/pages/geppetto/main.js | 1 + 5 files changed, 377 insertions(+), 216 deletions(-) create mode 100644 src/main/webapp/js/components/WidgetController.js create mode 100644 src/main/webapp/js/components/WidgetFactory.js diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index c85ea410f..55908cad2 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -39,43 +39,41 @@ define(function (require) { var spinner=require('./interface/loadingSpinner/LoadingSpinner.js'); var addWidget = require('./widgets/NewWidget.js'); - var _widgets = {}; - - //All the components potentially instantiable go here - var components = { - 'FORM':'interface/form/Form', - 'PANEL':'controls/panel/Panel', - 'LOGO':'interface/logo/Logo', - 'LOADINGSPINNER':'interface/loadingSpinner/LoadingSpinner', - 'SAVECONTROL':'interface/save/SaveControl', - 'TOGGLEBUTTON' : 'controls/toggleButton/ToggleButton', - 'CONTROLPANEL':'interface/controlPanel/controlpanel', - 'SPOTLIGHT':'interface/spotlight/spotlight', - 'MENUBUTTON':'controls/menuButton/MenuButton', - 'FOREGROUND':'interface/foregroundControls/ForegroundControls', - 'EXPERIMENTSTABLE':'interface/experimentsTable/ExperimentsTable', - 'HOME':'interface/home/HomeControl', - 'SIMULATIONCONTROLS':'interface/simulationControls/ExperimentControls', - 'CAMERACONTROLS': 'interface/cameraControls/CameraControls', - 'SHARE':'interface/share/share', - 'INFOMODAL':'controls/modals/InfoModal', - 'MDMODAL':'controls/modals/MarkDownModal', - 'QUERY':'interface/query/query', - 'TUTORIAL':'interface/tutorial/TutorialModule', - 'PYTHONCONSOLE': 'interface/pythonConsole/PythonConsole', - 'CHECKBOX': 'controls/Checkbox', - 'TEXTFIELD': 'controls/TextField', - 'RAISEDBUTTON': 'controls/RaisedButton', - 'DICOMVIEWER': 'interface/dicomViewer/DicomViewer', - 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', - 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer', - 'CAROUSEL': 'interface/carousel/Carousel', - 'CANVAS3D': 'interface/3dCanvas/Canvas', - //'WIDGETCONTAINER': 'widgets/WidgetContainer' - } - GEPPETTO.ComponentFactory = { + + //All the components potentially instantiable go here + components : { + 'FORM':'interface/form/Form', + 'PANEL':'controls/panel/Panel', + 'LOGO':'interface/logo/Logo', + 'LOADINGSPINNER':'interface/loadingSpinner/LoadingSpinner', + 'SAVECONTROL':'interface/save/SaveControl', + 'TOGGLEBUTTON' : 'controls/toggleButton/ToggleButton', + 'CONTROLPANEL':'interface/controlPanel/controlpanel', + 'SPOTLIGHT':'interface/spotlight/spotlight', + 'MENUBUTTON':'controls/menuButton/MenuButton', + 'FOREGROUND':'interface/foregroundControls/ForegroundControls', + 'EXPERIMENTSTABLE':'interface/experimentsTable/ExperimentsTable', + 'HOME':'interface/home/HomeControl', + 'SIMULATIONCONTROLS':'interface/simulationControls/ExperimentControls', + 'CAMERACONTROLS': 'interface/cameraControls/CameraControls', + 'SHARE':'interface/share/share', + 'INFOMODAL':'controls/modals/InfoModal', + 'MDMODAL':'controls/modals/MarkDownModal', + 'QUERY':'interface/query/query', + 'TUTORIAL':'interface/tutorial/TutorialModule', + 'PYTHONCONSOLE': 'interface/pythonConsole/PythonConsole', + 'CHECKBOX': 'controls/Checkbox', + 'TEXTFIELD': 'controls/TextField', + 'RAISEDBUTTON': 'controls/RaisedButton', + 'DICOMVIEWER': 'interface/dicomViewer/DicomViewer', + 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', + 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer', + 'CAROUSEL': 'interface/carousel/Carousel', + 'CANVAS3D': 'interface/3dCanvas/Canvas', + //'WIDGETCONTAINER': 'widgets/WidgetContainer' + }, loadSpinner:function(){ //We require this synchronously to properly show spinner when loading projects @@ -84,7 +82,7 @@ define(function (require) { addComponent: function(componentID, properties, container, callback){ var that=this; - require(["./" + components[componentID]], function(loadedModule){ + require(["./" + GEPPETTO.ComponentFactory.components[componentID]], function(loadedModule){ var component = React.createFactory(loadedModule)(properties) var renderedComponent = that.renderComponent(component, container, callback); return renderedComponent; @@ -92,178 +90,25 @@ define(function (require) { }, - camelize: function(str) { - return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) { - if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces - return index == 0 ? match.toUpperCase() : match.toLowerCase(); - }); - }, - - - /** - * Get an available id for an specific widget - * - * @module WidgetUtility - * @param {String} prefix - * @param {Array} widgetsList - * @returns {String} id - Available id for a widget - */ - getAvailableWidgetId: function (prefix, widgetsList) { - var index = 0; - var id = ""; - var available; - - do { - index++; - id = prefix + index; - available = true; - - for (var p in widgetsList) { - if (widgetsList[p].getId().toUpperCase() == id.toUpperCase()) { - available = false; - break; - } - } - } - while (available == false); - - return this.camelize(id); - }, - - /** - * Get the comments of a given widget file through an Ajax call. This is used to extract the comments on the methods - * and visualize them when using the help command. - * - * @param {String} file - */ - getFileComments: function (file) { - // var comments = ""; - // if (comments.length == 0) { - var comments = []; - //retrieve the script to get the comments for all the methods - $.ajax({ - async: false, - type: 'GET', - url: file, - dataType: "text", - //at success, read the file and extract the comments - success: function (data) { - var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; - comments = data.match(STRIP_COMMENTS); - }, - error: function () { - console.log('error fetching file with Ajax request'); - } - }); - - // comments = fetchedComments; - // } - return comments; - }, - addWidget: function(componentID, properties, callback){ - - if (!("id" in properties)){ - var widgetsList = []; - if(componentID in _widgets){ - widgetsList = _widgets[componentID].widgets; - } - else{ - _widgets[componentID] = {widgets: [], comments: this.getFileComments("geppetto/js/components/" + components[componentID] + ".js")} - } - properties["id"] = this.getAvailableWidgetId(componentID, widgetsList); + var that=this; + require(["./" + GEPPETTO.ComponentFactory.components[componentID]], function(loadedModule){ - - } - + var widgetController = GEPPETTO.WidgetFactory.getController(componentID); + if (!("id" in properties)){ + properties["id"] = widgetController.getAvailableWidgetId(); + } - var that=this; - require(["./" + components[componentID]], function(loadedModule){ var component = React.createFactory(addWidget(loadedModule))(properties); var renderedComponent = window[properties.id] = that.renderComponent(component, document.getElementById('widgetContainer'), callback); - _widgets[componentID].widgets.push(renderedComponent); - GEPPETTO.Console.updateHelpCommand(renderedComponent, properties.id, _widgets[componentID].comments); - GEPPETTO.Console.updateTags(properties.id, renderedComponent); - - //registers remove handler for widget - renderedComponent.$el.on("remove", function () { - - console.log('Pako'); - //remove tags and delete object upon destroying widget - GEPPETTO.Console.removeCommands(properties.id); - var widgetsList = _widgets[componentID].widgets; - for (var p in widgetsList) { - if (widgetsList[p].getId() == this.id) { - widgetsList.splice(p, 1); - break; - } - } - }); - - //register resize handler for widget - renderedComponent.$el.on("dialogresizestop", function (event, ui) { - - var height = ui.size.height; - var width = ui.size.width; - - GEPPETTO.Console.executeImplicitCommand(properties.id + ".setSize(" + height + "," + width + ")"); - - var left = ui.position.left; - var top = ui.position.top; - - window[properties.id].setPosition(left, top); - }); - - // //register drag handler for widget - renderedComponent.$el.on("dialogdragstop", function (event, ui) { - - var left = ui.position.left; - var top = ui.position.top; - - GEPPETTO.Console.executeImplicitCommand(properties.id + ".setPosition(" + left + "," + top + ")"); - }); - + + widgetController.registerWidget(renderedComponent) return renderedComponent; }); }, - renderComponent: function(component, container, callback){ - //Let's create a dialog - if (container == undefined){ - - //create the dialog window for the widget - var dialog = $("
                ").dialog( - { - resizable: true, - draggable: true, - top: 10, - height: 300, - width: 350, - dialogClass: component.props.id + "_dialog", - close: function (event, ui) { - if (event.originalEvent && - $(event.originalEvent.target).closest(".ui-dialog-titlebar-close").length) { - $("#" + this.id).remove(); - } - } - }); - - var dialogParent = dialog.parent(); - var that = this; - - //remove the jQuery UI icon - dialogParent.find("button.ui-dialog-titlebar-close").html(""); - dialogParent.find("button").append(""); - - - //Take focus away from close button - dialogParent.find("button.ui-dialog-titlebar-close").blur(); - - container = dialog.get(0); - } - return ReactDOM.render(component, container, callback); } }; diff --git a/src/main/webapp/js/components/WidgetController.js b/src/main/webapp/js/components/WidgetController.js new file mode 100644 index 000000000..b3cc4d06b --- /dev/null +++ b/src/main/webapp/js/components/WidgetController.js @@ -0,0 +1,249 @@ +/******************************************************************************* + * + * Copyright (c) 2011, 2016 OpenWorm. + * http://openworm.org + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the MIT License + * which accompanies this distribution, and is available at + * http://opensource.org/licenses/MIT + * + * Contributors: + * OpenWorm - http://openworm.org/people.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + *******************************************************************************/ +class WidgetController { + + constructor(componentID) { + this.componentID = componentID; + this.widgets = []; + this.comments = this.getFileComments("geppetto/js/components/" + GEPPETTO.ComponentFactory.components[componentID] + ".js"); + this.history = []; + this.WIDGET_EVENT_TYPE = { + DELETE: "delete", + UPDATE: "update", + RESET_DATA: "reset", + SELECTION_CHANGED: "select", + } + } + + camelize(str) { + return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) { + if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces + return index == 0 ? match.toUpperCase() : match.toLowerCase(); + }); + } + + registerWidget(widget){ + var that = this; + this.widgets.push(widget); + + GEPPETTO.Console.updateHelpCommand(widget, widget.getId(), this.comments); + GEPPETTO.Console.updateTags(widget.getId(), widget); + + //registers remove handler for widget + widget.$el.on("remove", function () { + + console.log('Pako'); + //remove tags and delete object upon destroying widget + GEPPETTO.Console.removeCommands(widget.getId()); + var widgetsList = that.widgets; + for (var p in widgetsList) { + if (widgetsList[p].getId() == this.id) { + widgetsList.splice(p, 1); + break; + } + } + }); + + //register resize handler for widget + widget.$el.on("dialogresizestop", function (event, ui) { + + var height = ui.size.height; + var width = ui.size.width; + + GEPPETTO.Console.executeImplicitCommand(widget.getId() + ".setSize(" + height + "," + width + ")"); + + var left = ui.position.left; + var top = ui.position.top; + + window[widget.getId()].setPosition(left, top); + }); + + // //register drag handler for widget + widget.$el.on("dialogdragstop", function (event, ui) { + + var left = ui.position.left; + var top = ui.position.top; + + GEPPETTO.Console.executeImplicitCommand(widget.getId() + ".setPosition(" + left + "," + top + ")"); + }); + } + + /** + * Get the comments of a given widget file through an Ajax call. This is used to extract the comments on the methods + * and visualize them when using the help command. + * + * @param {String} file + */ + getFileComments(file) { + // var comments = ""; + // if (comments.length == 0) { + var comments = []; + //retrieve the script to get the comments for all the methods + $.ajax({ + async: false, + type: 'GET', + url: file, + dataType: "text", + //at success, read the file and extract the comments + success: function (data) { + var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; + comments = data.match(STRIP_COMMENTS); + }, + error: function () { + console.log('error fetching file with Ajax request'); + } + }); + + // comments = fetchedComments; + // } + return comments; + } + + /** + * Get an available id for an specific widget + * + * @module WidgetUtility + * @param {String} prefix + * @param {Array} widgetsList + * @returns {String} id - Available id for a widget + */ + getAvailableWidgetId () { + var index = 0; + var id = ""; + var available; + + do { + index++; + id = this.componentID + index; + available = true; + + for (var p in this.widgets) { + if (this.widgets[p].getId().toUpperCase() == id.toUpperCase()) { + available = false; + break; + } + } + } + while (available == false); + + return this.camelize(id); + } + + /** + * Removes existing plotting widgets + */ + removeWidgets () { + //remove all existing widgets + for (var i = 0; i < this.widgets.length; i++) { + var widget = this.widgets[i]; + + //remove commands + GEPPETTO.Console.removeCommands(widget.getId()); + + widget.destroy(); + + i--; + } + + this.widgets = []; + } + /** + * Returns all plotting widgets objects + * + * @returns {Array} Array containing all plots + */ + getWidgets () { + return this.widgets; + } + + addToHistory (label, method, args, id) { + var elementPresentInHistory = false; + for (var i = 0; i < this.history.length; i++) { + if (this.history[i].label == label && this.history[i].method == method) { + elementPresentInHistory = true; + //moves it to the first position + this.history.splice(0, 0, this.history.splice(i, 1)[0]); + break; + } + } + if (!elementPresentInHistory) { + this.history.unshift({ + "label": label, + "method": method, + "arguments": args, + }); + } + + var widget = this.getWidgetById(id); + widget.updateNavigationHistoryBar(); + } + + /** + * Toggles variable visualiser widget on and off + */ + toggle () { + if (this.widgets.length > 0) { + this.on = !this.on; + for (var w in this.widgets) { + var widget = this.widgets[w]; + if (!this.on) { + widget.hide(); + } else { + widget.show(); + } + } + } + } + + getWidgetById(id){ + for (var i = 0; i< this.widgets.length; i++) { + var widget = this.widgets[i]; + if(widget.getId()==id){ + return widget; + } + } + + return null; + } + + update (event, parameters) { + //delete popup widget(s) + if (event == this.WIDGET_EVENT_TYPE.DELETE) { + this.removeWidgets(); + } + } + +} + +module.exports = WidgetController; + + diff --git a/src/main/webapp/js/components/WidgetFactory.js b/src/main/webapp/js/components/WidgetFactory.js new file mode 100644 index 000000000..715a2433b --- /dev/null +++ b/src/main/webapp/js/components/WidgetFactory.js @@ -0,0 +1,63 @@ +/******************************************************************************* + * + * Copyright (c) 2011, 2016 OpenWorm. + * http://openworm.org + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the MIT License + * which accompanies this distribution, and is available at + * http://opensource.org/licenses/MIT + * + * Contributors: + * OpenWorm - http://openworm.org/people.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + *******************************************************************************/ + +define(function (require) { + + return function (GEPPETTO) { + + var WidgetController = require('./WidgetController'); + + GEPPETTO.WidgetFactory = { + + _widgetsControllers : {}, + + getController: function(componentID) { + if(!(componentID in this._widgetsControllers)){ + this._widgetsControllers[componentID] = new WidgetController(componentID); + } + return this._widgetsControllers[componentID]; + }, + + /** + * Update all subscribed controller classes with new changes + * + * @param {Object} arguments - Set arguments with information to update the widgets + */ + update: function (event, parameters) { + for (var i = 0; i < _widgetsControllers.length; i++) { + this._widgetsControllers[i].update(event, parameters); + } + } + + }; + }; +}); diff --git a/src/main/webapp/js/components/interface/jsConsole/GEPPETTO.Console.js b/src/main/webapp/js/components/interface/jsConsole/GEPPETTO.Console.js index e6845dcd9..33cba3ee1 100644 --- a/src/main/webapp/js/components/interface/jsConsole/GEPPETTO.Console.js +++ b/src/main/webapp/js/components/interface/jsConsole/GEPPETTO.Console.js @@ -614,30 +614,33 @@ define(function (require) { commandsCount++; //match the function to comment var matchedDescription = ""; - for (var i = 0; i < comments.length; i++) { - var description = comments[i].toString(); - - //items matched - if (description.indexOf(prop) != -1) { - - /*series of formatting of the comments for the function, removes unnecessary - * blank and special characters. - */ - var splitComments = description.replace(/\*/g, "").split("\n"); - splitComments.splice(0, 1); - splitComments.splice(splitComments.length - 1, 1); - for (var s = 0; s < splitComments.length; s++) { - var line = splitComments[s].trim(); - if (line != "") { - //ignore the name line, already have it - if (line.indexOf("@command") == -1) { - //build description for function - matchedDescription += " " + line + "\n"; + if (comments != null){ + for (var i = 0; i < comments.length; i++) { + var description = comments[i].toString(); + + //items matched + if (description.indexOf(prop) != -1) { + + /*series of formatting of the comments for the function, removes unnecessary + * blank and special characters. + */ + var splitComments = description.replace(/\*/g, "").split("\n"); + splitComments.splice(0, 1); + splitComments.splice(splitComments.length - 1, 1); + for (var s = 0; s < splitComments.length; s++) { + var line = splitComments[s].trim(); + if (line != "") { + //ignore the name line, already have it + if (line.indexOf("@command") == -1) { + //build description for function + matchedDescription += " " + line + "\n"; + } } } } } } + //format and keep track of all commands available commandsFormmatted += (" -- " + functionName + "\n" + matchedDescription + "\n"); } diff --git a/src/main/webapp/js/pages/geppetto/main.js b/src/main/webapp/js/pages/geppetto/main.js index ae5632784..3acdf3451 100644 --- a/src/main/webapp/js/pages/geppetto/main.js +++ b/src/main/webapp/js/pages/geppetto/main.js @@ -45,6 +45,7 @@ require('../../../style/less/main.less'); var GEPPETTO = require('geppetto'); require('../../components/ComponentFactory')(GEPPETTO); +require('../../components/WidgetFactory')(GEPPETTO); GEPPETTO.ComponentFactory.loadSpinner(); From 775688e61c130d11d74f9482ac5112e539a19e2b Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 30 Apr 2017 17:59:13 +0100 Subject: [PATCH 125/339] remove log --- src/main/webapp/js/components/WidgetController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/WidgetController.js b/src/main/webapp/js/components/WidgetController.js index b3cc4d06b..b2c54ffcb 100644 --- a/src/main/webapp/js/components/WidgetController.js +++ b/src/main/webapp/js/components/WidgetController.js @@ -60,8 +60,8 @@ class WidgetController { //registers remove handler for widget widget.$el.on("remove", function () { + //FIXME: Called twice - console.log('Pako'); //remove tags and delete object upon destroying widget GEPPETTO.Console.removeCommands(widget.getId()); var widgetsList = that.widgets; From c8e62b02040f38b65f66b33df2569418e2ee6660 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 30 Apr 2017 21:33:59 +0100 Subject: [PATCH 126/339] remove comment --- src/main/webapp/webpack.config.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index 80136d500..61aa4e622 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -68,11 +68,7 @@ module.exports = { }, plugins: [ - new CopyWebpackPlugin(availableExtensions -// [ { from: 'extensions/geppetto-tibs/static/biography.html', -// to: 'biography.html' } ] - - ), + new CopyWebpackPlugin(availableExtensions), new HtmlWebpackPlugin({ filename: 'geppetto.vm', template: './js/pages/geppetto/geppetto.ejs', From 398257e3d51e168602dc941128b08eb6f19a4804 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 30 Apr 2017 21:34:24 +0100 Subject: [PATCH 127/339] make js path depend on url path --- .../java/org/geppetto/frontend/controllers/Application.java | 4 +++- src/main/webapp/js/pages/geppetto/geppetto.ejs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/Application.java b/src/main/java/org/geppetto/frontend/controllers/Application.java index c490fbc6d..69ae83573 100644 --- a/src/main/java/org/geppetto/frontend/controllers/Application.java +++ b/src/main/java/org/geppetto/frontend/controllers/Application.java @@ -106,8 +106,9 @@ else if(geppettoManager.getUser() == null) } @RequestMapping(value = "/geppetto", method = RequestMethod.GET) - public String geppetto(HttpServletRequest req) + public String geppetto(HttpServletRequest req, Model model) { + model.addAttribute("mainJsPath", "geppetto/build/main.bundle.js"); return getGeppetto(req); } @@ -116,6 +117,7 @@ public String geppettoWithContent(HttpServletRequest req, Model model, @PathVari { InputStream content = Application.class.getResourceAsStream("/build/static/" + page + ".html"); model.addAttribute("content", new String(new Scanner(content, "UTF-8").useDelimiter("\\A").next())); + model.addAttribute("mainJsPath", "../geppetto/build/main.bundle.js"); return getGeppetto(req); } diff --git a/src/main/webapp/js/pages/geppetto/geppetto.ejs b/src/main/webapp/js/pages/geppetto/geppetto.ejs index 2fbdc419f..9cbc6f532 100644 --- a/src/main/webapp/js/pages/geppetto/geppetto.ejs +++ b/src/main/webapp/js/pages/geppetto/geppetto.ejs @@ -108,6 +108,6 @@ $content ga('send', 'pageview'); - + From 466dc63c30707aac2080880a839f683187000e08 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 1 May 2017 01:22:08 +0100 Subject: [PATCH 128/339] adding popup to new container structure --- .../interface/popup/ButtonBarComponent.css | 70 ++++ .../interface/popup/ButtonBarComponent.js | 271 ++++++++++++ .../js/components/interface/popup/Popup.css | 141 +++++++ .../js/components/interface/popup/Popup.js | 396 ++++++++++++++++++ .../interface/popup/vendor/ajax-loader.gif | Bin 0 -> 4178 bytes .../vendor/bootstrap-colorpicker.min.css | 9 + .../popup/vendor/bootstrap-colorpicker.min.js | 1 + .../interface/popup/vendor/slick-theme.css | 182 ++++++++ .../interface/popup/vendor/slick.css | 117 ++++++ 9 files changed, 1187 insertions(+) create mode 100644 src/main/webapp/js/components/interface/popup/ButtonBarComponent.css create mode 100644 src/main/webapp/js/components/interface/popup/ButtonBarComponent.js create mode 100644 src/main/webapp/js/components/interface/popup/Popup.css create mode 100644 src/main/webapp/js/components/interface/popup/Popup.js create mode 100644 src/main/webapp/js/components/interface/popup/vendor/ajax-loader.gif create mode 100644 src/main/webapp/js/components/interface/popup/vendor/bootstrap-colorpicker.min.css create mode 100644 src/main/webapp/js/components/interface/popup/vendor/bootstrap-colorpicker.min.js create mode 100644 src/main/webapp/js/components/interface/popup/vendor/slick-theme.css create mode 100644 src/main/webapp/js/components/interface/popup/vendor/slick.css diff --git a/src/main/webapp/js/components/interface/popup/ButtonBarComponent.css b/src/main/webapp/js/components/interface/popup/ButtonBarComponent.css new file mode 100644 index 000000000..d83d4584c --- /dev/null +++ b/src/main/webapp/js/components/interface/popup/ButtonBarComponent.css @@ -0,0 +1,70 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2011, 2013 OpenWorm. + * http://openworm.org + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the MIT License + * which accompanies this distribution, and is available at + * http://opensource.org/licenses/MIT + * + * Contributors: + * OpenWorm - http://openworm.org/people.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + *******************************************************************************/ +.buttonbar-colorpicker { + background-color: rgba(66, 59, 59, 0.70); + border: 1px solid rgba(255, 255, 255, 0.25) !important; + border-radius: 0px !important; +} + +.colorpicker:after { + border-bottom: 6px solid rgba(255, 255, 255, 0.27) !important; +} + +.buttonBar-button { + height: 25px; + width: 33px; + margin: 0px; + background: none !important; + color: #fd6808; + font-size: 18px !important; + border: 0 !important; +} + +.buttonBar-button:hover { + font-size: 20px !important; + background: none !important; + color: #fc4b19; +} + +.buttonBarComponentDiv{ + margin-bottom: 5px; +} + +.button-bar-container { + width : 100%; + text-align: center; +} + +.button-bar-div { + height: 100%; +} \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/popup/ButtonBarComponent.js b/src/main/webapp/js/components/interface/popup/ButtonBarComponent.js new file mode 100644 index 000000000..448f71f5f --- /dev/null +++ b/src/main/webapp/js/components/interface/popup/ButtonBarComponent.js @@ -0,0 +1,271 @@ +define(function (require) { + + // var widgetUtility = require("../WidgetUtility"); + // widgetUtility.loadCss("geppetto/js/components/widgets/popup/ButtonBarComponent.css"); + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/widgets/popup/ButtonBarComponent.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + var React = require('react'); + var colorpicker = require('./vendor/bootstrap-colorpicker.min'); + + var ButtonBarComponent = React.createClass({ + colorPickerBtnId: '', + colorPickerActionFn: '', + + getInitialState: function () { + return { + + }; + }, + + componentDidMount: function () { + var that = this; + + if(that.props.instance!=null || that.props.instance!=undefined){ + that.props.resize(); + } + + // hookup color picker onChange + if (this.colorPickerBtnId != '') { + var path = this.props.instancePath; + var entity = eval(path); + var defColor = '0Xffffff'; + + // grab default color from instance + if (entity.hasCapability(GEPPETTO.Resources.VISUAL_CAPABILITY)) { + defColor = entity.getColor(); + } + + // init dat color picker + var coloPickerElement = $('#' + this.colorPickerBtnId); + coloPickerElement.colorpicker({format: 'hex', customClass: 'buttonbar-colorpicker'}); + coloPickerElement.colorpicker('setValue', defColor.replace(/0X/i, "#")); + + // closure on local scope at this point - hook on change event + coloPickerElement.on('changeColor', function (e) { + that.colorPickerActionFn(e.color.toHex().replace("#", "0x")); + $(this).css("color", e.color.toHex()); + }); + } + + if(this.props.buttonBarConfig.Events !=null || this.props.buttonBarConfig.Events!=undefined){ + this.props.geppetto.on(GEPPETTO.Events.Visibility_changed, function (instance) { + if(!$.isEmptyObject(that.props) || that.props != undefined){ + if(instance.getInstancePath() == that.props.instancePath){ + that.forceUpdate(); + }else{ + if((that.props.instance!=null || that.props.instance!=undefined) + && (instance.getParent()!=null || instance.getParent()!=undefined)){ + if(that.props.instance.getInstancePath() == instance.getParent().getInstancePath()){ + that.forceUpdate(); + } + } + } + } + }); + this.props.geppetto.on(GEPPETTO.Events.Select, function (instance) { + if(!$.isEmptyObject(that.props) || that.props != undefined){ + if(instance.getInstancePath() == that.props.instancePath){ + that.forceUpdate(); + }else{ + if((that.props.instance!=null || that.props.instance!=undefined) + && (instance.getParent()!=null || instance.getParent()!=undefined)){ + if(that.props.instance.getInstancePath() == instance.getParent().getInstancePath()){ + that.forceUpdate(); + } + } + } + } + }); + this.props.geppetto.on(GEPPETTO.Events.Color_set, function (instance) { + if(that.props!=null || that.props!=undefined){ + if(instance.instance.getInstancePath() == that.props.instancePath){ + that.forceUpdate(); + } + } + }); + } + }, + + + componentWillUnmount: function () { + console.log("unmount"); + this.props= {}; + }, + + replaceTokensWithPath: function(inputStr, path){ + return inputStr.replace(/\$instance\$/gi, path).replace(/\$instances\$/gi, '[' + path + ']'); + }, + + getActionString: function (control, path) { + var actionStr = ''; + + if (control.actions.length > 0) { + for (var i = 0; i < control.actions.length; i++) { + actionStr += ((i != 0) ? ";" : "") + this.replaceTokensWithPath(control.actions[i], path); + } + } + + return actionStr; + }, + + resolveCondition: function (control, path, negateCondition) { + if (negateCondition == undefined) { + negateCondition = false; + } + + var resolvedConfig = control; + + if (resolvedConfig.hasOwnProperty('condition')) { + // evaluate condition and reassign control depending on results + var conditionStr = this.replaceTokensWithPath(control.condition, path); + if (eval(conditionStr)) { + resolvedConfig = negateCondition ? resolvedConfig.false : resolvedConfig.true; + } else { + resolvedConfig = negateCondition ? resolvedConfig.true : resolvedConfig.false; + } + } + + return resolvedConfig; + }, + + refresh: function() { + this.forceUpdate(); + }, + + render: function () { + var showControls = this.props.showControls; + var config = this.props.buttonBarConfig; + var path = this.props.instancePath; + var ctrlButtons = []; + + // retrieve entity/instance + var entity = undefined; + try { + // need to eval because this is a nested path - not simply a global on window + entity = eval(path) + } catch (e) { + throw( "The instance " + path + " does not exist in the current model" ); + + return; + } + + // Add common control buttons to list + for (var control in config.Common) { + if ($.inArray(control.toString(), showControls.Common) != -1) { + var add = true; + + // check show condition + if(config.Common[control].showCondition != undefined){ + var condition = this.replaceTokensWithPath(config.Common[control].showCondition, path); + add = eval(condition); + } + + if(add) { + ctrlButtons.push(config.Common[control]); + } + } + } + + if(entity!=null||entity!=undefined){ + if (entity.hasCapability(GEPPETTO.Resources.VISUAL_CAPABILITY)) { + // Add visual capability controls to list + for (var control in config.VisualCapability) { + if ($.inArray(control.toString(), showControls.VisualCapability) != -1) { + var add = true; + + // check show condition + if(config.VisualCapability[control].showCondition != undefined){ + var condition = this.replaceTokensWithPath(config.VisualCapability[control].showCondition, path); + add = eval(condition); + } + + if(add) { + ctrlButtons.push(config.VisualCapability[control]); + } + } + } + } + } + + var that = this; + + return ( +
                + {ctrlButtons.map(function (control, id) { + // grab attributes to init button attributes + var controlConfig = that.resolveCondition(control, path); + var idVal = path.replace(/\./g, '_').replace(/\[/g, '_').replace(/\]/g, '_') + "_" + controlConfig.id + "_buttonBar_btn"; + var tooltip = controlConfig.tooltip; + var classVal = "btn buttonBar-button fa " + controlConfig.icon; + var styleVal = {}; + + // define action function + var actionFn = function (param) { + // NOTE: there is a closure on 'control' so it's always the right one + var controlConfig = that.resolveCondition(control, path); + + // take out action string + var actionStr = that.getActionString(controlConfig, path); + + if (param != undefined) { + actionStr = actionStr.replace(/\$param\$/gi, param); + } + + // run action + if (actionStr != '' && actionStr != undefined) { + GEPPETTO.Console.executeCommand(actionStr); + that.refresh(); + } + + // if conditional, swap icon with the other condition outcome + if (control.hasOwnProperty('condition')) { + var otherConfig = that.resolveCondition(control, path); + var element = $('#' + idVal); + element.removeClass(); + element.addClass("btn buttonBar-button fa " + otherConfig.icon); + } + }; + + // if conditional, swap icon with the other condition outcome + if (control.hasOwnProperty('condition')) { + var otherConfig = that.resolveCondition(control, path); + var element = $('#' + idVal); + element.removeClass(); + element.addClass("btn buttonBar-button fa " + otherConfig.icon); + } + + // figure out if we need to include the color picker (hook it up in didMount) + if (controlConfig.id == "color") { + that.colorPickerBtnId = idVal; + that.colorPickerActionFn = actionFn; + // set style val to color tint icon + var colorVal = String(entity.getColor().replace(/0X/i, "#") + "0000").slice(0, 7); + styleVal = {color: colorVal.startsWith('#') ? colorVal : ('#' + colorVal) }; + classVal += " color-picker-button"; + } + + return ( + + + + ) + })} +
                + ) + } + }); + + return ButtonBarComponent; +}); diff --git a/src/main/webapp/js/components/interface/popup/Popup.css b/src/main/webapp/js/components/interface/popup/Popup.css new file mode 100644 index 000000000..faa8ffa8e --- /dev/null +++ b/src/main/webapp/js/components/interface/popup/Popup.css @@ -0,0 +1,141 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2011, 2013 OpenWorm. + * http://openworm.org + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the MIT License + * which accompanies this distribution, and is available at + * http://opensource.org/licenses/MIT + * + * Contributors: + * OpenWorm - http://openworm.org/people.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + *******************************************************************************/ +.popup { + font-size: 14px; + font-weight: 200; + color: white; + overflow: auto !important; + -webkit-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.popup-link{ + color: #fc6320; +} + +.popup-link:hover { + color: #fc401a; + text-decoration: none; +} +.popup a { + color: #fc6320; +} + +.popup p { + color: #fc6320; +} + +.popup a:hover { + color: #fc401a; + text-decoration: none; +} + +.popup-image { + margin: 0 auto; + text-align: center; + color: white; + clear: both; + max-width: 100%; +} + +.invert { + -webkit-filter: invert(22%); + filter: invert(22%); +} + +.popup-slick-image { + margin: 0 auto; + text-align: center; + color: white; +} + +.popup-icon { + color: #7F98FF; + margin-left: 5px; + margin-right: 5px; +} + +.popup-icon-link { + color: #9bafff; + font-size: 15px; + margin-right: 2px; + margin-left: 2px; +} + +.popup-icon-link:hover { + color: #8098f9; +} + +.popup-slick { + clear: both; + margin-top: 40px; +} + +.popup-title { + font-weight: 600; + color: #fc6320; + cursor: pointer; + clear: both; + float: left; + text-transform: capitalize; + margin-bottom: 5px; + margin-top: 5px; + margin-bottom: 5px; +} + +.popup-title:hover { + color: #fc401a; +} + +.popup-chevron { + margin-left: 10px; + margin-top: 7px; + float: right; + color: #8A7F7F; + cursor: pointer; +} + +.popup-html { + clear: both; +} + +.popup-text { + font-size: 14px; + font-weight: 200; + text-align: justify; + color: white; + clear: both; +} diff --git a/src/main/webapp/js/components/interface/popup/Popup.js b/src/main/webapp/js/components/interface/popup/Popup.js new file mode 100644 index 000000000..3e80e5ee2 --- /dev/null +++ b/src/main/webapp/js/components/interface/popup/Popup.js @@ -0,0 +1,396 @@ + +/** + * Popup Widget + * + * @module Widgets/Popup + * @author Jesus R. Martinez (jesus@metacell.us) + */ +define(function (require) { + + //var Widget = require('../Widget'); + var $ = require('jquery'); + var Type = require('../../../geppettoModel/model/Type'); + var React = require('react'); + var ReactDOM = require('react-dom'); + var ButtonBarComponent = require('./ButtonBarComponent'); + + var anchorme = require('anchorme'); + var slick = require('slick-carousel'); + + // var widgetUtility = require("../WidgetUtility"); + // widgetUtility.loadCss("geppetto/js/components/widgets/popup/Popup.css"); + // widgetUtility.loadCss("geppetto/js/components/widgets/popup/vendor/slick.css"); + // widgetUtility.loadCss("geppetto/js/components/widgets/popup/vendor/slick-theme.css"); + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/widgets/popup/Popup.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/widgets/popup/vendor/slick.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = "geppetto/js/components/widgets/popup/vendor/slick-theme.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + + /** + * Private function to hookup custom event handlers + * + * NOTE: declared here so that it's private. + */ + var hookupCustomHandlers = function (handlers, popupDOM, popup) { + for (var i = 0; i < handlers.length; i++) { + // if not hooked already, then go ahead and hook it + if (handlers[i].hooked === false) { + // set hooked to avoid double triggers + handlers[i].hooked = true; + + // Find and iterate element with an instancepath attribute + popupDOM.find("a[instancepath]").each(function () { + var fun = handlers[i].funct; + var ev = handlers[i].event; + var metaType = handlers[i].meta; + var path = $(this).attr("instancepath").replace(/\$/g, ""); + var node; + + try { + node = eval(path); + } catch (ex) { + // if instance path doesn't exist set path to undefined + node = undefined; + } + + // hookup IF domain type is undefined OR it's defined and it matches the node type + if (metaType === undefined || (metaType !== undefined && node !== undefined && node.getMetaType() === metaType)) { + // hookup custom handler + $(this).on(ev, function () { + // invoke custom handler with instancepath as arg + fun(node, path, popup); + + // stop default event handler of the anchor from doing anything + return false; + }); + } + }); + } + } + }; + + // return Widget.View.extend({ + + var popupComponent = React.createClass({ + + data: null, + buttonBarConfig : null, + buttonBarControls : null, + buttonBar : null, + + /** + * Initialize the popup widget + */ + // initialize: function (options) { + // Widget.View.prototype.initialize.call(this, options); + // this.render(); + // this.setSize(100, 300); + // this.customHandlers = []; + // //set class pop up + // var selector = $("#" + this.id); + // selector.addClass("popup"); + // }, + + shouldComponentUpdate() { + return false; + }, + + componentDidMount: function () { + // this.render(); + this.id = this.props.id; + this.customHandlers = []; + //set class pop up + $("#" + this.id).addClass("popup"); + this.setSize(100, 300); + }, + + /** + * Sets the message that is displayed inside the widget + * + * @command setMessage(msg) + * @param {String} msg - The message that is displayed inside the widget + */ + setMessage: function (msg) { + $("#" + this.id).html(msg); + GEPPETTO.Console.debugLog("Set new Message for " + this.id); + + if (this.customHandlers.length > 0) { + // msg has changed, set hooked attribute on handlers to false + for (var i = 0; i < this.customHandlers.length; i++) { + this.customHandlers[i].hooked = false; + } + + // trigger routine that hooks up handlers + hookupCustomHandlers(this.customHandlers, $("#" + this.id), this); + GEPPETTO.Console.debugLog("Hooked up custom handlers for " + this.id); + } + + return this; + }, + + /** + * Sets the message that is displayed inside the widget through an instance of type Text + * + * @command setText(textInstance) + * @param {Object} textInstance - An instance of type Text + */ + setText: function (textNode) { + return this.setMessage(this.getVariable(textNode).getInitialValues()[0].value.text); + }, + + /** + * Sets the message that is displayed inside the widget through an instance of type HTML + * + * @command setHTML(htmlInstance) + * @param {Object} htmlInstance - An instance of type HTML + */ + setHTML: function (htmlNode) { + if($.isArray(htmlNode)){ + var html = ""; + for(var i in htmlNode){ + var values = this.getVariable(htmlNode[i]).getInitialValues(); + html += values[0].value.html; + } + this.setMessage(html); + }else{ + this.setMessage(this.getVariable(htmlNode).getInitialValues()[0].value.html); + } + }, + + + /** + * Sets the message that is displayed inside the widget through an instance of any type. + * + * @command setData(anyInstance) + * @param {Object} anyInstance - An instance of any type + */ + + setData: function (anyInstance, filter) { + this.controller.addToHistory(anyInstance.getName(),"setData",[anyInstance, filter], this.getId()); + this.data = anyInstance; + + this.setMessage(this.getHTML(anyInstance, "", filter)); + var changeIcon=function(chevron){ + if (chevron.hasClass('fa-chevron-circle-down')) { + chevron.removeClass("fa-chevron-circle-down").addClass("fa-chevron-circle-up"); + } + else { + chevron.removeClass("fa-chevron-circle-up").addClass("fa-chevron-circle-down"); + } + }; + $("#" + this.getId() + ' .popup-title').click(function (e) { + changeIcon($($(e.target).attr("data-target") + "_chevron")); + }); + $("#" + this.getId() + " .popup-chevron").click(function (e) { + changeIcon($(e.target)); + }); + $("#" + this.getId() + " .slickdiv").slick(); + + if(this.buttonBarConfig!=null && this.buttonBarConfig!=undefined){ + this.renderButtonBar(); + } + + if(this.collapsed){ + this.$el.dialogExtend("collapse"); + } + return this; + }, + + /** + * + * @param anyInstance + * @returns {string} + */ + getHTML: function (anyInstance, id, filter) { + var anchorOptions = { + "attributes":{ + "target": "_blank", + "class" : "popup_link" + }, + "html":true, + ips:false, + emails:true, + urls:true, + TLDs:20, + truncate:0, + defaultProtocol:"http://" + }; + var type = anyInstance; + if(!(type instanceof Type)){ + type=anyInstance.getType(); + } + var html = ""; + + //let's check the filter + if(filter!=undefined && type.getMetaType() != GEPPETTO.Resources.COMPOSITE_TYPE_NODE){ + if($.inArray(type.getMetaType(), filter)==-1){ + //this type is not in the filter! + return html; + } + } + + if (type.getMetaType() == GEPPETTO.Resources.COMPOSITE_TYPE_NODE) { + for (var i = 0; i < type.getVariables().length; i++) { + var v = type.getVariables()[i]; + + if(filter!=undefined){ + if($.inArray(v.getType().getMetaType(), filter)==-1){ + //this type is not in the filter! + continue; + } + } + + var id = this.getId() + "_" + type.getId() + "_el_" + i; + if(filter==undefined){ + //Titles are only displayed if there's no filter..maybe random, make it a separate parameter + html += "" + } + html += this.getHTML(v, id, filter); + } + } + else if (type.getMetaType() == GEPPETTO.Resources.HTML_TYPE) { + var value = this.getVariable(anyInstance).getInitialValues()[0].value; + html += ""; + } + else if (type.getMetaType() == GEPPETTO.Resources.TEXT_TYPE) { + var value = this.getVariable(anyInstance).getInitialValues()[0].value; + html += ""; + } + else if (type.getMetaType() == GEPPETTO.Resources.IMAGE_TYPE) { + if(this.getVariable(anyInstance).getInitialValues()[0] != undefined) { + var value = this.getVariable(anyInstance).getInitialValues()[0].value; + if (value.eClass == GEPPETTO.Resources.ARRAY_VALUE) { + //if it's an array we use slick to create a carousel + var elements = ""; + for (var j = 0; j < value.elements.length; j++) { + var image = value.elements[j].initialValue; + elements += ""; + } + html += ""; + } + else if (value.eClass == GEPPETTO.Resources.IMAGE) { + //otherwise we just show an image + var image = value; + html += ""; + } + } + } + return html; + }, + + /** + * Returns the variable for a node or variable node + * + * @command getVariable(node) + * @param {Object} variable - A variable + */ + getVariable: function (node) { + if (node.getMetaType() == GEPPETTO.Resources.INSTANCE_NODE) { + return node.getVariable(); + } + else { + return node; + } + }, + + /** + * Sets a custom handler for a given event for nodes that point to nodes via instancePath attribute on HTML anchors. + * + * @command addCustomNodeHandler(funct, eventType) + * @param {function} funct - Handler function + * @param {String} eventType - event that triggers the custom handler + */ + addCustomNodeHandler: function (funct, eventType, metaType) { + this.customHandlers.push({funct: funct, event: eventType, meta: metaType, hooked: false}); + + // trigger routine that hooks up handlers + hookupCustomHandlers(this.customHandlers, $("#" + this.id), this); + return this; + }, + + renderButtonBar: function(){ + var that = this; + var buttonBarContainer = 'button-bar-container-' + this.id; + var barDiv = 'bar-div-'+this.id; + if(this.buttonBar != null){ + ReactDOM.unmountComponentAtNode(document.getElementById(barDiv)); + $("#"+buttonBarContainer).remove(); + } + + this.$el.parent().append("
                "); + + var instance = null; + var instancePath = ''; + + if(this.buttonBarConfig.filter!=null && this.buttonBarConfig.filter!=undefined){ + if(this.data!=null && this.data!=undefined){ + instance = this.buttonBarConfig.filter(this.data); + instancePath = instance.getPath(); + } + } + + this.buttonBar = ReactDOM.render( + React.createElement(ButtonBarComponent, {buttonBarConfig: this.buttonBarConfig, showControls:this.buttonBarControls, + instancePath : instancePath, instance : instance, geppetto: GEPPETTO, resize : function(){that.setSize(that.size.height,that.size.width);}}), + document.getElementById(barDiv) + ); + }, + + setButtonBarControls : function(controls){ + this.buttonBarControls = controls; + }, + + setButtonBarConfiguration : function(configuration){ + this.buttonBarConfig = configuration; + if(this.data!=null || this.data!=undefined){ + this.renderButtonBar(); + } + + // if the user clicks outside this popup hide color pickers if visible + var popupSelector = $("#" + this.id); + $(document).click(function (e) { + if (!popupSelector.is(e.target) // if the target of the click isn't the container... + && popupSelector.has(e.target).length === 0 // ... nor a descendant of the container + && !$(e.target).parents('.colorpicker').length > 0 // ... and it's not a child of a color picker + ) { + // hide color picker (it's place in the body so we can't restrict the search) if any + $('.colorpicker-visible').addClass('colorpicker-hidden').removeClass('colorpicker-visible'); + } + }); + }, + + destroy: function () { + var bar = document.getElementById('bar-div-'+this.id); + if(bar!=null || bar!=undefined){ + ReactDOM.unmountComponentAtNode(bar); + } + Widget.View.prototype.destroy.call(this); + }, + + render: function () { + return ( +
                +
                + ) + } + + + }); + + return popupComponent; +}); diff --git a/src/main/webapp/js/components/interface/popup/vendor/ajax-loader.gif b/src/main/webapp/js/components/interface/popup/vendor/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..e0e6e9760bc04861cc4771e327f22ed7962e0858 GIT binary patch literal 4178 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 literal 0 HcmV?d00001 diff --git a/src/main/webapp/js/components/interface/popup/vendor/bootstrap-colorpicker.min.css b/src/main/webapp/js/components/interface/popup/vendor/bootstrap-colorpicker.min.css new file mode 100644 index 000000000..14a9ee9a1 --- /dev/null +++ b/src/main/webapp/js/components/interface/popup/vendor/bootstrap-colorpicker.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Colorpicker + * http://mjolnic.github.io/bootstrap-colorpicker/ + * + * Originally written by (c) 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + */.colorpicker-saturation{float:left;width:100px;height:100px;cursor:crosshair;background-image:url("../img/bootstrap-colorpicker/saturation.png")}.colorpicker-saturation i{position:absolute;top:0;left:0;display:block;width:5px;height:5px;margin:-4px 0 0 -4px;border:1px solid #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-saturation i b{display:block;width:5px;height:5px;border:1px solid #fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-hue,.colorpicker-alpha{float:left;width:15px;height:100px;margin-bottom:4px;margin-left:4px;cursor:row-resize}.colorpicker-hue i,.colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:100%;height:1px;margin-top:-1px;background:#000;border-top:1px solid #fff}.colorpicker-hue{background-image:url("../img/bootstrap-colorpicker/hue.png")}.colorpicker-alpha{display:none;background-image:url("../img/bootstrap-colorpicker/alpha.png")}.colorpicker-saturation,.colorpicker-hue,.colorpicker-alpha{background-size:contain}.colorpicker{top:0;left:0;z-index:2500;min-width:130px;padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1}.colorpicker:before,.colorpicker:after{display:table;line-height:0;content:""}.colorpicker:after{clear:both}.colorpicker:before{position:absolute;top:-7px;left:6px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.colorpicker:after{position:absolute;top:-6px;left:7px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.colorpicker div{position:relative}.colorpicker.colorpicker-with-alpha{min-width:140px}.colorpicker.colorpicker-with-alpha .colorpicker-alpha{display:block}.colorpicker-color{height:10px;margin-top:5px;clear:both;background-image:url("../img/bootstrap-colorpicker/alpha.png");background-position:0 100%}.colorpicker-color div{height:10px}.colorpicker-selectors{display:none;height:10px;margin-top:5px;clear:both}.colorpicker-selectors i{float:left;width:10px;height:10px;cursor:pointer}.colorpicker-selectors i+i{margin-left:3px}.colorpicker-element .input-group-addon i,.colorpicker-element .add-on i{display:inline-block;width:16px;height:16px;vertical-align:text-top;cursor:pointer}.colorpicker.colorpicker-inline{position:relative;z-index:auto;display:inline-block;float:none}.colorpicker.colorpicker-horizontal{width:110px;height:auto;min-width:110px}.colorpicker.colorpicker-horizontal .colorpicker-saturation{margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-color{width:100px}.colorpicker.colorpicker-horizontal .colorpicker-hue,.colorpicker.colorpicker-horizontal .colorpicker-alpha{float:left;width:100px;height:15px;margin-bottom:4px;margin-left:0;cursor:col-resize}.colorpicker.colorpicker-horizontal .colorpicker-hue i,.colorpicker.colorpicker-horizontal .colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:1px;height:15px;margin-top:0;background:#fff;border:0}.colorpicker.colorpicker-horizontal .colorpicker-hue{background-image:url("../img/bootstrap-colorpicker/hue-horizontal.png")}.colorpicker.colorpicker-horizontal .colorpicker-alpha{background-image:url("../img/bootstrap-colorpicker/alpha-horizontal.png")}.colorpicker.colorpicker-hidden{display:none}.colorpicker.colorpicker-visible{display:block}.colorpicker-inline.colorpicker-visible{display:inline-block}.colorpicker-right:before{right:6px;left:auto}.colorpicker-right:after{right:7px;left:auto} \ No newline at end of file diff --git a/src/main/webapp/js/components/interface/popup/vendor/bootstrap-colorpicker.min.js b/src/main/webapp/js/components/interface/popup/vendor/bootstrap-colorpicker.min.js new file mode 100644 index 000000000..05c0e0744 --- /dev/null +++ b/src/main/webapp/js/components/interface/popup/vendor/bootstrap-colorpicker.min.js @@ -0,0 +1 @@ +!function(a){"use strict";"object"==typeof exports?module.exports=a(window.jQuery):"function"==typeof define&&define.amd?define(["jquery"],a):window.jQuery&&!window.jQuery.fn.colorpicker&&a(window.jQuery)}(function(a){"use strict";var b=function(b,c){this.value={h:0,s:0,b:0,a:1},this.origFormat=null,c&&a.extend(this.colors,c),b&&(void 0!==b.toLowerCase?(b+="",this.setColor(b)):void 0!==b.h&&(this.value=b))};b.prototype={constructor:b,colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",transparent:"transparent"},_sanitizeNumber:function(a){return"number"==typeof a?a:isNaN(a)||null===a||""===a||void 0===a?1:void 0!==a.toLowerCase?parseFloat(a):1},isTransparent:function(a){return a?(a=a.toLowerCase().trim(),"transparent"===a||a.match(/#?00000000/)||a.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/)):!1},rgbaIsTransparent:function(a){return 0===a.r&&0===a.g&&0===a.b&&0===a.a},setColor:function(a){a=a.toLowerCase().trim(),a&&(this.isTransparent(a)?this.value={h:0,s:0,b:0,a:0}:this.value=this.stringToHSB(a)||{h:0,s:0,b:0,a:1})},stringToHSB:function(b){b=b.toLowerCase();var c;"undefined"!=typeof this.colors[b]&&(b=this.colors[b],c="alias");var d=this,e=!1;return a.each(this.stringParsers,function(a,f){var g=f.re.exec(b),h=g&&f.parse.apply(d,[g]),i=c||f.format||"rgba";return h?(e=i.match(/hsla?/)?d.RGBtoHSB.apply(d,d.HSLtoRGB.apply(d,h)):d.RGBtoHSB.apply(d,h),d.origFormat=i,!1):!0}),e},setHue:function(a){this.value.h=1-a},setSaturation:function(a){this.value.s=a},setBrightness:function(a){this.value.b=1-a},setAlpha:function(a){this.value.a=parseInt(100*(1-a),10)/100},toRGB:function(a,b,c,d){a||(a=this.value.h,b=this.value.s,c=this.value.b),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-Math.abs(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],{r:Math.round(255*e),g:Math.round(255*f),b:Math.round(255*g),a:d||this.value.a}},toHex:function(a,b,c,d){var e=this.toRGB(a,b,c,d);return this.rgbaIsTransparent(e)?"transparent":"#"+(1<<24|parseInt(e.r)<<16|parseInt(e.g)<<8|parseInt(e.b)).toString(16).substr(1)},toHSL:function(a,b,c,d){a=a||this.value.h,b=b||this.value.s,c=c||this.value.b,d=d||this.value.a;var e=a,f=(2-b)*c,g=b*c;return g/=f>0&&1>=f?f:2-f,f/=2,g>1&&(g=1),{h:isNaN(e)?0:e,s:isNaN(g)?0:g,l:isNaN(f)?0:f,a:isNaN(d)?0:d}},toAlias:function(a,b,c,d){var e=this.toHex(a,b,c,d);for(var f in this.colors)if(this.colors[f]===e)return f;return!1},RGBtoHSB:function(a,b,c,d){a/=255,b/=255,c/=255;var e,f,g,h;return g=Math.max(a,b,c),h=g-Math.min(a,b,c),e=0===h?null:g===a?(b-c)/h:g===b?(c-a)/h+2:(a-b)/h+4,e=(e+360)%6*60/360,f=0===h?0:h/g,{h:this._sanitizeNumber(e),s:f,b:g,a:this._sanitizeNumber(d)}},HueToRGB:function(a,b,c){return 0>c?c+=1:c>1&&(c-=1),1>6*c?a+(b-a)*c*6:1>2*c?b:2>3*c?a+(b-a)*(2/3-c)*6:a},HSLtoRGB:function(a,b,c,d){0>b&&(b=0);var e;e=.5>=c?c*(1+b):c+b-c*b;var f=2*c-e,g=a+1/3,h=a,i=a-1/3,j=Math.round(255*this.HueToRGB(f,e,g)),k=Math.round(255*this.HueToRGB(f,e,h)),l=Math.round(255*this.HueToRGB(f,e,i));return[j,k,l,this._sanitizeNumber(d)]},toString:function(a){a=a||"rgba";var b=!1;switch(a){case"rgb":return b=this.toRGB(),this.rgbaIsTransparent(b)?"transparent":"rgb("+b.r+","+b.g+","+b.b+")";case"rgba":return b=this.toRGB(),"rgba("+b.r+","+b.g+","+b.b+","+b.a+")";case"hsl":return b=this.toHSL(),"hsl("+Math.round(360*b.h)+","+Math.round(100*b.s)+"%,"+Math.round(100*b.l)+"%)";case"hsla":return b=this.toHSL(),"hsla("+Math.round(360*b.h)+","+Math.round(100*b.s)+"%,"+Math.round(100*b.l)+"%,"+b.a+")";case"hex":return this.toHex();case"alias":return this.toAlias()||this.toHex();default:return b}},stringParsers:[{re:/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,format:"rgb",parse:function(a){return[a[1],a[2],a[3],1]}},{re:/rgb\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,format:"rgb",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],1]}},{re:/rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[a[1],a[2],a[3],a[4]]}},{re:/rgba\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],a[4]]}},{re:/hsl\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,format:"hsl",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/hsla\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"hsla",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,format:"hex",parse:function(a){return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16),1]}},{re:/#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,format:"hex",parse:function(a){return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16),1]}}],colorNameToHex:function(a){return"undefined"!=typeof this.colors[a.toLowerCase()]?this.colors[a.toLowerCase()]:!1}};var c={horizontal:!1,inline:!1,color:!1,format:!1,input:"input",container:!1,component:".add-on, .input-group-addon",sliders:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setHue"},alpha:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setAlpha"}},slidersHorz:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:100,maxTop:0,callLeft:"setHue",callTop:!1},alpha:{maxLeft:100,maxTop:0,callLeft:"setAlpha",callTop:!1}},template:'
                -
                -
                - -
                -
                -
                -
                -
                -
                -
                -
                -
                - - - -
                -
                +
                +
                +
                +
                +
                +
                +
                +
                +
                +
                +
                + + + +
                +
                +
                From d9e716b024b76dba89270f3ec8e3f3540cbbce21 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 13:39:26 +0100 Subject: [PATCH 140/339] call new widget factory --- src/main/webapp/js/pages/geppetto/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/pages/geppetto/main.js b/src/main/webapp/js/pages/geppetto/main.js index 3acdf3451..5230c3b0c 100644 --- a/src/main/webapp/js/pages/geppetto/main.js +++ b/src/main/webapp/js/pages/geppetto/main.js @@ -45,7 +45,7 @@ require('../../../style/less/main.less'); var GEPPETTO = require('geppetto'); require('../../components/ComponentFactory')(GEPPETTO); -require('../../components/WidgetFactory')(GEPPETTO); +require('../../components/NewWidgetFactory')(GEPPETTO); GEPPETTO.ComponentFactory.loadSpinner(); From 585f5c4a5c525e049fc585c0fc8aacac4dde4863 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 13:40:26 +0100 Subject: [PATCH 141/339] readd raw load of file for extracting comments --- src/main/webapp/js/components/WidgetController.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/WidgetController.js b/src/main/webapp/js/components/WidgetController.js index f99e8043f..2af2ed97c 100644 --- a/src/main/webapp/js/components/WidgetController.js +++ b/src/main/webapp/js/components/WidgetController.js @@ -104,8 +104,8 @@ class WidgetController { * @param {String} file */ getFileComments(file) { - //var fileContent = require("raw-loader!./" + GEPPETTO.ComponentFactory.components[this.componentID] + ".js"); - var fileContent = ""; + var fileContent = require("raw-loader!./" + GEPPETTO.ComponentFactory.components[this.componentID] + ".js"); + //var fileContent = ""; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var comments = []; From c5fc5111eeb7be1d156811b72a6547eb7f18e548 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 13:40:47 +0100 Subject: [PATCH 142/339] allows new and old add widget --- .../webapp/js/components/ComponentFactory.js | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index f1df72bc9..aa5010c08 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -71,16 +71,16 @@ define(function (require) { 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer', 'CAROUSEL': 'interface/carousel/Carousel', - 'CANVAS3D': 'interface/3dCanvas/Canvas', - 'PLOT': 'interface/plot/Plot', - 'POPUP': 'interface/popup/Popup' + 'CANVAS3D': 'interface/3dCanvas/Canvas' + // 'PLOT': 'interface/plot/Plot', + // 'POPUP': 'interface/popup/Popup' //'WIDGETCONTAINER': 'widgets/WidgetContainer' }, - componentsShortcut : { - "1": "POPUP" - }, + // componentsShortcut : { + // "1": "POPUP" + // }, loadSpinner:function(){ //We require this synchronously to properly show spinner when loading projects @@ -98,27 +98,35 @@ define(function (require) { }, addWidget: function(componentID, properties, callback){ - if (properties === undefined){ - properties = {}; - } - if (componentID in this.componentsShortcut){ - componentID = this.componentsShortcut[componentID]; - } - - var that=this; - require(["./" + GEPPETTO.ComponentFactory.components[componentID]], function(loadedModule){ - - var widgetController = GEPPETTO.WidgetFactory.getController(componentID); - if (!("id" in properties)){ - properties["id"] = widgetController.getAvailableWidgetId(); + + if (componentID in this.components){ + if (properties === undefined){ + properties = {}; } + // if (componentID in this.componentsShortcut){ + // componentID = this.componentsShortcut[componentID]; + // } - var component = React.createFactory(addWidget(loadedModule))(properties); - var renderedComponent = window[properties.id] = that.renderComponent(component, document.getElementById('widgetContainer'), callback); - - widgetController.registerWidget(renderedComponent) - return renderedComponent; - }); + var that=this; + require(["./" + GEPPETTO.ComponentFactory.components[componentID]], function(loadedModule){ + + var widgetController = GEPPETTO.NewWidgetFactory.getController(componentID); + if (!("id" in properties)){ + properties["id"] = widgetController.getAvailableWidgetId(); + } + + var component = React.createFactory(addWidget(loadedModule))(properties); + var renderedComponent = window[properties.id] = that.renderComponent(component, document.getElementById('widgetContainer'), callback); + + widgetController.registerWidget(renderedComponent) + return renderedComponent; + }); + } + else{ + var newWidget = GEPPETTO.WidgetFactory.addWidget(componentID); + return newWidget; + } + }, renderComponent: function(component, container, callback){ From b428c136d2785e7c245d382c3f5d0726e4271f43 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 14:06:40 +0100 Subject: [PATCH 143/339] fix for default camera controls --- .../style/less/components/controls/camera-controls.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/webapp/style/less/components/controls/camera-controls.less b/src/main/webapp/style/less/components/controls/camera-controls.less index fa606aefc..113baf46e 100644 --- a/src/main/webapp/style/less/components/controls/camera-controls.less +++ b/src/main/webapp/style/less/components/controls/camera-controls.less @@ -5,6 +5,10 @@ -o-transform: rotate(@deg); } +#camera-controls { + margin-top: 50px; +} + .position-toolbar { position: relative; From 6face567974f0e5cdf7fe7d8784c2de483272f01 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 2 May 2017 14:31:14 +0100 Subject: [PATCH 144/339] More work extracting the 3D canvas. Legacy canvas removed. Still work in progress. --- .../webapp/js/communication/MessageHandler.js | 20 +- .../components/interface/3dCanvas/Canvas.js | 596 +---- .../3dCanvas/GEPPETTO.SceneController.js | 1406 ++-------- .../3dCanvas/GEPPETTO.SceneFactory.js | 678 ----- .../interface/3dCanvas/SceneFactory.js | 739 ------ .../interface/3dCanvas/ThreeDEngine.js | 2301 +++++++++++++++++ .../interface/3dCanvas/TrackballControls.js | 8 +- .../webapp/js/geppettoModel/ModelFactory.js | 16 +- .../geppettoProject/ExperimentsController.js | 3 + src/main/webapp/js/pages/geppetto/G.js | 398 +-- .../js/pages/geppetto/GEPPETTO.Events.js | 4 +- .../webapp/js/pages/geppetto/GEPPETTO.Init.js | 311 +-- .../webapp/js/pages/geppetto/GEPPETTO.Main.js | 46 +- src/main/webapp/js/pages/geppetto/GEPPETTO.js | 391 +-- .../geppetto}/THREEx.KeyboardState.js | 0 .../webapp/js/pages/geppetto/geppetto.ejs | 1 - 16 files changed, 2789 insertions(+), 4129 deletions(-) delete mode 100644 src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneFactory.js delete mode 100644 src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js create mode 100644 src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js rename src/main/webapp/js/{components/interface/3dCanvas => pages/geppetto}/THREEx.KeyboardState.js (100%) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 56b963074..d3682384e 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -315,18 +315,6 @@ define(function (require) { this.augmentInstancesArray(window.Instances); console.timeEnd(GEPPETTO.Resources.CREATING_INSTANCES); - console.time(GEPPETTO.Resources.CREATING_SCENE); - GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.CREATING_SCENE); - // build scene here from Geppetto model populating visual objects in the instance tree - // Updates the simulation controls visibility - var webGLStarted = GEPPETTO.init(GEPPETTO.FE.createContainer()); - - if (webGLStarted) { - // we call it only the first time - GEPPETTO.SceneController.animate(); - } - GEPPETTO.SceneController.buildScene(window.Instances, window.Model); - console.timeEnd(GEPPETTO.Resources.CREATING_SCENE); GEPPETTO.trigger(GEPPETTO.Events.Model_loaded); GEPPETTO.Console.log(GEPPETTO.Resources.MODEL_LOADED); @@ -377,8 +365,7 @@ define(function (require) { // STEP 2: add new instances for new variables if any var newInstances = GEPPETTO.ModelFactory.createInstancesFromDiffReport(diffReport); - // STEP: 3 update scene - GEPPETTO.SceneController.updateSceneWithNewInstances(newInstances); + // STEP: 4 update components GEPPETTO.trigger(GEPPETTO.Events.Instances_created, newInstances); @@ -426,10 +413,7 @@ define(function (require) { // STEP 2: add new instances for new types if any var newInstances = GEPPETTO.ModelFactory.createInstancesFromDiffReport(diffReport); - // STEP 3: update scene - GEPPETTO.SceneController.updateSceneWithNewInstances(newInstances); - - // STEP: 4 update components + // STEP: 3 update components GEPPETTO.trigger(GEPPETTO.Events.Instances_created, newInstances); console.timeEnd(GEPPETTO.Resources.IMPORT_TYPE_RESOLVED); diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 0457b413a..e376cfe30 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -7,506 +7,168 @@ define(function (require) { document.getElementsByTagName("head")[0].appendChild(link); var React = require('react'); - - var THREE = require('three'); var isWebglEnabled = require('detector-webgl'); - require('./TrackballControls'); - require('./OBJLoader'); - var THREEx = require('./THREEx.KeyboardState'); - THREE.ColladaLoader = require('imports?THREE=three!exports?THREE.ColladaLoader!../../../../node_modules\/three\/examples\/js\/loaders\/ColladaLoader'); - THREE.ConvolutionShader = require('imports?THREE=three!exports?THREE.ConvolutionShader!../../../../node_modules\/three\/examples\/js\/shaders\/ConvolutionShader'); - THREE.CopyShader = require('imports?THREE=three!exports?THREE.CopyShader!../../../../node_modules\/three\/examples\/js\/shaders\/CopyShader'); - THREE.FilmShader = require('imports?THREE=three!exports?THREE.FilmShader!../../../../node_modules\/three\/examples\/js\/shaders\/FilmShader'); - THREE.FocusShader = require('imports?THREE=three!exports?THREE.FocusShader!../../../../node_modules\/three\/examples\/js\/shaders\/FocusShader'); - THREE.EffectComposer = require('imports?THREE=three!exports?THREE.EffectComposer!../../../../node_modules\/three\/examples\/js\/postprocessing\/EffectComposer'); - THREE.MaskPass = require('imports?THREE=three!exports?THREE.MaskPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/MaskPass'); - THREE.RenderPass = require('imports?THREE=three!exports?THREE.RenderPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/RenderPass'); - THREE.BloomPass = require('imports?THREE=three!exports?THREE.BloomPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/BloomPass'); - THREE.ShaderPass = require('imports?THREE=three!exports?THREE.ShaderPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/ShaderPass'); - THREE.FilmPass = require('imports?THREE=three!exports?THREE.FilmPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/FilmPass'); - var SceneFactory = require('./SceneFactory'); + var ThreeDEngine = require('./ThreeDEngine'); var CameraControls = require('../cameraControls/CameraControls'); var canvasComponent = React.createClass({ - factory: null, - camera: null, + engine: null, container: null, - controls: null, - scene: null, - meshes: {}, - splitMeshes: {}, - connectionLines: {}, - renderer: null, - clock: new THREE.Clock(), - stats: null, - projector: null, - keyboard: new THREEx.KeyboardState(), - needsUpdate: false, - metadata: {}, - customUpdate: null, - mouseClickListener: null, - rotationMode: false, - mouse: { - x: 0, - y: 0 - }, - visualModelMap: null, - idCounter: 0, - sceneCenter: new THREE.Vector3(), - cameraPosition: new THREE.Vector3(), - canvasCreated: false, - listenersCreated: false, selected: [], - pickingEnabled: true, // flag to enable disable 3d picking backgroundColor: 0x101010, - setupScene: function () { - this.scene = new THREE.Scene(); - this.visualModelMap = {}; - this.meshes = {}; - this.splitMeshes = {}; - this.connectionLines = {}; - }, - - /** - * Sets up the camera that is used to view the objects in the 3D Scene. - * @command setupCamera() - */ - setupCamera: function () { - // Camera - var SCREEN_WIDTH = $(this.container).width(); - var SCREEN_HEIGHT = $(this.container).height(); - var VIEW_ANGLE = 60; - var ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT; - var NEAR = 10; - var FAR = 2000000; - this.camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR); - this.scene.add(this.camera); - this.camera.position.set(this.cameraPosition.x, this.cameraPosition.y, this.cameraPosition.z); - this.camera.up = new THREE.Vector3(0, 1, 0); - this.camera.direction = new THREE.Vector3(0, 0, 1); - this.camera.lookAt(this.sceneCenter); - }, - - /** - * Set up the WebGL Renderer - */ - setupRenderer: function () { - // Reuse a single WebGL renderer. - // NOTE: Recreating the renderer causes camera displacement on Chrome OSX. - if (!this.canvasCreated) { - this.renderer = new THREE.WebGLRenderer({ - antialias: true, - alpha: true - }); - - } - this.configureRenderer(); - this.canvasCreated = true; + display: function (instances) { + this.engine.buildScene(instances); + return this; }, - configureRenderer: function (shaders) { - - if (shaders == undefined) { - shaders = false; - } - - var color = new THREE.Color(this.backgroundColor); - //this.renderer.setClearColor(color, 1); - var width = $(this.container).width(); - var height = $(this.container).height(); - this.renderer.setPixelRatio(window.devicePixelRatio); - this.renderer.setSize(width, height); - this.renderer.autoClear = false; - this.container.appendChild(this.renderer.domElement); - - var renderModel = new THREE.RenderPass(this.scene, this.camera); - - this.composer = new THREE.EffectComposer(this.renderer); - - if (shaders) { - var effectBloom = new THREE.BloomPass(0.75); - var effectFilm = new THREE.FilmPass(0.5, 0.5, 1448, false); - var effectFocus = new THREE.ShaderPass(THREE.FocusShader); - - effectFocus.uniforms["screenWidth"].value = window.innerWidth; - effectFocus.uniforms["screenHeight"].value = window.innerHeight; - - effectFocus.renderToScreen = true; - - this.composer.addPass(renderModel); - this.composer.addPass(effectBloom); - this.composer.addPass(effectFilm); - this.composer.addPass(effectFocus); - } else { - //standard - var copyPass = new THREE.ShaderPass(THREE.CopyShader); - copyPass.renderToScreen = true; - this.composer.addPass(renderModel); - this.composer.addPass(copyPass); - } - + displayAllInstances: function () { + //TODO: Implement this which will add all models and start listening when new models are added or removed + //ON GEPPETTO.Events.Instances_created + //GEPPETTO.SceneController.updateSceneWithNewInstances(newInstances); + //ON GEPPETTO.Events.Instance_deleted + //GEPPETTO.SceneController.removeFromScene(instance); + return this; }, /** - * Light up the scene + * Selects an instance + * + * @param {String} instancePath - Path of instance to select + * @param {String} geometryIdentifier - Identifier of the geometry that was clicked */ - setupLights: function () { - // Lights - this.camera.add(new THREE.PointLight(0xffffff, 1.5)); - + selectInstance: function (instancePath, geometryIdentifier) { + this.engine.selectInstance(instancePath, geometryIdentifier); + return this; }, - /** - * Sets up the controls used by the camera to make it able to zoom and - * pan. - */ - setupControls: function () { - // Controls - this.controls = new THREE.TrackballControls(this.camera, this.renderer.domElement); - this.controls.noZoom = false; - this.controls.noPan = false; + assignRandomColor: function (instance) { + this.engine.assignRandomColor(instance); + return this; }, /** - * Set up the listeners use to detect mouse movement and windoe resizing + * Zoom to the passed instances + * @param instances */ - setupListeners: function () { - if (!this.listenersCreated) { - var that = this; - // when the mouse moves, call the given function - this.renderer.domElement.addEventListener('mousedown', function (event) { - if (event.button == 0) //only for left click - { - if (that.pickingEnabled) { - var intersects = that.getIntersectedObjects(); - - if (intersects.length > 0) { - var selected = ""; - var geometryIdentifier = ""; - - // sort intersects - var compare = function (a, b) { - if (a.distance < b.distance) - return -1; - if (a.distance > b.distance) - return 1; - return 0; - }; - - intersects.sort(compare); - - var selectedIntersect; - // Iterate and get the first visible item (they are now ordered by proximity) - for (var i = 0; i < intersects.length; i++) { - // figure out if the entity is visible - var instancePath = ""; - if (intersects[i].object.hasOwnProperty("instancePath")) { - instancePath = intersects[i].object.instancePath; - geometryIdentifier = intersects[i].object.geometryIdentifier; - } else { - //weak assumption: if the object doesn't have an instancePath its parent will - instancePath = intersects[i].object.parent.instancePath; - geometryIdentifier = intersects[i].object.parent.geometryIdentifier; - } - if (instancePath != null || undefined) { - var visible = eval(instancePath + '.visible'); - if (intersects.length == 1 || i == intersects.length) { - //if there's only one element intersected we select it regardless of its opacity - if (visible) { - selected = instancePath; - selectedIntersect = intersects[i]; - break; - } - } else { - //if there are more than one element intersected and opacity of the current one is less than 1 - //we skip it to realize a "pick through" - var opacity = this.meshes[instancePath].defaultOpacity; - if ((opacity == 1 && visible) || GEPPETTO.isKeyPressed("ctrl")) { - selected = instancePath; - selectedIntersect = intersects[i]; - break; - } else if (visible && opacity < 1 && opacity > 0) { - //if only transparent objects intersected select first or the next down if - //one is already selected in order to enable "burrow through" sample. - if (selected == "" && !eval(instancePath + '.selected')) { - selected = instancePath; - selectedIntersect = intersects[i]; - } else { - if (eval(instancePath + '.selected') && i != intersects.length - 1) { - selected = ""; - } - } - } - } - } - } - - - if (selected != "") { - if (that.meshes.hasOwnProperty(selected) || that.splitMeshes.hasOwnProperty(selected)) { - if (!GEPPETTO.isKeyPressed("shift")) { - GEPPETTO.G.unSelectAll(); - } - - var selectedIntersectCoordinates = [selectedIntersect.point.x, selectedIntersect.point.y, selectedIntersect.point.z] - if (geometryIdentifier == undefined) { - geometryIdentifier = ""; - } - GEPPETTO.Console.executeCommand(selected + '.select(' + false + ', ' + '"' + geometryIdentifier + '", [' + selectedIntersectCoordinates + '])'); - } - } - } else if (GEPPETTO.isKeyPressed("ctrl")) { - GEPPETTO.G.unSelectAll(); - } - } - } - }, false); - - $("#" + this.props.id).on("dialogresizestop resizeEnd", function (event, ui) { - var [width, height] = that.setContainerDimensions() - that.camera.aspect = width / height; - that.camera.updateProjectionMatrix(); - that.renderer.setSize(width, height); - that.composer.setSize(width, height); - }); - - this.renderer.domElement.addEventListener('mousemove', function (event) { - that.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - that.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - }, false); - - - this.listenersCreated = true; - } + zoomTo: function (instances) { + this.engine.zoomTo(instances); + return this; }, /** - * Set container dimensions depending on parent dialog + * Sets whether to use wireframe or not to visualize any instance. */ - setContainerDimensions: function(){ - var containerSelector = $(this.container); - var height=containerSelector.parent().height()-40 - var width=containerSelector.parent().width()-30 - containerSelector.height(height); - containerSelector.width(width); - return [width, height]; + setWireframe: function (wireframe) { + this.engine.setWireframe(wireframe); + return this; }, /** - * Reset camera for scene. + * Show an instance + * + * @param {String} + * instancePath - Instance path of the instance to make visible */ - resetCamera: function () { - this.controls.reset(); - - var aabbMin = null; - var aabbMax = null; - - this.scene.traverse(function (child) { - if (child.hasOwnProperty("geometry")) { - child.geometry.computeBoundingBox(); - - var bb = child.geometry.boundingBox; - bb.translate(child.localToWorld(new THREE.Vector3())); - - // If min and max vectors are null, first values become - // default min and max - if (aabbMin == null && aabbMax == null) { - aabbMin = bb.min; - aabbMax = bb.max; - } - - // Compare other meshes, particles BB's to find min and max - else { - aabbMin.x = Math.min(aabbMin.x, bb.min.x); - aabbMin.y = Math.min(aabbMin.y, bb.min.y); - aabbMin.z = Math.min(aabbMin.z, bb.min.z); - aabbMax.x = Math.max(aabbMax.x, bb.max.x); - aabbMax.y = Math.max(aabbMax.y, bb.max.y); - aabbMax.z = Math.max(aabbMax.z, bb.max.z); - } - } - }); - - if (aabbMin != null && aabbMax != null) { - // Compute world AABB center - this.sceneCenter.x = (aabbMax.x + aabbMin.x) * 0.5; - this.sceneCenter.y = (aabbMax.y + aabbMin.y) * 0.5; - this.sceneCenter.z = (aabbMax.z + aabbMin.z) * 0.5; - - this.updateCamera(aabbMax, aabbMin); - } + showInstance: function (instancePath) { + this.engine.showInstance(instancePath); + return this; }, /** - * Update camera with new position and place to lookat + * Hide an instance + * + * @param {String} + * instancePath - Path of the instance to hide */ - updateCamera: function (aabbMax, aabbMin) { - // Compute world AABB "radius" - var diag = new THREE.Vector3(); - diag = diag.subVectors(aabbMax, aabbMin); - var radius = diag.length() * 0.5; - - this.pointCameraTo(this.sceneCenter); - - // Compute offset needed to move the camera back that much needed to center AABB - var offset = radius / Math.sin(Math.PI / 180.0 * this.camera.fov * 0.5); - - var dir = this.camera.direction.clone(); - dir.multiplyScalar(offset); - - // Store camera position - this.camera.position.addVectors(dir, this.controls.target); - this.camera.updateProjectionMatrix(); - }, - - boundingBox: function (obj) { - if (obj instanceof THREE.Mesh) { - - var geometry = obj.geometry; - geometry.computeBoundingBox(); - return geometry.boundingBox; - - } - - if (obj instanceof THREE.Object3D) { - - var bb = new THREE.Box3(); - for (var i = 0; i < obj.children.length; i++) { - bb.union(this.boundingBox(obj.children[i])); - } - return bb; - } - }, - - shapeCenterOfGravity: function (obj) { - return this.boundingBox(obj).center(); - }, - - /** */ - pointCameraTo: function (node) { - // Refocus camera to the center of the new object - var COG; - if (node instanceof THREE.Vector3) { - COG = node; - } else { - COG = this.shapeCenterOfGravity(node); - } - var v = new THREE.Vector3(); - v.subVectors(COG, this.controls.target); - this.camera.position.addVectors( - this.camera.position, v); - - // retrieve camera orientation - - this.camera.lookAt(COG); - this.controls.target.set(COG.x, COG.y, COG.z); + hideInstance: function (instancePath) { + this.engine.hideInstance(instancePath); + return this; }, /** - * Status of scene, populated or not + * Change the color of a given instance * - * @returns {Boolean} True or false depending whether scene is populated - * or not + * @param {String} + * instancePath - Instance path of the instance to change color + * @param {String} + * color - The color to set */ - isScenePopulated: function () { - return !(_.isEmpty(this.visualModelMap)); + setColor: function (instancePath, color) { + this.engine.setColor(instancePath, color); + return this; }, /** - * Has canvas been created? + * Change the default opacity for a given instance * - * @returns {Boolean] True or false if canvas has been created or not - */ - isCanvasCreated: function () { - return this.canvasCreated; - }, - - /** - * Sets up the HUD display with the scene stat's fps. + * @param {String} + * instancePath - Instance path of the instance to set the opacity of + * @param {String} + * opacity - The value of the opacity between 0 and 1 */ - setupStats: function () { - // Stats - if ($("#stats").length == 0) { - if (VARS != null) { - this.stats = new Stats(); - this.stats.domElement.style.float = 'right'; - this.stats.domElement.style.position = 'absolute'; - this.stats.domElement.style.top = '60px'; - this.stats.domElement.style.right = '5px'; - this.stats.domElement.style.zIndex = 100; - $('#controls').append(this.stats.domElement); - } - } + setOpacity: function (instancePath, opacity) { + this.engine.setOpacity(instancePath, opacity); + return this; }, /** - * Displays HUD for FPS stats + * Set the threshold (number of 3D primitives on the scene) above which we switch the visualization to lines + * @param threshold */ - toggleStats: function (mode) { - if (mode) { - if ($("#stats").length == 0) { - this.setupStats(); - } else { - $("#stats").show(); - } - } else { - $("#stats").hide(); - } + setLinesThreshold: function (threshold) { + this.engine.setLinesThreshold(ithreshold); + return this; }, - /** - * Adds debug axis to the scene + * Change the type of geometry used to visualize a given instance + * + * @param {String} + * instance - The instance to change the geometry type for + * @param {String} + * type - The geometry type, see GEPPETTO.Resources.GeometryTypes + * @param {String} + * thickness - Optional: the thickness to be used if the geometry is "lines" */ - setupAxis: function () { - // To use enter the axis length - this.scene.add(new THREE.AxisHelper(200)); + setGeometryType: function (instance, type, thickness) { + this.engine.setGeometryType(instance, type, thickness); + return this; }, + /** - * Renders objects in the scene + * Activates a visual group */ - renderCanvas: function () { - this.renderer.clear(); - this.composer.render(0.01); + showVisualGroups: function (visualGroup, mode, instances) { + this.engine.showVisualGroups(visualGroup, mode, instances); + return this; }, /** - * Returns intersected objects from mouse click + * Light up the passed instance * - * @returns {Array} a list of objects intersected by the current mouse coordinates + * @param {Instance} + * instance - the instance to be lit + * @param {Instance} + * colorfn - a function to map the intensity to a color + * @param {Float} + * intensity - the lighting intensity from 0 (no illumination) to 1 (full illumination) */ - getIntersectedObjects: function () { - // create a Ray with origin at the mouse position and direction into th scene (camera direction) - var vector = new THREE.Vector3(this.mouse.x, this.mouse.y, 1); - vector.unproject(this.camera); - - var raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize()); - - var visibleChildren = []; - this.scene.traverse(function (child) { - if (child.visible && !(child.clickThrough == true)) { - if (child.geometry != null && child.geometry != undefined) { - child.geometry.computeBoundingBox(); - visibleChildren.push(child); - } - } - }); - - // returns an array containing all objects in the scene with which the ray intersects - return raycaster.intersectObjects(visibleChildren); + lightUpEntity: function (instance, colorfn, intensity) { + this.engine.lightUpEntity(instance, colorfn, intensity); + return this; }, - /** * @param x * @param y */ incrementCameraPan: function (x, y) { - this.controls.incrementPanEnd(x, y); + this.engine.incrementCameraPan(x, y); + return this; }, /** @@ -515,14 +177,16 @@ define(function (require) { * @param z */ incrementCameraRotate: function (x, y, z) { - this.controls.incrementRotationEnd(x, y, z); + this.engine.incrementCameraRotate(x, y, z); + return this; }, /** * @param z */ incrementCameraZoom: function (z) { - this.controls.incrementZoomEnd(z); + this.engine.incrementCameraZoom(z); + return this; }, /** @@ -531,7 +195,8 @@ define(function (require) { * @param z */ setCameraPosition: function (x, y, z) { - this.controls.setPosition(x, y, z); + this.engine.setCameraPosition(x, y, z); + return this; }, /** @@ -541,27 +206,38 @@ define(function (require) { * @param radius */ setCameraRotation: function (rx, ry, rz, radius) { - this.controls.setRotation(rx, ry, rz, radius); + this.engine.setCameraRotation(rx, ry, rz, radius); + return this; }, + /** + * Rotate the camera around the selection + * + */ + autoRotate: function () { + this.engine.autoRotate(); + return this; + }, - animate: function () { - this.debugUpdate = this.needsUpdate; - // so that we log only the cycles when we are updating the scene - - this.controls.update(); - - this.isAnimated = true; - requestAnimationFrame(this.animate); - this.renderCanvas(); + /** + * Reset camera + */ + resetCamera: function () { + this.engine.resetCamera(); + return this; + }, - if (this.stats) { - this.stats.update(); - } - if (this.debugUpdate) { - GEPPETTO.log(GEPPETTO.Resources.UPDATE_FRAME_END); - } + /** + * Set container dimensions depending on parent dialog + */ + setContainerDimensions: function () { + var containerSelector = $(this.container); + var height = containerSelector.parent().height() - 40 + var width = containerSelector.parent().width() - 30 + containerSelector.height(height); + containerSelector.width(width); + return [width, height]; }, @@ -573,23 +249,23 @@ define(function (require) { if (!isWebglEnabled) { Detector.addGetWebGLMessage(); } else { - this.factory = new SceneFactory(this); this.container = $("#" + this.props.id + "_component").get(0); + this.engine = new ThreeDEngine(this.container, this.props.id); + GEPPETTO.SceneController.add3DCanvas(this); this.setContainerDimensions(); - this.setupScene(); - this.setupCamera(); - this.setupRenderer(); - this.setupLights(); - this.setupControls(); - this.setupListeners(); - this.animate(); + var that = this; + $("#" + this.props.id).on("dialogresizestop resizeEnd", function (event, ui) { + var [width, height] = that.setContainerDimensions() + that.engine.setSize(width, height); + }); + } }, render: function () { return (
                - +
                ) } diff --git a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js index e584c809f..2a7195d45 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js @@ -1,1275 +1,233 @@ /** - * Controls events coming to the 3D side of things; selecting, showing, hiding, ghosting, lighting of entities. Command to populate and update scene are also here, and then dispatch to factory. + * Global controller for all 3d canvas components. + * Methods called in this controller will apply to all the 3D canvas components. * - * @author Jesus R. Martinez (jesus@metacell.us) + * + * @author Matteo Cantarelli */ define(function (require) { return function (GEPPETTO) { - var THREE = require('three'); - require('../../../common/GEPPETTO.Resources')(GEPPETTO); GEPPETTO.SceneController = - { - linesThreshold: 2000, - aboveLinesThreshold: false, - wireframe: false, - isAnimated: false, - - /** - * Populate the scene with given instances - * - * @param instances - - * skeleton with instances and visual entities - */ - buildScene: function (instances) { - GEPPETTO.SceneController.traverseInstances(instances); - GEPPETTO.getVARS().scene.updateMatrixWorld(true); - }, - + { + linesThreshold: 2000, + aboveLinesThreshold: false, + canvasComponents: [], + wireframe: false, - /** - * Add new visual instances to the scene - * @param instances - */ - updateSceneWithNewInstances: function (instances) { - var updateCamera=false; - if(Object.keys(GEPPETTO.getVARS().meshes).length === 0){ - updateCamera=true; - } - for (var g = 0; g < instances.length; g++) { - // add instance to scene - GEPPETTO.SceneController.checkVisualInstance(instances[g]); - } - if(updateCamera){ - G.resetCamera(); - } - }, - /** - * Traverse the instances building a visual object when needed - * - * @param instances - - * skeleton with instances and visual entities - */ - traverseInstances: function (instances, lines, thickness) { - for (var j = 0; j < instances.length; j++) { - GEPPETTO.SceneController.checkVisualInstance(instances[j], lines, thickness); - } - }, + add3DCanvas: function (canvasComponent) { + this.canvasComponents.push(canvasComponent); + }, - /** - * Check if we need to create a visual object for a given instance and keeps iterating - * - * @param instances - - * skeleton with instances and visual entities - */ - checkVisualInstance: function (instance, lines, thickness) { - if (instance.hasCapability(GEPPETTO.Resources.VISUAL_CAPABILITY)) { - //since the visualcapability propagates up through the parents we can avoid visiting things that don't have it - if ((instance.getType().getMetaType() != GEPPETTO.Resources.ARRAY_TYPE_NODE) && instance.getVisualType()) { - GEPPETTO.SceneFactory.buildVisualInstance(instance, lines, thickness); - } - // this block keeps traversing the instances - if (instance.getMetaType() == GEPPETTO.Resources.INSTANCE_NODE) { - GEPPETTO.SceneController.traverseInstances(instance.getChildren(), lines, thickness); - } else if (instance.getMetaType() == GEPPETTO.Resources.ARRAY_INSTANCE_NODE) { - GEPPETTO.SceneController.traverseInstances(instance, lines, thickness); + apply: function (args) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i][args[0]].apply(this, arguments); } - } - }, - - - /** - * Applies visual transformation to given aspect. - */ - applyVisualTransformation: function (visualAspect, transformation) { - // NOTE: visualAspect currently disregarded as the - // transformation applies to the entire scene - GEPPETTO.getVARS().renderer.setCurrentMatrix(transformation); - }, - - /** - * Light up the entity - * - * @param {Instance} - * instance - the instance to be lit - * @param {Float} - * intensity - the lighting intensity from 0 (no illumination) to 1 (full illumination) - */ - lightUpEntity: function (instance, colorfn, intensity) { - var threeObject; - if (instance in GEPPETTO.getVARS().meshes) { - threeObject = GEPPETTO.getVARS().meshes[instance]; - } - else { - threeObject = GEPPETTO.getVARS().splitMeshes[instance]; - } - - var [r,g,b] = colorfn(intensity); - threeObject.material.color.setRGB(r,g,b); - }, + }, - /** - * Set ghost effect, bridge between other classes and ghost effect call - * - * @param {boolean} - * apply - Turn on or off the ghost effect - */ - setGhostEffect: function (apply) { - GEPPETTO.SceneController.ghostEffect(GEPPETTO.getVARS().meshes, apply); - GEPPETTO.SceneController.ghostEffect(GEPPETTO.getVARS().splitMeshes, apply); - }, - - /** - * Apply ghost effect to all meshes that are not selected - * - * @param {Array} - * meshes - Array of meshes to apply ghost effect to . - * @param {boolean} - * apply - Ghost effect on or off - */ - ghostEffect: function (meshes, apply) { - for (var v in meshes) { - var mesh = meshes[v]; - if (mesh != null && mesh.visible) { - if (apply && (!mesh.ghosted) && (!mesh.selected)) { - if (mesh instanceof THREE.Object3D) { - mesh.ghosted = true; - mesh.traverse(function (object) { - if (object.hasOwnProperty("material")) { - if (object.visible) { - object.ghosted = true; - object.material.transparent = true; - object.material.opacity = GEPPETTO.Resources.OPACITY.GHOST; - } - } - }); - } else { - mesh.ghosted = true; - mesh.material.transparent = true; - mesh.material.opacity = GEPPETTO.Resources.OPACITY.GHOST; - } - } else if ((!apply) && (mesh.ghosted)) { - if (mesh instanceof THREE.Object3D) { - mesh.ghosted = false; - mesh.traverse(function (object) { - if (object.hasOwnProperty("material")) { - if (object.visible) { - object.ghosted = false; - object.material.opacity = object.material.defaultOpacity; - if (object.material.opacity == 1) { - object.material.transparent = false; - } - } - } - }); - } else { - mesh.ghosted = false; - mesh.material.opacity = mesh.material.defaultOpacity; - if (mesh.material.opacity == 1) { - mesh.material.transparent = false; - } - } + isVisible: function (variables) { + var visible = false; + for (var i = 0; i < variables.length; i++) { + if (variables[i].isVisible()) { + visible = true; + break; } } + return visible; + }, - } - }, - - select: function (instances) { - for (var i = 0; i < instances.length; i++) { - instances[i].select(); - } - - }, - - deselect: function (instances) { - for (var i = 0; i < instances.length; i++) { - instances[i].deselect(); - } - }, - - /** - * Selects an aspect given the path of it. Color changes to yellow, and opacity become 100%. - * - * @param {String} - * instancePath - Path of aspect of mesh to select - */ - selectInstance: function (instancePath, geometryIdentifier) { - if(geometryIdentifier!=undefined && geometryIdentifier!=""){ - instancePath = instancePath + "." + geometryIdentifier; - } - var meshes = this.getRealMeshesForInstancePath(instancePath); - if (meshes.length > 0) { - for (var meshesIndex in meshes) { - var mesh = meshes[meshesIndex]; - - if (!mesh.visible) { - GEPPETTO.SceneController.merge(instancePath,true); - } - if (mesh.selected == false) { - if (mesh instanceof THREE.Object3D) { - mesh.traverse(function (child) { - if (child.hasOwnProperty("material")) { - GEPPETTO.SceneController.setThreeColor(child.material.color, GEPPETTO.Resources.COLORS.SELECTED); - child.material.opacity = Math.max(0.5, child.material.defaultOpacity); - child.geometry.computeBoundingBox(); - GEPPETTO.getVARS().controls.target.copy(child.position); - GEPPETTO.getVARS().controls.target.add(child.geometry.boundingBox.getCenter()); - } - }); - } else { - GEPPETTO.SceneController.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.SELECTED); - mesh.material.opacity = Math.max(0.5, mesh.material.defaultOpacity); - mesh.geometry.computeBoundingBox(); - //let's set the center of rotation to the selected mesh - GEPPETTO.getVARS().controls.target.copy(mesh.position); - GEPPETTO.getVARS().controls.target.add(mesh.geometry.boundingBox.getCenter()); - } - mesh.selected = true; - mesh.ghosted = false; - - - GEPPETTO.getVARS().camera.updateProjectionMatrix(); - - } - if(GEPPETTO.isKeyPressed('z')){ - this.zoomTo([eval(instancePath)]); + isSelected: function (variables) { + var selected = false; + for (var i = 0; i < variables.length; i++) { + if (variables[i].hasOwnProperty('isSelected') && variables[i].isSelected()) { + selected = true; + break; } - } - - return true; - } - return false; - }, - - /** - * Get Meshes associated to an instance - * - * @param {String} - * instancePath - Path of the instance - */ - getRealMeshesForInstancePath: function (instancePath) { - var meshes = []; - if (instancePath in GEPPETTO.getVARS().splitMeshes) { - for (var keySplitMeshes in GEPPETTO.getVARS().splitMeshes) { - if (keySplitMeshes.startsWith(instancePath)) { - meshes.push(GEPPETTO.getVARS().splitMeshes[keySplitMeshes]); - } + return selected; + }, + select: function (instances) { + for (var i = 0; i < instances.length; i++) { + instances[i].select(); } - } - else { - if (instancePath in GEPPETTO.getVARS().meshes) { - meshes.push(GEPPETTO.getVARS().meshes[instancePath]); - } - } - return meshes; - }, - /** - * Deselect aspect, or mesh as far as tree js is concerned. - * - * @param {String} - * instancePath - Path of the mesh/aspect to select - */ - deselectInstance: function (instancePath) { - var meshes = this.getRealMeshesForInstancePath(instancePath); - if (meshes.length > 0) { - for (var meshesIndex in meshes) { - var mesh = meshes[meshesIndex]; - // match instancePath to mesh store in variables properties - if (!mesh.visible) { - GEPPETTO.SceneController.merge(instancePath,false); - } - // make sure that path was selected in the first place - if (mesh.selected == true) { - if (mesh instanceof THREE.Object3D) { - mesh.traverse(function (child) { - if (child.hasOwnProperty("material")) { - GEPPETTO.SceneController.setThreeColor(child.material.color, child.material.defaultColor); - child.material.opacity = child.material.defaultOpacity; - } - }); - mesh.selected = false; - } - } else { - GEPPETTO.SceneController.setThreeColor(mesh.material.color, mesh.material.defaultColor); - mesh.material.opacity = mesh.material.defaultOpacity; - mesh.selected = false; - } - } - return true; - } - return false; - }, + }, - /** - * - * @param instances - */ - show: function (instances) { - for (var i = 0; i < instances.length; i++) { - instances[i].show(); - } - }, - - /** - * - * @param instances - */ - hide: function (instances) { - for (var i = 0; i < instances.length; i++) { - instances[i].hide(); - } - }, - - /** - * Resets the scene controller - */ - reset: function () { - GEPPETTO.SceneController.complexity = 0; - GEPPETTO.SceneController.aboveLinesThreshold = false; - }, - - /** - * Sets whether to use wireframe for the materials of the meshes - */ - setWireframe: function (wireframe) { - GEPPETTO.SceneController.wireframe = wireframe; - GEPPETTO.getVARS().scene.traverse(function (child) { - if (child instanceof THREE.Mesh) { - if(!(child.material.nowireframe==true)){ - child.material.wireframe = GEPPETTO.SceneController.wireframe; - } - } - }); - }, - - /** - * Show aspect, make it visible. - * - * @param {String} - * instancePath - Instance path of aspect to make visible - */ - showInstance: function (instancePath) { - var meshes = this.getRealMeshesForInstancePath(instancePath); - if (meshes.length > 0) { - for (var i = 0; i < meshes.length; i++) { - var mesh = meshes[i]; - if (mesh) { - mesh.traverse(function (object) { - object.visible = true; - }); - } + deselect: function (instances) { + for (var i = 0; i < instances.length; i++) { + instances[i].deselect(); } - } - }, - - /** - * Hide instance - * - * @param {String} - * instancePath - Path of the aspect to make invisible - */ - hideInstance: function (instancePath) { - var meshes = this.getRealMeshesForInstancePath(instancePath); - for (var i=0;i 0) { - for (var i = 0; i < meshes.length; i++) { - var mesh = meshes[i]; - if (mesh) { - mesh.traverse(function (object) { - if (object.hasOwnProperty("material")) { - GEPPETTO.SceneController.setThreeColor(object.material.color, color); - object.material.defaultColor = color; - } - }); - } - } - } - }, - - /** - * Assign random color to instance if leaf - if not leaf assign random colr to all leaf children recursively - * @param instance - */ - assignRandomColor: function(instance){ - var getRandomColor = function() { - var letters = '0123456789ABCDEF'; - var color = '0x'; - for (var i = 0; i < 6; i++ ) { - color += letters[Math.floor(Math.random() * 16)]; - } - return color; - }; - - var meshes = this.getRealMeshesForInstancePath(instance.getInstancePath()); - if (meshes.length > 0) { - for (var i = 0; i < meshes.length; i++) { - var mesh = meshes[i]; - if (mesh) { - var randomColor=getRandomColor(); - mesh.traverse(function (object) { - if (object.hasOwnProperty("material")) { - GEPPETTO.SceneController.setThreeColor(object.material.color, randomColor); - object.material.defaultColor = randomColor; - } - }); - } - } - } + }, - - }, - - /** - * - * @param threeColor - * @param color - */ - setThreeColor: function (threeColor, color) { - if (color.indexOf && color.indexOf("rgb") == -1) { - threeColor.setHex(color); - - } else if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { - threeColor.r = color.r; - threeColor.g = color.g; - threeColor.b = color.b; - } else { - threeColor.set(color); - } - }, - - /** - * Change the default opacity for a given aspect. The opacity set with this command API will be persisted across different workflows, e.g. selection. - * - * @param {String} - * instancePath - Instance path of aspect to change opacity for - */ - setOpacity: function (instancePath, opacity) { - var mesh = GEPPETTO.getVARS().meshes[instancePath]; - if (mesh != undefined) { - mesh.defaultOpacity = opacity; - if (opacity == 1) { - mesh.traverse(function (object) { - if (object.hasOwnProperty("material")) { - object.material.transparent = false; - object.material.opacity = 1; - object.material.defaultOpacity = 1; - } - }); - } else { - mesh.traverse(function (object) { - if (object.hasOwnProperty("material")) { - object.material.transparent = true; - object.material.opacity = opacity; - object.material.defaultOpacity = opacity; - } - }); - } - - return true; - } - return false; - }, - - /** - * Set the threshold (number of 3D primitives on the scene) above which we switch the visualization to lines - * for teh CompositeVisualTypes - * @param threshold - */ - setLinesThreshold: function (threshold) { - GEPPETTO.SceneController.linesThreshold = threshold; - }, - - /** - * Change the type of geometry used to visualize the instance - * - * @param {String} - * instance - The instance to change the geometry type for - * @param {String} - * type - The geometry type, see GEPPETTO.Resources.GeometryTypes - * @param {String} - * thickness - Optional: the thickness to be used if the geometry is "lines" - */ - setGeometryType: function (instance, type, thickness) { - var lines = false; - if (type === GEPPETTO.Resources.GeometryTypes.LINES) { - lines = true; - } else if (type === GEPPETTO.Resources.GeometryTypes.TUBES) { - lines = false - } else if (type === GEPPETTO.Resources.GeometryTypes.CYLINDERS) { - lines = false - } else { - return false; - } - - this.traverseInstances([instance], lines, thickness); - - return true; - }, - - /** - * Set the type of geometry used to visualize all the instances in the scene - * @param type - The geometry type either "lines", "tubes" or "cylinders" - */ - setAllGeometriesType: function (type) { - var visualInstances = GEPPETTO.ModelFactory.getAllInstancesWithCapability(GEPPETTO.Resources.VISUAL_CAPABILITY, window.Instances); - for (var i = 0; i < visualInstances.length; i++) { - if (GEPPETTO.getVARS().meshes[visualInstances[i].getInstancePath()]) { - var visualType = visualInstances[i].getVisualType(); - if (visualType) { - if (visualType.getWrappedObj().eClass == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { - GEPPETTO.SceneController.setGeometryType(visualInstances[i], type); - } - } - } - } - }, - - - /** - * - * @param instance - */ - zoomToInstance: function (instance) { - GEPPETTO.getVARS().controls.reset(); - - var zoomParameters = {}; - var mesh = GEPPETTO.getVARS().meshes[instance.getInstancePath()]; - mesh.traverse(function (object) { - if (object.hasOwnProperty("geometry")) { - GEPPETTO.SceneController.addMeshToZoomParameters(object, zoomParameters); - } - }); - - GEPPETTO.SceneController.zoomToParameters(zoomParameters); - - }, - - /** - * - * @param zoomParameters - */ - zoomToParameters: function (zoomParameters) { - // Compute world AABB center - GEPPETTO.getVARS().sceneCenter.x = (zoomParameters.aabbMax.x + zoomParameters.aabbMin.x) * 0.5; - GEPPETTO.getVARS().sceneCenter.y = (zoomParameters.aabbMax.y + zoomParameters.aabbMin.y) * 0.5; - GEPPETTO.getVARS().sceneCenter.z = (zoomParameters.aabbMax.z + zoomParameters.aabbMin.z) * 0.5; - - GEPPETTO.updateCamera(zoomParameters.aabbMax, zoomParameters.aabbMin) - }, - - /** - * - * @param mesh - * @param zoomParameters - * @returns {*} - */ - addMeshToZoomParameters: function (mesh, zoomParameters) { - mesh.geometry.computeBoundingBox(); - aabbMin = mesh.geometry.boundingBox.min; - aabbMax = mesh.geometry.boundingBox.max; - - bb = mesh.geometry.boundingBox; - bb.translate(mesh.localToWorld(new THREE.Vector3())); - - // If min and max vectors are null, first values become default min and max - if (zoomParameters.aabbMin == undefined && zoomParameters.aabbMax == undefined) { - zoomParameters.aabbMin = bb.min; - zoomParameters.aabbMax = bb.max; - } else { - // Compare other meshes, particles BB's to find min and max - zoomParameters.aabbMin.x = Math.min(zoomParameters.aabbMin.x, bb.min.x); - zoomParameters.aabbMin.y = Math.min(zoomParameters.aabbMin.y, bb.min.y); - zoomParameters.aabbMin.z = Math.min(zoomParameters.aabbMin.z, bb.min.z); - zoomParameters.aabbMax.x = Math.max(zoomParameters.aabbMax.x, bb.max.x); - zoomParameters.aabbMax.y = Math.max(zoomParameters.aabbMax.y, bb.max.y); - zoomParameters.aabbMax.z = Math.max(zoomParameters.aabbMax.z, bb.max.z); - } - - return zoomParameters; - }, - - /** - * - * @param instances - */ - zoomTo: function (instances) { - GEPPETTO.getVARS().controls.reset(); - - GEPPETTO.SceneController.zoomToParameters(GEPPETTO.SceneController.zoomIterator(instances, {})); - }, - - /** - * - * @param instances - * @param zoomParameters - * @returns {*} - */ - zoomIterator: function (instances, zoomParameters) { - for (var i = 0; i < instances.length; i++) { - var instancePath = instances[i].getInstancePath(); - var mesh = GEPPETTO.getVARS().meshes[instancePath]; - if (mesh) { - mesh.traverse(function (object) { - if (object.hasOwnProperty("geometry")) { - GEPPETTO.SceneController.addMeshToZoomParameters(object, zoomParameters); - } - }); - } - else { - zoomParameters = GEPPETTO.SceneController.zoomIterator(instances[i].getChildren(), zoomParameters); - } - - } - return zoomParameters; - }, - - /** - * Change color for meshes that are connected to other meshes. Color depends on whether that instance is an output, input or both - * - * @param {Instance} - * instance - The instance for which we want to show the connections - * @param {String} - * type - Type of connection, input or output (See GEPPETTO.Resources.INPUT/OUTPUT) - */ - highlightConnectedInstances: function (instance, type) { - - var inputs = {}; - var outputs = {}; - - var connections = instance.getConnections(type); - - - for (var c = 0; c < connections.length; c++) { - var connection = connections[c]; - - var otherEndPath = connection.getA().getPath() == instance.getInstancePath() ? - connection.getB().getPath() : - connection.getA().getPath(); - - var connectionType = connection.getA().getPath() == instance.getInstancePath() ? - GEPPETTO.Resources.OUTPUT : - GEPPETTO.Resources.INPUT; - - - // determine whether connection is input or output - if (connectionType == GEPPETTO.Resources.INPUT) { - //I want to change the colour the instances that are an input to the instance passed as a parameter - var mesh = GEPPETTO.getVARS().meshes[connection.getA().getPath()]; //this is the instance input to the current one - if (outputs[otherEndPath]) { - GEPPETTO.SceneController.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.INPUT_AND_OUTPUT); - } - else { - GEPPETTO.SceneController.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.INPUT_TO_SELECTED); - } - inputs[otherEndPath] = connection.getInstancePath(); - } else if (connectionType == GEPPETTO.Resources.OUTPUT) { - //I want to change the colour the instances that are an output of the instance passed as a parameter - var mesh = GEPPETTO.getVARS().meshes[connection.getB().getPath()]; //this is the instance output of the current on - if (inputs[otherEndPath]) { - GEPPETTO.SceneController.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.INPUT_AND_OUTPUT); - } - else { - GEPPETTO.SceneController.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.OUTPUT_TO_SELECTED); - } - outputs[otherEndPath] = connection.getInstancePath(); - } - } - }, - - /** - * Restore the original colour of the connected instances - * - * @param {Instance} - * instance - A connected instance - */ - restoreConnectedInstancesColour: function (instance) { - - var connections = instance.getConnections(); - - for (var c = 0; c < connections.length; c++) { - var connection = connections[c]; - - var mesh = connection.getA().getPath() == instance.getInstancePath() ? - GEPPETTO.getVARS().meshes[connection.getB().getPath()] : - GEPPETTO.getVARS().meshes[connection.getA().getPath()]; - - // if mesh is not selected, give it ghost or default color and opacity - if (!mesh.selected) { - // if there are nodes still selected, give it a ghost effect. If not nodes are - // selected, give the meshes old default color - if (G.getSelectionOptions().unselected_transparent) { - mesh.material.transparent = true; - mesh.material.opacity = GEPPETTO.Resources.OPACITY.GHOST; - mesh.ghosted = true; - } - GEPPETTO.SceneController.setThreeColor(mesh.material.color, mesh.material.defaultColor); - - } - // if mesh is selected, make it look like so - else { - GEPPETTO.SceneController.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.SELECTED); - mesh.material.transparent = true; - mesh.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - } - } - }, - - /** - * - * @param instance - */ - showConnectionLines: function (instance) { - var connections = instance.getConnections(); - - var mesh = GEPPETTO.getVARS().meshes[instance.getInstancePath()]; - var inputs = {}; - var outputs = {}; - var defaultOrigin = mesh.position.clone(); - - for (var c = 0; c < connections.length; c++) { - - var connection = connections[c]; - var type = connection.getA().getPath() == instance.getInstancePath() ? - GEPPETTO.Resources.OUTPUT : - GEPPETTO.Resources.INPUT; - - var otherEndPath = connection.getA().getPath() == instance.getInstancePath() ? - connection.getB().getPath() : - connection.getA().getPath(); - - - var otherEndMesh = GEPPETTO.getVARS().meshes[otherEndPath]; - - var destination; - var origin; - - if(connection.getA().getPoint()==undefined){ - //same as before - origin=defaultOrigin; - } - else{ - //the specified coordinate - var p = connection.getA().getPoint(); - if (type == GEPPETTO.Resources.OUTPUT) { - origin= new THREE.Vector3(p.x+mesh.position.x,p.y+mesh.position.y,p.z+mesh.position.z); - } - else if(type == GEPPETTO.Resources.INPUT) { - origin= new THREE.Vector3(p.x+otherEndMesh.position.x,p.y+otherEndMesh.position.y,p.z+otherEndMesh.position.z); - } - } - - if(connection.getB().getPoint()==undefined){ - //same as before - destination=otherEndMesh.position.clone();; - } - else{ - //the specified coordinate - var p = connection.getB().getPoint(); - if (type == GEPPETTO.Resources.OUTPUT) { - destination= new THREE.Vector3(p.x+otherEndMesh.position.x,p.y+otherEndMesh.position.y,p.z+otherEndMesh.position.z); - } - else if(type == GEPPETTO.Resources.INPUT) { - destination= new THREE.Vector3(p.x+mesh.position.x,p.y+mesh.position.y,p.z+mesh.position.z); - } - } - - var geometry = new THREE.Geometry(); - - geometry.vertices.push(origin, destination); - geometry.verticesNeedUpdate = true; - geometry.dynamic = true; - - var colour = null; - - - if (type == GEPPETTO.Resources.INPUT) { - - colour = GEPPETTO.Resources.COLORS.INPUT_TO_SELECTED; - - // figure out if connection is both, input and output - if (outputs[otherEndPath]) { - colour = GEPPETTO.Resources.COLORS.INPUT_AND_OUTPUT; - } - - if (inputs[otherEndPath]) { - inputs[otherEndPath].push(connection.getInstancePath()); - } - else { - inputs[otherEndPath] = []; - inputs[otherEndPath].push(connection.getInstancePath()); - } - } - - else if (type == GEPPETTO.Resources.OUTPUT) { - - colour = GEPPETTO.Resources.COLORS.OUTPUT_TO_SELECTED; - // figure out if connection is both, input and output - if (inputs[otherEndPath]) { - colour = GEPPETTO.Resources.COLORS.INPUT_AND_OUTPUT; - } - - if (outputs[otherEndPath]) { - outputs[otherEndPath].push(connection.getInstancePath()); - } - else { - outputs[otherEndPath] = []; - outputs[otherEndPath].push(connection.getInstancePath()); - } - } - - var material = new THREE.LineDashedMaterial({dashSize: 3, gapSize: 1}); - material.color.setHex(colour); - - var line = new THREE.LineSegments(geometry, material); - line.updateMatrixWorld(true); - - - if (GEPPETTO.getVARS().connectionLines[connection.getInstancePath()]) { - GEPPETTO.getVARS().scene.remove(GEPPETTO.getVARS().connectionLines[connection.getInstancePath()]); + /** + * Selects an instance given its. + * + * @param {String} instancePath - Path of instance to select + * @param {String} geometryIdentifier - Identifier of the geometry that was clicked + */ + selectInstance: function (instancePath, geometryIdentifier) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].selectInstance(instancePath, geometryIdentifier); } + }, - GEPPETTO.getVARS().scene.add(line); - GEPPETTO.getVARS().connectionLines[connection.getInstancePath()] = line; - } - }, - - /** - * Removes connection lines, all if nothing is passed in or just the ones passed in. - * - * @param instance - optional, instance for which we want to remove the connections - */ - removeConnectionLines: function (instance) { - if (instance != undefined) { - var connections = instance.getConnections(); - // get connections for given instance and remove only those - var lines = GEPPETTO.getVARS().connectionLines; - for (var i = 0; i < connections.length; i++) { - if (lines.hasOwnProperty(connections[i].getInstancePath())) { - // remove the connection line from the scene - GEPPETTO.getVARS().scene.remove(lines[connections[i].getInstancePath()]); - // remove the conneciton line from the GEPPETTO list of connection lines - delete lines[connections[i].getInstancePath()]; - } - } - } else { - // remove all connection lines - var lines = GEPPETTO.getVARS().connectionLines; - for (var key in lines) { - if (lines.hasOwnProperty(key)) { - GEPPETTO.getVARS().scene.remove(lines[key]); - } + /** + * + * @param instances + */ + show: function (instances) { + for (var i = 0; i < instances.length; i++) { + instances[i].show(); } - GEPPETTO.getVARS().connectionLines = []; - } - }, - - splitHighlightedMesh: function (targetObjects, aspects) { - var groups = {}; - for (a in aspects) { - // create object to hold geometries used for merging objects - // in groups - var geometryGroups = {}; - - var mergedMesh = GEPPETTO.getVARS().meshes[a]; + }, - /* - * reset the aspect instance path group mesh, this is used to group /*visual objects that don't belong to any of the groups passed as parameter - */ - GEPPETTO.getVARS().splitMeshes[a] = null; - geometryGroups[a] = new THREE.Geometry(); - var highlightedMesh = a + ".highlighted"; - GEPPETTO.getVARS().splitMeshes[highlightedMesh] = null; - geometryGroups[highlightedMesh] = new THREE.Geometry(); - - // get map of all meshes that merged mesh was merging - var map = mergedMesh.mergedMeshesPaths; - - // loop through individual meshes, add them to group, set - // new material to them - for (v in map) { - var m = GEPPETTO.getVARS().visualModelMap[map[v]]; - if (m.instancePath in targetObjects) { - // merged mesh into corresponding geometry - var geometry = geometryGroups[highlightedMesh]; - geometry.merge(m.geometry, m.matrix); - } else { - // merged mesh into corresponding geometry - var geometry = geometryGroups[a]; - geometry.merge(m.geometry, m.matrix); - } + /** + * + * @param instances + */ + hide: function (instances) { + for (var i = 0; i < instances.length; i++) { + instances[i].hide(); } + }, - groups[a] = {}; - groups[a].color = mergedMesh.material.color; - groups[highlightedMesh] = {}; - var newGroups = {}; - newGroups[a] = {}; - newGroups[highlightedMesh] = {}; - GEPPETTO.SceneController.createGroupMeshes(a, geometryGroups, newGroups); - } - return groups; - }, - - /** - * Highlight part of a mesh - * - * @param {String} - * path - Path of mesh to highlight - * @param {boolean} - * mode - Highlight or unhighlight - */ - highlight: function (targetObjects, aspects, mode) { - var splitHighlightedGroups = GEPPETTO.SceneController.splitHighlightedMesh(targetObjects, aspects); - - for (groupName in splitHighlightedGroups) { - // get group mesh - var groupMesh = GEPPETTO.getVARS().splitMeshes[groupName]; - - if (!(groupName in aspects)) { - if (mode) { - GEPPETTO.SceneController.setThreeColor(groupMesh.material.color, GEPPETTO.Resources.COLORS.HIGHLIGHTED); - groupMesh.highlighted = true; - } else { - GEPPETTO.SceneController.setThreeColor(groupMesh.material.color, groupMesh.material.defaultColor); - groupMesh.highlighted = false; - } - } else { - GEPPETTO.SceneController.setThreeColor(groupMesh.material.color, splitHighlightedGroups[groupName].color.getHex()); + assignRandomColor: function (instance) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].assignRandomColor(instance); } - } - }, + }, - /** - * Split merged mesh into individual meshes - * - * @param {String} - * instancePath - Path of aspect, corresponds to original merged mesh - * @param {AspectSubTreeNode} - * visualizationTree - Aspect Visualization Tree with groups info for visual objects - * @param {object} - * groups - The groups that we need to split mesh into - */ - splitGroups: function (instance, groupElements) { - - var instancePath = instance.getInstancePath(); - - // retrieve the merged mesh - var mergedMesh = GEPPETTO.getVARS().meshes[instancePath]; - // create object to hold geometries used for merging objects in - // groups - var geometryGroups = {}; - - /* - * reset the aspect instance path group mesh, this is used to group visual objects that don't belong to any of the groups passed as parameter + /** + * Zoom to the passed instances in all the canvas + * @param instances */ - GEPPETTO.getVARS().splitMeshes[instancePath] = null; - geometryGroups[instancePath] = new THREE.Geometry(); - - // create map of geometry groups for groups - for (var groupElement in groupElements) { - var groupName = instancePath + "." + groupElement; - - var geometry = new THREE.Geometry(); - geometry.groupMerge = true; - - geometryGroups[groupName] = geometry; - } - - // get map of all meshes that merged mesh was merging - var map = mergedMesh.mergedMeshesPaths; - - // flag for keep track what visual objects were added to group - // meshes already - var added = false; - // loop through individual meshes, add them to group, set new - // material to them - - for (var v in map) { - if (v != undefined) { - var m = GEPPETTO.getVARS().visualModelMap[map[v]]; - - eval(map[v].substring(0, map[v].lastIndexOf("."))); - var object = instance.getVisualType()[map[v].replace(instancePath + ".", "")]; - - // If it is a segment compare to the id otherwise check in the visual groups - if (object.getId() in groupElements) { - // true means don't add to mesh with non-groups visual objects - added = GEPPETTO.SceneController.addMeshToGeometryGroup(instance, object.getId(), geometryGroups, m) - } else { - // get group elements list for object - var groupElementsReference = object.getInitialValue().value.groupElements; - for (var i = 0; i < groupElementsReference.length; i++) { - var objectGroup = GEPPETTO.ModelFactory.resolve(groupElementsReference[i].$ref).getId(); - if (objectGroup in groupElements) { - // true means don't add to mesh with non-groups visual objects - added = GEPPETTO.SceneController.addMeshToGeometryGroup(instance, objectGroup, geometryGroups, m) - } - } - } - - // if visual object didn't belong to group, add it to mesh - // with remainder of them - if (!added) { - var geometry = geometryGroups[instancePath]; - if (m instanceof THREE.Line) { - geometry.vertices.push(m.geometry.vertices[0]); - geometry.vertices.push(m.geometry.vertices[1]); - } else { - // merged mesh into corresponding geometry - geometry.merge(m.geometry, m.matrix); - } - } - // reset flag for next visual object - added = false; - } - } - - groupElements[instancePath] = {}; - groupElements[instancePath].color = GEPPETTO.Resources.COLORS.SPLIT; - GEPPETTO.SceneController.createGroupMeshes(instancePath, geometryGroups, groupElements); - }, - - /** - * Add mesh to geometry groups - * - * @param {String} - * instancePath - Path of aspect, corresponds to original merged mesh - * @param {String} - * id - local path to the group - * @param {object} - * groups - The groups that we need to split mesh into - * @param {object} - * m - current mesh - */ - addMeshToGeometryGroup: function (instance, id, geometryGroups, m) { - // name of group, mix of aspect path and group name - var groupName = instance.getInstancePath() + "." + id; - // retrieve corresponding geometry for this group - var geometry = geometryGroups[groupName]; - // only merge if flag is set to true - if (m instanceof THREE.Line) { - geometry.vertices.push(m.geometry.vertices[0]); - geometry.vertices.push(m.geometry.vertices[1]); - } else { - // merged mesh into corresponding geometry - geometry.merge(m.geometry, m.matrix); - } - return true; - }, - - /** - * Create group meshes for given groups, retrieves from map if already present - */ - createGroupMeshes: function (instancePath, geometryGroups, groups) { - var mergedMesh = GEPPETTO.getVARS().meshes[instancePath]; - // switch visible flag to false for merged mesh and remove from scene - mergedMesh.visible = false; - GEPPETTO.getVARS().scene.remove(mergedMesh); - - for (g in groups) { - var groupName = g; - if (groupName.indexOf(instancePath) <= -1) { - groupName = instancePath + "." + g; + zoomTo: function (instances) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].zoomTo(instances); } + }, - var groupMesh = GEPPETTO.getVARS().splitMeshes[groupName]; - var geometryGroup = geometryGroups[groupName]; - - if (mergedMesh instanceof THREE.Line) { - var material = GEPPETTO.SceneFactory.getLineMaterial(); - groupMesh = new THREE.LineSegments(geometryGroup, material); - } else { - var material = GEPPETTO.SceneFactory.getMeshPhongMaterial(); - groupMesh = new THREE.Mesh(geometryGroup, material); + /** + * Sets whether to use wireframe or not to visualize any instance. + * Applies to all existing canvas. + */ + setWireframe: function (wireframe) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setWireframe(wireframe); } - groupMesh.instancePath = instancePath; - groupMesh.geometryIdentifier = g; - groupMesh.geometry.dynamic = false; - groupMesh.position.copy(mergedMesh.position); - - - GEPPETTO.getVARS().splitMeshes[groupName] = groupMesh; + }, - // Update visualization feature for a mesh - if (mergedMesh.ghosted) { - GEPPETTO.SceneController.ghostEffect([groupMesh], true); - } - if (mergedMesh.selected) { - GEPPETTO.SceneController.selectInstance(groupName); + /** + * Show an instance in all the existing canvas + * + * @param {String} + * instancePath - Instance path of the instance to make visible + */ + showInstance: function (instancePath) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].showInstance(instancePath); } - groupMesh.selected = mergedMesh.selected; - - // add split mesh to scenne and set flag to visible - groupMesh.visible = true; - GEPPETTO.getVARS().scene.add(groupMesh); - } - }, - - /** - * Merge mesh that was split before - * - * @param {String} - * aspectPath - Path to aspect that points to mesh - */ - merge: function (aspectPath, visible) { - // get mesh from map - var mergedMesh = GEPPETTO.getVARS().meshes[aspectPath]; + }, - // if merged mesh is not visible, turn it on and turn split one - // off - if (!mergedMesh.visible) { - for (path in GEPPETTO.getVARS().splitMeshes) { - // retrieve split mesh that is on the scene - var splitMesh = GEPPETTO.getVARS().splitMeshes[path]; - if (splitMesh) { - if (aspectPath == splitMesh.instancePath) { - splitMesh.visible = false; - // remove split mesh from scene - GEPPETTO.getVARS().scene.remove(splitMesh); - } - } - } - if(visible){ - // add merged mesh to scene and set flag to true - mergedMesh.visible = true; - GEPPETTO.getVARS().scene.add(mergedMesh); + /** + * Hide an instance in all the existing canvas + * + * @param {String} + * instancePath - Path of the instance to hide + */ + hideInstance: function (instancePath) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].hideInstance(instancePath); + } + }, + + /** + * Change the color of a given instance in all the existing canvas + * + * @param {String} + * instancePath - Instance path of the instance to change color + * @param {String} + * color - The color to set + */ + setColor: function (instancePath, color) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setColor(instancePath, color); + } + }, + + /** + * Change the default opacity for a given instance in all existing canvas. + * + * @param {String} + * instancePath - Instance path of the instance to set the opacity of + * @param {String} + * opacity - The value of the opacity between 0 and 1 + */ + setOpacity: function (instancePath, opacity) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setOpacity(instancePath, opacity); } - } - }, + }, - showVisualGroupsRaw: function (visualGroups, instance, meshesContainer) { - var instancePath = instance.getInstancePath(); - for (g in visualGroups) { - // retrieve visual group object - var visualGroup = visualGroups[g]; - - // get full group name to access group mesh - var groupName = g; - if (groupName.indexOf(instancePath) <= -1) { - groupName = instancePath + "." + g; + /** + * Set the threshold (number of 3D primitives on the scene) above which we switch the visualization to lines + * for all the existing canvas + * @param threshold + */ + setLinesThreshold: function (threshold) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setLinesThreshold(ithreshold); + } + }, + + /** + * Change the type of geometry used to visualize a given instance in all the existing canvas + * + * @param {String} + * instance - The instance to change the geometry type for + * @param {String} + * type - The geometry type, see GEPPETTO.Resources.GeometryTypes + * @param {String} + * thickness - Optional: the thickness to be used if the geometry is "lines" + */ + setGeometryType: function (instance, type, thickness) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setGeometryType(instance, type, thickness); } - - // get group mesh - var groupMesh = meshesContainer[groupName]; - groupMesh.visible = true; - GEPPETTO.SceneController.setThreeColor(groupMesh.material.color, visualGroup.color); - } - }, - - /** - * Shows a visual group - */ - showVisualGroups: function (visualGroups, mode, instances) { - for (var i = 0; i < instances.length; i++) { - var instance = instances[i]; - var instancePath = instance.getInstancePath(); - GEPPETTO.SceneController.merge(instancePath,true); - if (mode) { - var mergedMesh = GEPPETTO.getVARS().meshes[instancePath]; - var map = mergedMesh.mergedMeshesPaths; - //no mergedMeshesPaths means object hasn't been merged, single object - if(map!=undefined||null){ - GEPPETTO.SceneController.splitGroups(instance, visualGroups); - GEPPETTO.SceneController.showVisualGroupsRaw(visualGroups, instance, GEPPETTO.getVARS().splitMeshes); - - }else{ - GEPPETTO.SceneController.showVisualGroupsRaw(visualGroups, instance, GEPPETTO.getVARS().meshes); - } - - } - } - }, + }, - isVisible: function (variables) { - var visible = false; - for (var i = 0; i < variables.length; i++) { - if (variables[i].isVisible()) { - visible = true; - break; - } - } - return visible; - }, - - isSelected: function (variables) { - var selected = false; - for (var i = 0; i < variables.length; i++) { - if (variables[i].hasOwnProperty('isSelected') && variables[i].isSelected()) { - selected = true; - break; + /** + * Activates a visual group + */ + showVisualGroups: function (visualGroup, mode, instances) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].showVisualGroups(visualGroup, mode, instances); + } + }, + + /** + * Light up the entity in all canvas + * + * @param {Instance} + * instance - the instance to be lit + * @param {Instance} + * colorfn - a function to map the intensity to a color + * @param {Float} + * intensity - the lighting intensity from 0 (no illumination) to 1 (full illumination) + */ + lightUpEntity: function (instance, colorfn, intensity) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].lightUpEntity(instance, colorfn, intensity); } } - return selected; - }, - - /** - * Animate simulation - */ - animate: function () { - GEPPETTO.getVARS().debugUpdate = GEPPETTO.getVARS().needsUpdate; - // so that we log only the cycles when we are updating the scene - - GEPPETTO.getVARS().controls.update(); - - this.isAnimated = true; - requestAnimationFrame(GEPPETTO.SceneController.animate); - GEPPETTO.render(); - - if (GEPPETTO.getVARS().stats) { - GEPPETTO.getVARS().stats.update(); - } - if (GEPPETTO.getVARS().debugUpdate) { - GEPPETTO.log(GEPPETTO.Resources.UPDATE_FRAME_END); - } - }, - /** - * Remove given entity from scene - * - * @param entity - */ - removeFromScene: function (entity) { - var path = entity.getPath(); - var mergedMesh = GEPPETTO.getVARS().meshes[path]; - if (mergedMesh) { - GEPPETTO.getVARS().scene.remove(mergedMesh); - delete GEPPETTO.getVARS().meshes[path]; - } - var splitMesh = GEPPETTO.getVARS().splitMeshes[path]; - if (splitMesh) { - if (path == splitMesh.instancePath) { - GEPPETTO.getVARS().scene.remove(splitMesh); - } - delete GEPPETTO.getVARS().splitMeshes[path]; - } - }, - } + } } }) -; + ; diff --git a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneFactory.js b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneFactory.js deleted file mode 100644 index 5ab7888e4..000000000 --- a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneFactory.js +++ /dev/null @@ -1,678 +0,0 @@ -/** - * GEPPETTO Visualisation engine built on top of ThreeJS. Displays a scene as defined on org.geppetto.core. Factory class for creating and updating ThreeJS objects - * - * @author matteo@openworm.org (Matteo Cantarelli) - * @author Jesus R. Martinez (jesus@metacell.us) - */ -define(function (require) { - return function (GEPPETTO) { - var $ = require('jquery'); - var THREE = require('three'); - require('../../../common/GEPPETTO.Resources')(GEPPETTO); - - GEPPETTO.SceneFactory = - { - - linesUserInput:false, - linesUserPreference:undefined, - - setLinesUserInput:function(askUser){ - GEPPETTO.SceneFactory.linesUserInput=askUser; - }, - - - buildVisualInstance: function (instance, lines, thickness) { - var meshes = GEPPETTO.SceneFactory.generate3DObjects(instance, lines, thickness); - GEPPETTO.SceneFactory.init3DObject(meshes, instance); - }, - - /** - * Initializes a group of meshes that were created and adds them to the 3D scene - * - * @param {Object} - * meshes - The meshes that need to be initialized - */ - init3DObject: function (meshes, instance) { - var instancePath = instance.getInstancePath(); - var position = instance.getPosition(); - for (var m in meshes) { - var mesh = meshes[m]; - - mesh.instancePath = instancePath; - // if the model file is specifying a position for the loaded meshes then we translate them here - if (position != null) { - var p = new THREE.Vector3(position.x, position.y, position.z); - mesh.position.set(p.x, p.y, p.z); - mesh.matrixAutoUpdate = false; - mesh.applyMatrix(new THREE.Matrix4().makeTranslation(p.x, p.y, p.z)); - mesh.geometry.verticesNeedUpdate = true; - mesh.updateMatrix(); - // mesh.geometry.translate(position.x, position.y,position.z); - } - GEPPETTO.getVARS().scene.add(mesh); - GEPPETTO.getVARS().meshes[instancePath] = mesh; - GEPPETTO.getVARS().meshes[instancePath].visible = true; - GEPPETTO.getVARS().meshes[instancePath].ghosted = false; - GEPPETTO.getVARS().meshes[instancePath].defaultOpacity = 1; - GEPPETTO.getVARS().meshes[instancePath].selected = false; - GEPPETTO.getVARS().meshes[instancePath].input = false; - GEPPETTO.getVARS().meshes[instancePath].output = false; - - //Split anything that was splitted before - if (instancePath in GEPPETTO.getVARS().splitMeshes) { - var splitMeshes = GEPPETTO.getVARS().splitMeshes; - var elements = {}; - for (var splitMesh in splitMeshes) { - if (splitMeshes[splitMesh].instancePath == instancePath && splitMesh != instancePath) { - visualObject = splitMesh.substring(instancePath.length + 1); - elements[visualObject] = ""; - } - } - if (Object.keys(elements).length > 0) { - GEPPETTO.SceneController.splitGroups(instance, elements); - } - } - } - }, - - /** - * - * @param instance - * @param lines - * @param thickness - * @returns {Array} - */ - generate3DObjects: function (instance, lines, thickness) { - var previous3DObject = GEPPETTO.getVARS().meshes[instance.getInstancePath()]; - var color = undefined; - if (previous3DObject) { - color=previous3DObject.material.defaultColor; - // if an object already exists for this aspect we remove it. This could happen in case we are changing how an aspect - // is visualized, e.g. lines over tubes representation - GEPPETTO.getVARS().scene.remove(previous3DObject); - var splitMeshes = GEPPETTO.getVARS().splitMeshes; - for (var m in splitMeshes) { - if (m.indexOf(instance.getInstancePath()) != -1) { - GEPPETTO.getVARS().scene.remove(splitMeshes[m]); - //splitMeshes[m] = null; - } - } - - } - //TODO This can be optimised, no need to create both - var materials = - { - "mesh": GEPPETTO.SceneFactory.getMeshPhongMaterial(color), - "line": GEPPETTO.SceneFactory.getLineMaterial(thickness,color) - }; - var instanceObjects = []; - var threeDeeObjList = GEPPETTO.SceneFactory.walkVisTreeGen3DObjs(instance, materials, lines); - - // only merge if there are more than one object - if (threeDeeObjList.length > 1) { - var mergedObjs = GEPPETTO.SceneFactory.merge3DObjects(threeDeeObjList, materials); - // investigate need to obj.dispose for obj in threeDeeObjList - if (mergedObjs != null) { - mergedObjs.instancePath = instance.getInstancePath(); - instanceObjects.push(mergedObjs); - } else { - for (var obj in threeDeeObjList) { - threeDeeObjList[obj].instancePath = instance.getInstancePath(); - instanceObjects.push(threeDeeObjList[obj]); - } - } - } - else if(threeDeeObjList.length==1) - { - // only one object in list, add it to local array and set - instanceObjects.push(threeDeeObjList[0]); - instanceObjects[0].instancePath = instance.getInstancePath(); - } - - return instanceObjects; - }, - - /** - * - * @param instance - * @param materials - * @param lines - * @returns {Array} - */ - walkVisTreeGen3DObjs: function (instance, materials, lines) { - var threeDeeObj = null; - var threeDeeObjList = []; - var visualType = instance.getVisualType(); - if (visualType == undefined) { - return threeDeeObjList; - } - else { - if ($.isArray(visualType)) { - //TODO if there is more than one visual type we need to display all of them - visualType = visualType[0]; - } - } - if (visualType.getMetaType() == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { - for (var v in visualType.getVariables()) { - var visualValue = visualType.getVariables()[v].getWrappedObj().initialValues[0].value; - threeDeeObj = GEPPETTO.SceneFactory.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getVariables()[v].getId(), materials, lines); - if (threeDeeObj) { - threeDeeObjList.push(threeDeeObj); - } - } - } else { - var visualValue = visualType.getWrappedObj().defaultValue; - threeDeeObj = GEPPETTO.SceneFactory.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getId(), materials, lines); - if (threeDeeObj) { - threeDeeObjList.push(threeDeeObj); - } - } - - return threeDeeObjList; - }, - - - /** - * - * @param objArray - * @param materials - * @returns {*} - */ - merge3DObjects: function (objArray, materials) { - var mergedMeshesPaths = []; - var ret = null; - var mergedLines; - var mergedMeshes; - objArray.forEach(function (obj) { - if (obj instanceof THREE.Line) { - if (mergedLines === undefined) { - mergedLines = new THREE.Geometry() - } - mergedLines.vertices.push(obj.geometry.vertices[0]); - mergedLines.vertices.push(obj.geometry.vertices[1]); - } - else if (obj.geometry.type == "Geometry") { - // This catches both Collada an OBJ - if (objArray.length > 1) { - throw Error("Merging of multiple OBJs or Colladas not supported"); - } - else { - ret = obj; - } - } - else { - if (mergedMeshes === undefined) { - mergedMeshes = new THREE.Geometry() - } - obj.geometry.dynamic = true; - obj.geometry.verticesNeedUpdate = true; - obj.updateMatrix(); - mergedMeshes.merge(obj.geometry, obj.matrix); - } - mergedMeshesPaths.push(obj.instancePath); - - }); - - if (mergedLines === undefined) { - // There are no line geometries, we just create a mesh for the merge of the solid geometries - // and apply the mesh material - ret = new THREE.Mesh(mergedMeshes, materials["mesh"]); - } else { - ret = new THREE.LineSegments(mergedLines, materials["line"]); - if (mergedMeshes != undefined) { - // we merge into a single mesh both types of geometries (from lines and 3D objects) - var tempmesh = new THREE.Mesh(mergedMeshes, materials["mesh"]); - ret.geometry.merge(tempmesh.geometry, tempmesh.matrix); - } - } - - if (ret != null && !Array.isArray(ret)) { - ret.mergedMeshesPaths = mergedMeshesPaths; - } - - return ret; - - }, - - - /** - * - * @param instance - * @param node - * @param id - * @param materials - * @param lines - * @returns {*} - */ - visualizationTreeNodeTo3DObj: function (instance, node, id, materials, lines) { - var threeObject = null; - - if (lines === undefined) { - // Unless it's being forced we use a threshold to decide whether to use lines or cylinders - if (!GEPPETTO.SceneController.aboveLinesThreshold) { - //Unless we are already above the threshold... - GEPPETTO.SceneController.aboveLinesThreshold = GEPPETTO.SceneController.complexity > GEPPETTO.SceneController.linesThreshold; - - if (GEPPETTO.SceneController.aboveLinesThreshold) { - - if(GEPPETTO.SceneFactory.linesUserInput && GEPPETTO.SceneFactory.linesUserPreference==undefined){ - - //we need to ask the user - GEPPETTO.SceneFactory.linesUserPreference = confirm("The model you are loading has a complex morphology, would you like to render it using lines instead of 3D shapes? Be careful, choosing to use 3D shapes might crash your browser!"); - - if (GEPPETTO.SceneFactory.linesUserPreference) { - GEPPETTO.SceneController.setAllGeometriesType(GEPPETTO.Resources.GeometryTypes.LINES); - } - else{ - } - } - else{ - GEPPETTO.SceneController.setAllGeometriesType(GEPPETTO.Resources.GeometryTypes.LINES); - } - } - } - - if(GEPPETTO.SceneController.aboveLinesThreshold && GEPPETTO.SceneFactory.linesUserInput){ - lines = GEPPETTO.SceneFactory.linesUserPreference; - } - else{ - lines = GEPPETTO.SceneController.aboveLinesThreshold; - } - } - - var material = lines ? materials["line"] : materials["mesh"]; - - switch (node.eClass) { - case GEPPETTO.Resources.PARTICLE: - threeObject = GEPPETTO.SceneFactory.createParticle(node); - break; - - case GEPPETTO.Resources.CYLINDER: - if (lines) { - threeObject = GEPPETTO.SceneFactory.create3DLineFromNode(node, material); - } else { - threeObject = GEPPETTO.SceneFactory.create3DCylinderFromNode(node, material); - } - break; - - case GEPPETTO.Resources.SPHERE: - if (lines) { - threeObject = GEPPETTO.SceneFactory.create3DLineFromNode(node, material); - } else { - threeObject = GEPPETTO.SceneFactory.create3DSphereFromNode(node, material); - } - break; - case GEPPETTO.Resources.COLLADA: - threeObject = GEPPETTO.SceneFactory.loadColladaModelFromNode(node); - break; - case GEPPETTO.Resources.OBJ: - threeObject = GEPPETTO.SceneFactory.loadThreeOBJModelFromNode(node); - break; - } - if (threeObject) { - threeObject.visible = true; - // TODO: this is empty for collada and obj nodes - var instancePath = instance.getInstancePath() + "." + id; - threeObject.instancePath = instancePath; - threeObject.highlighted = false; - - // TODO: shouldn't that be the vistree? why is it also done at the loadEntity level?? - GEPPETTO.getVARS().visualModelMap[instancePath] = threeObject; - } - return threeObject; - }, - - /** - * - * @param node - * @returns {*} - */ - loadColladaModelFromNode: function (node) { - var loader = new THREE.ColladaLoader(); - loader.options.convertUpAxis = true; - var scene = null; - loader.parse(node.collada, function (collada) { - scene = collada.scene; - scene.traverse(function (child) { - if (child instanceof THREE.Mesh) { - child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; - child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.material.wireframe = GEPPETTO.SceneController.wireframe; - child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.geometry.computeVertexNormals(); - } - if (child instanceof THREE.SkinnedMesh) { - child.material.skinning = true; - child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; - child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.material.wireframe = GEPPETTO.SceneController.wireframe; - child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.geometry.computeVertexNormals(); - } - }); - }); - return scene; - }, - - /** - * - * @param node - * @returns {*} - */ - loadThreeOBJModelFromNode: function (node) { - var manager = new THREE.LoadingManager(); - manager.onProgress = function (item, loaded, total) { - console.log(item, loaded, total); - }; - var loader = new THREE.OBJLoader(manager); - var scene = loader.parse(node.obj); - - scene.traverse(function (child) { - if (child instanceof THREE.Mesh) { - child.material.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); - child.material.wireframe = GEPPETTO.SceneController.wireframe; - child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; - child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.geometry.computeVertexNormals(); - } - }); - - return scene; - }, - - /** - * - * @param node - * @returns {THREE.Vector3|*} - */ - createParticle: function (node) { - threeObject = new THREE.Vector3(node.position.x, node.position.y, node.position.z); - threeObject.visible = true; - threeObject.instancePath = node.instancePath; - threeObject.highlighted = false; - // TODO: does that need to be done? - GEPPETTO.getVARS().visualModelMap[node.instancePath] = threeObject; - - return threeObject; - - }, - - /** - * - * @param node - * @param material - * @returns {THREE.Line} - */ - create3DLineFromNode: function (node, material) { - if (node.eClass == GEPPETTO.Resources.CYLINDER) { - var bottomBasePos = new THREE.Vector3(node.position.x, node.position.y, node.position.z); - var topBasePos = new THREE.Vector3(node.distal.x, node.distal.y, node.distal.z); - - var axis = new THREE.Vector3(); - axis.subVectors(topBasePos, bottomBasePos); - var midPoint = new THREE.Vector3(); - midPoint.addVectors(bottomBasePos, topBasePos).multiplyScalar(0.5); - - var geometry = new THREE.Geometry(); - geometry.vertices.push(bottomBasePos); - geometry.vertices.push(topBasePos); - var threeObject = new THREE.Line(geometry, material); - threeObject.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 2)); - threeObject.lookAt(axis); - threeObject.position.fromArray(midPoint.toArray()); - - threeObject.geometry.verticesNeedUpdate = true; - } else if (node.eClass == GEPPETTO.Resources.SPHERE) { - var sphere = new THREE.SphereGeometry(node.radius, 20, 20); - threeObject = new THREE.Mesh(sphere, material); - threeObject.position.set(node.position.x, node.position.y, node.position.z); - threeObject.geometry.verticesNeedUpdate = true; - } - return threeObject; - }, - - /** - * - * @param cylNode - * @param material - * @returns {THREE.Mesh} - */ - create3DCylinderFromNode: function (cylNode, material) { - - var bottomBasePos = new THREE.Vector3(cylNode.position.x, cylNode.position.y, cylNode.position.z); - var topBasePos = new THREE.Vector3(cylNode.distal.x, cylNode.distal.y, cylNode.distal.z); - - var axis = new THREE.Vector3(); - axis.subVectors(topBasePos, bottomBasePos); - var midPoint = new THREE.Vector3(); - midPoint.addVectors(bottomBasePos, topBasePos).multiplyScalar(0.5); - - var c = new THREE.CylinderGeometry(cylNode.topRadius, cylNode.bottomRadius, axis.length(), 20, 1, false); - c.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 2)); - var threeObject = new THREE.Mesh(c, material); - - threeObject.lookAt(axis); - threeObject.position.fromArray(midPoint.toArray()); - - threeObject.geometry.verticesNeedUpdate = true; - return threeObject; - }, - - /** - * Modify the origin and radius of a sphere - * @returns {THREE.Mesh} - */ - modify3DSphere:function(object,x,y,z,radius){ - // Impossible to change the radius of a Sphere. - // Removing old object and creating a new one - GEPPETTO.getVARS().scene.remove(object); - return this.add3DSphere(x,y,z,radius); - }, - - /** - * Add a 3D sphere to the scene at the given coordinates (4) points. - * It could be any geometry really. - * @returns {THREE.Mesh} - */ - add3DSphere: function (x,y,z,radius) { - if (GEPPETTO.SceneController.aboveLinesThreshold) { - radius = 1; - } - - var material= new THREE.MeshBasicMaterial({side:THREE.DoubleSide}); - material.nowireframe=true; - material.opacity= 0.6; - material.transparent=true; - material.color.setHex("0xff0000"); - - var sphereNode ={radius:radius,position:{x:x, y:y, z:z}} - var mesh = GEPPETTO.SceneFactory.create3DSphereFromNode(sphereNode, material) - mesh.renderOrder=1; - mesh.clickThrough=true; - GEPPETTO.getVARS().scene.add(mesh); - return mesh; - }, - - - /** - * Add a 3D plane to the scene at the given coordinates (4) points. - * It could be any geometry really. - * @returns {THREE.Mesh} - */ - add3DPlane: function (x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4, textureURL) { - - var geometry=new THREE.Geometry(); - geometry.vertices.push( - new THREE.Vector3(x1,y1,z1),//vertex0 - new THREE.Vector3(x2,y2,z2),//1 - new THREE.Vector3(x3,y3,z3),//2 - new THREE.Vector3(x4,y4,z4)//3 - ); - geometry.faces.push( - new THREE.Face3(2,1,0),//use vertices of rank 2,1,0 - new THREE.Face3(3,1,2)//vertices[3],1,2... - ); - geometry.computeBoundingBox(); - - var max = geometry.boundingBox.max, - min = geometry.boundingBox.min; - var offset = new THREE.Vector2(0 - min.x, 0 - min.y); - var range = new THREE.Vector2(max.x - min.x, max.y - min.y); - var faces = geometry.faces; - - geometry.faceVertexUvs[0] = []; - - for (var i = 0; i < faces.length ; i++) { - - var v1 = geometry.vertices[faces[i].a], - v2 = geometry.vertices[faces[i].b], - v3 = geometry.vertices[faces[i].c]; - - geometry.faceVertexUvs[0].push([ - new THREE.Vector2((v1.x + offset.x)/range.x ,(v1.y + offset.y)/range.y), - new THREE.Vector2((v2.x + offset.x)/range.x ,(v2.y + offset.y)/range.y), - new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y) - ]); - } - geometry.uvsNeedUpdate = true; - geometry.dynamic = true; - - var material= new THREE.MeshBasicMaterial({side:THREE.DoubleSide}); - material.nowireframe=true; - if(textureURL!=undefined){ - var loader = new THREE.TextureLoader(); - // load a resource - loader.load( - // resource URL - textureURL, - // Function when resource is loaded - function ( texture ) { - //texture.minFilter = THREE.LinearFilter; - material.map=texture; - texture.flipY = false; - material.opacity= 0.3; - material.transparent=true; - material.needsUpdate = true; - - }, - // Function called when download progresses - function ( xhr ) { - console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); - }, - // Function called when download errors - function ( xhr ) { - console.log( 'An error happened' ); - } - ); - - } - else{ - material.opacity= 0.3; - material.transparent=true; - material.color.setHex("0xb0b0b0"); - } - - var mesh = new THREE.Mesh(geometry, material); - mesh.renderOrder=1; - mesh.clickThrough=true; - GEPPETTO.getVARS().scene.add(mesh); - return mesh; - }, - - /** - * Modify the coordinates (4) points of an existing plane. - * @returns {THREE.Mesh} - */ - modify3DPlane:function(object,x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4){ - object.geometry.vertices[0].set(x1,y1,z1); - object.geometry.vertices[1].set(x2,y2,z2); - object.geometry.vertices[2].set(x3,y3,z3); - object.geometry.vertices[3].set(x4,y4,z4); - object.geometry.verticesNeedUpdate = true; - return object; - }, - - - /** - * - * @param sphereNode - * @param material - * @returns {THREE.Mesh|*} - */ - create3DSphereFromNode: function (sphereNode, material) { - - var sphere = new THREE.SphereGeometry(sphereNode.radius, 20, 20); - // sphere.applyMatrix(new THREE.Matrix4().makeScale(-1,1,1)); - var threeObject = new THREE.Mesh(sphere, material); - threeObject.position.set(sphereNode.position.x, sphereNode.position.y, sphereNode.position.z); - - threeObject.geometry.verticesNeedUpdate = true; - return threeObject; - }, - - /** - * - * @param thickness - * @returns {THREE.LineBasicMaterial} - */ - getLineMaterial: function (thickness, color) { - var options = {}; - if (thickness) { - options.linewidth = thickness; - } - if (color == undefined) { - color = GEPPETTO.Resources.COLORS.DEFAULT; - } - var material = new THREE.LineBasicMaterial(options); - material.color.setHex(color); - material.defaultColor = color; - material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - return material; - }, - - /** - * - * @param color - * @returns {THREE.MeshPhongMaterial} - */ - getMeshPhongMaterial: function (color) { - if (color == undefined) { - color = GEPPETTO.Resources.COLORS.DEFAULT; - } - var material = new THREE.MeshPhongMaterial( - { - opacity: 1, - shininess: 10, - shading: THREE.SmoothShading - }); - - material.color.setHex(color); - material.defaultColor = color; - material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - return material; - }, - - /** - * - * @returns {THREE.PointsMaterial} - */ - getParticleMaterial: function () { - var textureLoader = new THREE.TextureLoader(); - var pMaterial = new THREE.PointsMaterial( - { - size: 5, - map: textureLoader.load("geppetto/images/particle.png"), - blending: THREE.AdditiveBlending, - depthTest: false, - transparent: true - }); - pMaterial.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); - pMaterial.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; - pMaterial.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - pMaterial.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - return pMaterial; - } - }; - } -}); diff --git a/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js b/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js deleted file mode 100644 index 04376e988..000000000 --- a/src/main/webapp/js/components/interface/3dCanvas/SceneFactory.js +++ /dev/null @@ -1,739 +0,0 @@ -define(['jquery'], function () { - - var THREE = require('three'); - - function SceneFactory(viewer) { - - this.viewer = viewer; - this.linesUserInput=false; - this.linesUserPreference=undefined; - this.linesThreshold=2000; - this.aboveLinesThreshold=false; - this.wireframe=false; - this.isAnimated=false; - } - - SceneFactory.prototype = { - - constructor: SceneFactory, - - - setLinesUserInput:function(askUser){ - this.linesUserInput=askUser; - }, - - - buildScene: function (instances) { - this.traverseInstances(instances); - this.viewer.scene.updateMatrixWorld(true); - this.viewer.resetCamera(); - }, - - - updateSceneWithNewInstances: function (instances) { - var updateCamera=false; - if(Object.keys(Gthis.viewer.meshes).length === 0){ - updateCamera=true; - } - for (var g = 0; g < instances.length; g++) { - // add instance to scene - this.checkVisualInstance(instances[g]); - } - if(updateCamera){ - this.viewer.resetCamera(); - } - }, - - /** - * Traverse the instances building a visual object when needed - * - * @param instances - - * skeleton with instances and visual entities - */ - traverseInstances: function (instances, lines, thickness) { - for (var j = 0; j < instances.length; j++) { - this.checkVisualInstance(instances[j], lines, thickness); - } - }, - - /** - * Check if we need to create a visual object for a given instance and keeps iterating - * - * @param instances - - * skeleton with instances and visual entities - */ - checkVisualInstance: function (instance, lines, thickness) { - if (instance.hasCapability(GEPPETTO.Resources.VISUAL_CAPABILITY)) { - //since the visualcapability propagates up through the parents we can avoid visiting things that don't have it - if ((instance.getType().getMetaType() != GEPPETTO.Resources.ARRAY_TYPE_NODE) && instance.getVisualType()) { - this.buildVisualInstance(instance, lines, thickness); - } - // this block keeps traversing the instances - if (instance.getMetaType() == GEPPETTO.Resources.INSTANCE_NODE) { - this.traverseInstances(instance.getChildren(), lines, thickness); - } else if (instance.getMetaType() == GEPPETTO.Resources.ARRAY_INSTANCE_NODE) { - this.traverseInstances(instance, lines, thickness); - } - } - }, - - - buildVisualInstance: function (instance, lines, thickness) { - var meshes = this.generate3DObjects(instance, lines, thickness); - this.init3DObject(meshes, instance); - }, - - /** - * Initializes a group of meshes that were created and adds them to the 3D scene - * - * @param {Object} - * meshes - The meshes that need to be initialized - */ - init3DObject: function (meshes, instance) { - var instancePath = instance.getInstancePath(); - var position = instance.getPosition(); - for (var m in meshes) { - var mesh = meshes[m]; - - mesh.instancePath = instancePath; - // if the model file is specifying a position for the loaded meshes then we translate them here - if (position != null) { - var p = new THREE.Vector3(position.x, position.y, position.z); - mesh.position.set(p.x, p.y, p.z); - mesh.matrixAutoUpdate = false; - mesh.applyMatrix(new THREE.Matrix4().makeTranslation(p.x, p.y, p.z)); - mesh.geometry.verticesNeedUpdate = true; - mesh.updateMatrix(); - // mesh.geometry.translate(position.x, position.y,position.z); - } - this.viewer.scene.add(mesh); - this.viewer.meshes[instancePath] = mesh; - this.viewer.meshes[instancePath].visible = true; - this.viewer.meshes[instancePath].ghosted = false; - this.viewer.meshes[instancePath].defaultOpacity = 1; - this.viewer.meshes[instancePath].selected = false; - this.viewer.meshes[instancePath].input = false; - this.viewer.meshes[instancePath].output = false; - - //Split anything that was splitted before - if (instancePath in this.viewer.splitMeshes) { - var splitMeshes = this.viewer.splitMeshes; - var elements = {}; - for (var splitMesh in splitMeshes) { - if (splitMeshes[splitMesh].instancePath == instancePath && splitMesh != instancePath) { - visualObject = splitMesh.substring(instancePath.length + 1); - elements[visualObject] = ""; - } - } - if (Object.keys(elements).length > 0) { - this.splitGroups(instance, elements); - } - } - } - }, - - /** - * - * @param instance - * @param lines - * @param thickness - * @returns {Array} - */ - generate3DObjects: function (instance, lines, thickness) { - var previous3DObject = this.viewer.meshes[instance.getInstancePath()]; - var color = undefined; - if (previous3DObject) { - color=previous3DObject.material.defaultColor; - // if an object already exists for this aspect we remove it. This could happen in case we are changing how an aspect - // is visualized, e.g. lines over tubes representation - this.viewer.scene.remove(previous3DObject); - var splitMeshes = this.viewer.splitMeshes; - for (var m in splitMeshes) { - if (m.indexOf(instance.getInstancePath()) != -1) { - this.viewer.scene.remove(splitMeshes[m]); - //splitMeshes[m] = null; - } - } - - } - var that = this; - //TODO This can be optimised, no need to create both - var materials = - { - "mesh": that.getMeshPhongMaterial(color), - "line": that.getLineMaterial(thickness,color) - }; - var instanceObjects = []; - var threeDeeObjList = this.walkVisTreeGen3DObjs(instance, materials, lines); - - // only merge if there are more than one object - if (threeDeeObjList.length > 1) { - var mergedObjs = this.merge3DObjects(threeDeeObjList, materials); - // investigate need to obj.dispose for obj in threeDeeObjList - if (mergedObjs != null) { - mergedObjs.instancePath = instance.getInstancePath(); - instanceObjects.push(mergedObjs); - } else { - for (var obj in threeDeeObjList) { - threeDeeObjList[obj].instancePath = instance.getInstancePath(); - instanceObjects.push(threeDeeObjList[obj]); - } - } - } - else if(threeDeeObjList.length==1) - { - // only one object in list, add it to local array and set - instanceObjects.push(threeDeeObjList[0]); - instanceObjects[0].instancePath = instance.getInstancePath(); - } - - return instanceObjects; - }, - - /** - * - * @param instance - * @param materials - * @param lines - * @returns {Array} - */ - walkVisTreeGen3DObjs: function (instance, materials, lines) { - var threeDeeObj = null; - var threeDeeObjList = []; - var visualType = instance.getVisualType(); - if (visualType == undefined) { - return threeDeeObjList; - } - else { - if ($.isArray(visualType)) { - //TODO if there is more than one visual type we need to display all of them - visualType = visualType[0]; - } - } - if (visualType.getMetaType() == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { - for (var v in visualType.getVariables()) { - var visualValue = visualType.getVariables()[v].getWrappedObj().initialValues[0].value; - threeDeeObj = this.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getVariables()[v].getId(), materials, lines); - if (threeDeeObj) { - threeDeeObjList.push(threeDeeObj); - } - } - } else { - var visualValue = visualType.getWrappedObj().defaultValue; - threeDeeObj = this.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getId(), materials, lines); - if (threeDeeObj) { - threeDeeObjList.push(threeDeeObj); - } - } - - return threeDeeObjList; - }, - - - /** - * - * @param objArray - * @param materials - * @returns {*} - */ - merge3DObjects: function (objArray, materials) { - var mergedMeshesPaths = []; - var ret = null; - var mergedLines; - var mergedMeshes; - objArray.forEach(function (obj) { - if (obj instanceof THREE.Line) { - if (mergedLines === undefined) { - mergedLines = new THREE.Geometry() - } - mergedLines.vertices.push(obj.geometry.vertices[0]); - mergedLines.vertices.push(obj.geometry.vertices[1]); - } - else if (obj.geometry.type == "Geometry") { - // This catches both Collada an OBJ - if (objArray.length > 1) { - throw Error("Merging of multiple OBJs or Colladas not supported"); - } - else { - ret = obj; - } - } - else { - if (mergedMeshes === undefined) { - mergedMeshes = new THREE.Geometry() - } - obj.geometry.dynamic = true; - obj.geometry.verticesNeedUpdate = true; - obj.updateMatrix(); - mergedMeshes.merge(obj.geometry, obj.matrix); - } - mergedMeshesPaths.push(obj.instancePath); - - }); - - if (mergedLines === undefined) { - // There are no line geometries, we just create a mesh for the merge of the solid geometries - // and apply the mesh material - ret = new THREE.Mesh(mergedMeshes, materials["mesh"]); - } else { - ret = new THREE.LineSegments(mergedLines, materials["line"]); - if (mergedMeshes != undefined) { - // we merge into a single mesh both types of geometries (from lines and 3D objects) - var tempmesh = new THREE.Mesh(mergedMeshes, materials["mesh"]); - ret.geometry.merge(tempmesh.geometry, tempmesh.matrix); - } - } - - if (ret != null && !Array.isArray(ret)) { - ret.mergedMeshesPaths = mergedMeshesPaths; - } - - return ret; - - }, - - - /** - * - * @param instance - * @param node - * @param id - * @param materials - * @param lines - * @returns {*} - */ - visualizationTreeNodeTo3DObj: function (instance, node, id, materials, lines) { - var threeObject = null; - - if (lines === undefined) { - // Unless it's being forced we use a threshold to decide whether to use lines or cylinders - if (!this.aboveLinesThreshold) { - //Unless we are already above the threshold... - this.aboveLinesThreshold = this.complexity > this.linesThreshold; - - if (this.aboveLinesThreshold) { - - if(this.linesUserInput && this.linesUserPreference==undefined){ - - //we need to ask the user - this.linesUserPreference = confirm("The model you are loading has a complex morphology, would you like to render it using lines instead of 3D shapes? Be careful, choosing to use 3D shapes might crash your browser!"); - - if (this.linesUserPreference) { - this.setAllGeometriesType(GEPPETTO.Resources.GeometryTypes.LINES); - } - else{ - } - } - else{ - this.setAllGeometriesType(GEPPETTO.Resources.GeometryTypes.LINES); - } - } - } - - if(this.aboveLinesThreshold && this.linesUserInput){ - lines = this.linesUserPreference; - } - else{ - lines = this.aboveLinesThreshold; - } - } - - var material = lines ? materials["line"] : materials["mesh"]; - - switch (node.eClass) { - case GEPPETTO.Resources.PARTICLE: - threeObject = this.createParticle(node); - break; - - case GEPPETTO.Resources.CYLINDER: - if (lines) { - threeObject = this.create3DLineFromNode(node, material); - } else { - threeObject = this.create3DCylinderFromNode(node, material); - } - break; - - case GEPPETTO.Resources.SPHERE: - if (lines) { - threeObject = this.create3DLineFromNode(node, material); - } else { - threeObject = this.create3DSphereFromNode(node, material); - } - break; - case GEPPETTO.Resources.COLLADA: - threeObject = this.loadColladaModelFromNode(node); - break; - case GEPPETTO.Resources.OBJ: - threeObject = this.loadThreeOBJModelFromNode(node); - break; - } - if (threeObject) { - threeObject.visible = true; - // TODO: this is empty for collada and obj nodes - var instancePath = instance.getInstancePath() + "." + id; - threeObject.instancePath = instancePath; - threeObject.highlighted = false; - - // TODO: shouldn't that be the vistree? why is it also done at the loadEntity level?? - this.viewer.visualModelMap[instancePath] = threeObject; - } - return threeObject; - }, - - /** - * - * @param node - * @returns {*} - */ - loadColladaModelFromNode: function (node) { - var loader = new THREE.ColladaLoader(); - loader.options.convertUpAxis = true; - var scene = null; - loader.parse(node.collada, function (collada) { - scene = collada.scene; - scene.traverse(function (child) { - if (child instanceof THREE.Mesh) { - child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; - child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.material.wireframe = this.wireframe; - child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.geometry.computeVertexNormals(); - } - if (child instanceof THREE.SkinnedMesh) { - child.material.skinning = true; - child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; - child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.material.wireframe = this.wireframe; - child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.geometry.computeVertexNormals(); - } - }); - }); - return scene; - }, - - /** - * - * @param node - * @returns {*} - */ - loadThreeOBJModelFromNode: function (node) { - var manager = new THREE.LoadingManager(); - manager.onProgress = function (item, loaded, total) { - console.log(item, loaded, total); - }; - var loader = new THREE.OBJLoader(manager); - var scene = loader.parse(node.obj); - - scene.traverse(function (child) { - if (child instanceof THREE.Mesh) { - child.material.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); - child.material.wireframe = this.wireframe; - child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; - child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - child.geometry.computeVertexNormals(); - } - }); - - return scene; - }, - - /** - * - * @param node - * @returns {THREE.Vector3|*} - */ - createParticle: function (node) { - threeObject = new THREE.Vector3(node.position.x, node.position.y, node.position.z); - threeObject.visible = true; - threeObject.instancePath = node.instancePath; - threeObject.highlighted = false; - // TODO: does that need to be done? - this.viewer.visualModelMap[node.instancePath] = threeObject; - - return threeObject; - - }, - - /** - * - * @param node - * @param material - * @returns {THREE.Line} - */ - create3DLineFromNode: function (node, material) { - if (node.eClass == GEPPETTO.Resources.CYLINDER) { - var bottomBasePos = new THREE.Vector3(node.position.x, node.position.y, node.position.z); - var topBasePos = new THREE.Vector3(node.distal.x, node.distal.y, node.distal.z); - - var axis = new THREE.Vector3(); - axis.subVectors(topBasePos, bottomBasePos); - var midPoint = new THREE.Vector3(); - midPoint.addVectors(bottomBasePos, topBasePos).multiplyScalar(0.5); - - var geometry = new THREE.Geometry(); - geometry.vertices.push(bottomBasePos); - geometry.vertices.push(topBasePos); - var threeObject = new THREE.Line(geometry, material); - threeObject.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 2)); - threeObject.lookAt(axis); - threeObject.position.fromArray(midPoint.toArray()); - - threeObject.geometry.verticesNeedUpdate = true; - } else if (node.eClass == GEPPETTO.Resources.SPHERE) { - var sphere = new THREE.SphereGeometry(node.radius, 20, 20); - threeObject = new THREE.Mesh(sphere, material); - threeObject.position.set(node.position.x, node.position.y, node.position.z); - threeObject.geometry.verticesNeedUpdate = true; - } - return threeObject; - }, - - /** - * - * @param cylNode - * @param material - * @returns {THREE.Mesh} - */ - create3DCylinderFromNode: function (cylNode, material) { - - var bottomBasePos = new THREE.Vector3(cylNode.position.x, cylNode.position.y, cylNode.position.z); - var topBasePos = new THREE.Vector3(cylNode.distal.x, cylNode.distal.y, cylNode.distal.z); - - var axis = new THREE.Vector3(); - axis.subVectors(topBasePos, bottomBasePos); - var midPoint = new THREE.Vector3(); - midPoint.addVectors(bottomBasePos, topBasePos).multiplyScalar(0.5); - - var c = new THREE.CylinderGeometry(cylNode.topRadius, cylNode.bottomRadius, axis.length(), 20, 1, false); - c.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 2)); - var threeObject = new THREE.Mesh(c, material); - - threeObject.lookAt(axis); - threeObject.position.fromArray(midPoint.toArray()); - - threeObject.geometry.verticesNeedUpdate = true; - return threeObject; - }, - - /** - * Modify the origin and radius of a sphere - * @returns {THREE.Mesh} - */ - modify3DSphere:function(object,x,y,z,radius){ - // Impossible to change the radius of a Sphere. - // Removing old object and creating a new one - this.viewer.scene.remove(object); - return this.add3DSphere(x,y,z,radius); - }, - - /** - * Add a 3D sphere to the scene at the given coordinates (4) points. - * It could be any geometry really. - * @returns {THREE.Mesh} - */ - add3DSphere: function (x,y,z,radius) { - if (this.aboveLinesThreshold) { - radius = 1; - } - - var material= new THREE.MeshBasicMaterial({side:THREE.DoubleSide}); - material.nowireframe=true; - material.opacity= 0.6; - material.transparent=true; - material.color.setHex("0xff0000"); - - var sphereNode ={radius:radius,position:{x:x, y:y, z:z}} - var mesh = this.create3DSphereFromNode(sphereNode, material) - mesh.renderOrder=1; - mesh.clickThrough=true; - this.viewer.scene.add(mesh); - return mesh; - }, - - - /** - * Add a 3D plane to the scene at the given coordinates (4) points. - * It could be any geometry really. - * @returns {THREE.Mesh} - */ - add3DPlane: function (x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4, textureURL) { - - var geometry=new THREE.Geometry(); - geometry.vertices.push( - new THREE.Vector3(x1,y1,z1),//vertex0 - new THREE.Vector3(x2,y2,z2),//1 - new THREE.Vector3(x3,y3,z3),//2 - new THREE.Vector3(x4,y4,z4)//3 - ); - geometry.faces.push( - new THREE.Face3(2,1,0),//use vertices of rank 2,1,0 - new THREE.Face3(3,1,2)//vertices[3],1,2... - ); - geometry.computeBoundingBox(); - - var max = geometry.boundingBox.max, - min = geometry.boundingBox.min; - var offset = new THREE.Vector2(0 - min.x, 0 - min.y); - var range = new THREE.Vector2(max.x - min.x, max.y - min.y); - var faces = geometry.faces; - - geometry.faceVertexUvs[0] = []; - - for (var i = 0; i < faces.length ; i++) { - - var v1 = geometry.vertices[faces[i].a], - v2 = geometry.vertices[faces[i].b], - v3 = geometry.vertices[faces[i].c]; - - geometry.faceVertexUvs[0].push([ - new THREE.Vector2((v1.x + offset.x)/range.x ,(v1.y + offset.y)/range.y), - new THREE.Vector2((v2.x + offset.x)/range.x ,(v2.y + offset.y)/range.y), - new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y) - ]); - } - geometry.uvsNeedUpdate = true; - geometry.dynamic = true; - - var material= new THREE.MeshBasicMaterial({side:THREE.DoubleSide}); - material.nowireframe=true; - if(textureURL!=undefined){ - var loader = new THREE.TextureLoader(); - // load a resource - loader.load( - // resource URL - textureURL, - // Function when resource is loaded - function ( texture ) { - //texture.minFilter = THREE.LinearFilter; - material.map=texture; - texture.flipY = false; - material.opacity= 0.3; - material.transparent=true; - material.needsUpdate = true; - - }, - // Function called when download progresses - function ( xhr ) { - console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); - }, - // Function called when download errors - function ( xhr ) { - console.log( 'An error happened' ); - } - ); - - } - else{ - material.opacity= 0.3; - material.transparent=true; - material.color.setHex("0xb0b0b0"); - } - - var mesh = new THREE.Mesh(geometry, material); - mesh.renderOrder=1; - mesh.clickThrough=true; - this.viewer.scene.add(mesh); - return mesh; - }, - - /** - * Modify the coordinates (4) points of an existing plane. - * @returns {THREE.Mesh} - */ - modify3DPlane:function(object,x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4){ - object.geometry.vertices[0].set(x1,y1,z1); - object.geometry.vertices[1].set(x2,y2,z2); - object.geometry.vertices[2].set(x3,y3,z3); - object.geometry.vertices[3].set(x4,y4,z4); - object.geometry.verticesNeedUpdate = true; - return object; - }, - - - /** - * - * @param sphereNode - * @param material - * @returns {THREE.Mesh|*} - */ - create3DSphereFromNode: function (sphereNode, material) { - - var sphere = new THREE.SphereGeometry(sphereNode.radius, 20, 20); - // sphere.applyMatrix(new THREE.Matrix4().makeScale(-1,1,1)); - var threeObject = new THREE.Mesh(sphere, material); - threeObject.position.set(sphereNode.position.x, sphereNode.position.y, sphereNode.position.z); - - threeObject.geometry.verticesNeedUpdate = true; - return threeObject; - }, - - /** - * - * @param thickness - * @returns {THREE.LineBasicMaterial} - */ - getLineMaterial: function (thickness, color) { - var options = {}; - if (thickness) { - options.linewidth = thickness; - } - if (color == undefined) { - color = GEPPETTO.Resources.COLORS.DEFAULT; - } - var material = new THREE.LineBasicMaterial(options); - material.color.setHex(color); - material.defaultColor = color; - material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - return material; - }, - - /** - * - * @param color - * @returns {THREE.MeshPhongMaterial} - */ - getMeshPhongMaterial: function (color) { - if (color == undefined) { - color = GEPPETTO.Resources.COLORS.DEFAULT; - } - var material = new THREE.MeshPhongMaterial( - { - opacity: 1, - shininess: 10, - shading: THREE.SmoothShading - }); - - material.color.setHex(color); - material.defaultColor = color; - material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - return material; - }, - - /** - * - * @returns {THREE.PointsMaterial} - */ - getParticleMaterial: function () { - var textureLoader = new THREE.TextureLoader(); - var pMaterial = new THREE.PointsMaterial( - { - size: 5, - map: textureLoader.load("geppetto/images/particle.png"), - blending: THREE.AdditiveBlending, - depthTest: false, - transparent: true - }); - pMaterial.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); - pMaterial.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; - pMaterial.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; - pMaterial.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; - return pMaterial; - } - -}; - - return SceneFactory; -}); - diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js new file mode 100644 index 000000000..8f266a40a --- /dev/null +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -0,0 +1,2301 @@ +/* +* The 3D engine used by the 3D canvas component. This class is internal, any method you +* use from inside here might break between versions. Methods maintained are the exposed ones +* through the 3D canvas component. +* +*/ +define(['jquery'], function () { + + var THREE = require('three'); + require('./TrackballControls'); + require('./OBJLoader'); + THREE.ColladaLoader = require('imports?THREE=three!exports?THREE.ColladaLoader!../../../../node_modules\/three\/examples\/js\/loaders\/ColladaLoader'); + THREE.ConvolutionShader = require('imports?THREE=three!exports?THREE.ConvolutionShader!../../../../node_modules\/three\/examples\/js\/shaders\/ConvolutionShader'); + THREE.CopyShader = require('imports?THREE=three!exports?THREE.CopyShader!../../../../node_modules\/three\/examples\/js\/shaders\/CopyShader'); + THREE.FilmShader = require('imports?THREE=three!exports?THREE.FilmShader!../../../../node_modules\/three\/examples\/js\/shaders\/FilmShader'); + THREE.FocusShader = require('imports?THREE=three!exports?THREE.FocusShader!../../../../node_modules\/three\/examples\/js\/shaders\/FocusShader'); + THREE.EffectComposer = require('imports?THREE=three!exports?THREE.EffectComposer!../../../../node_modules\/three\/examples\/js\/postprocessing\/EffectComposer'); + THREE.MaskPass = require('imports?THREE=three!exports?THREE.MaskPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/MaskPass'); + THREE.RenderPass = require('imports?THREE=three!exports?THREE.RenderPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/RenderPass'); + THREE.BloomPass = require('imports?THREE=three!exports?THREE.BloomPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/BloomPass'); + THREE.ShaderPass = require('imports?THREE=three!exports?THREE.ShaderPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/ShaderPass'); + THREE.FilmPass = require('imports?THREE=three!exports?THREE.FilmPass!../../../../node_modules\/three\/examples\/js\/postprocessing\/FilmPass'); + + function ThreeDEngine(container, viewerId) { + + this.container = container; + this.viewerId = viewerId; + + //Engine components + this.scene = new THREE.Scene(); + this.camera = null; + this.controls = null; + this.renderer = null; + this.stats = null; + this.projector = null; + this.sceneCenter = new THREE.Vector3(); + this.cameraPosition = new THREE.Vector3(); + + this.mouse = { x: 0, y: 0 }; + + //The content of the scene + this.meshes = {}; + this.splitMeshes = {}; + this.connectionLines = {}; + this.visualModelMap = {}; + this.complexity = 0; + + //Settings + this.linesThreshold = 2000; + this.aboveLinesThreshold = false; + this.wireframe = false; + this.isAnimated = false; + this.debugUpdate = false; + this.needsUpdate = false; + this.pickingEnabled = true; // flag to enable disable 3d picking + this.linesUserInput = false; + this.linesUserPreference = undefined; + + //Initialisation + this.setupCamera(); + this.setupRenderer(); + this.setupLights(); + this.setupControls(); + this.setupListeners(); + this.animate(); + }; + + ThreeDEngine.prototype = { + + constructor: ThreeDEngine, + + /** + * Sets up the controls used by the camera to make it able to zoom and + * pan. + */ + setupControls: function () { + // Controls + this.controls = new THREE.TrackballControls(this.camera, this.renderer.domElement, this.viewerId); + this.controls.noZoom = false; + this.controls.noPan = false; + }, + + /** + * Sets up the camera that is used to view the objects in the 3D Scene. + * @command setupCamera() + */ + setupCamera: function () { + // Camera + var width = $(this.container).width(); + var height = $(this.container).height(); + var angle = 60; + var aspect = width / angle; + var near = 10; + var far = 2000000; + this.camera = new THREE.PerspectiveCamera(angle, aspect, near, far); + this.scene.add(this.camera); + this.camera.position.set(this.cameraPosition.x, this.cameraPosition.y, this.cameraPosition.z); + this.camera.up = new THREE.Vector3(0, 1, 0); + this.camera.direction = new THREE.Vector3(0, 0, 1); + this.camera.lookAt(this.sceneCenter); + }, + + /** + * Set up the WebGL Renderer + */ + setupRenderer: function () { + // Reuse a single WebGL renderer. + // NOTE: Recreating the renderer causes camera displacement on Chrome OSX. + if (!this.canvasCreated) { + this.renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: true + }); + + } + this.configureRenderer(); + this.canvasCreated = true; + }, + + /** + * Set up the listeners use to detect mouse movement and windoe resizing + */ + setupListeners: function () { + var that = this; + // when the mouse moves, call the given function + this.renderer.domElement.addEventListener('mousedown', function (event) { + if (event.button == 0) //only for left click + { + if (that.pickingEnabled) { + var intersects = that.getIntersectedObjects(); + + if (intersects.length > 0) { + var selected = ""; + var geometryIdentifier = ""; + + // sort intersects + var compare = function (a, b) { + if (a.distance < b.distance) + return -1; + if (a.distance > b.distance) + return 1; + return 0; + }; + + intersects.sort(compare); + + var selectedIntersect; + // Iterate and get the first visible item (they are now ordered by proximity) + for (var i = 0; i < intersects.length; i++) { + // figure out if the entity is visible + var instancePath = ""; + if (intersects[i].object.hasOwnProperty("instancePath")) { + instancePath = intersects[i].object.instancePath; + geometryIdentifier = intersects[i].object.geometryIdentifier; + } else { + //weak assumption: if the object doesn't have an instancePath its parent will + instancePath = intersects[i].object.parent.instancePath; + geometryIdentifier = intersects[i].object.parent.geometryIdentifier; + } + if (instancePath != null || undefined) { + var visible = eval(instancePath + '.visible'); + if (intersects.length == 1 || i == intersects.length) { + //if there's only one element intersected we select it regardless of its opacity + if (visible) { + selected = instancePath; + selectedIntersect = intersects[i]; + break; + } + } else { + //if there are more than one element intersected and opacity of the current one is less than 1 + //we skip it to realize a "pick through" + var opacity = this.meshes[instancePath].defaultOpacity; + if ((opacity == 1 && visible) || GEPPETTO.isKeyPressed("ctrl")) { + selected = instancePath; + selectedIntersect = intersects[i]; + break; + } else if (visible && opacity < 1 && opacity > 0) { + //if only transparent objects intersected select first or the next down if + //one is already selected in order to enable "burrow through" sample. + if (selected == "" && !eval(instancePath + '.selected')) { + selected = instancePath; + selectedIntersect = intersects[i]; + } else { + if (eval(instancePath + '.selected') && i != intersects.length - 1) { + selected = ""; + } + } + } + } + } + } + + + if (selected != "") { + if (that.meshes.hasOwnProperty(selected) || that.splitMeshes.hasOwnProperty(selected)) { + if (!GEPPETTO.isKeyPressed("shift")) { + that.unSelectAll(); + } + + var selectedIntersectCoordinates = [selectedIntersect.point.x, selectedIntersect.point.y, selectedIntersect.point.z] + if (geometryIdentifier == undefined) { + geometryIdentifier = ""; + } + GEPPETTO.Console.executeCommand(selected + '.select(' + false + ', ' + '"' + geometryIdentifier + '", [' + selectedIntersectCoordinates + '])'); + } + } + } else if (GEPPETTO.isKeyPressed("ctrl")) { + that.unSelectAll(); + } + } + } + }, false); + + this.renderer.domElement.addEventListener('mousemove', function (event) { + that.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + that.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + }, false); + + }, + + setSize: function (width, height) { + this.camera.aspect = width / height; + this.camera.updateProjectionMatrix(); + this.renderer.setSize(width, height); + this.composer.setSize(width, height); + }, + + configureRenderer: function (shaders) { + + if (shaders == undefined) { + shaders = false; + } + + var color = new THREE.Color(this.backgroundColor); + //this.renderer.setClearColor(color, 1); + var width = $(this.container).width(); + var height = $(this.container).height(); + this.renderer.setPixelRatio(window.devicePixelRatio); + this.renderer.setSize(width, height); + this.renderer.autoClear = false; + this.container.appendChild(this.renderer.domElement); + + var renderModel = new THREE.RenderPass(this.scene, this.camera); + + this.composer = new THREE.EffectComposer(this.renderer); + + if (shaders) { + var effectBloom = new THREE.BloomPass(0.75); + var effectFilm = new THREE.FilmPass(0.5, 0.5, 1448, false); + var effectFocus = new THREE.ShaderPass(THREE.FocusShader); + + effectFocus.uniforms["screenWidth"].value = window.innerWidth; + effectFocus.uniforms["screenHeight"].value = window.innerHeight; + + effectFocus.renderToScreen = true; + + this.composer.addPass(renderModel); + this.composer.addPass(effectBloom); + this.composer.addPass(effectFilm); + this.composer.addPass(effectFocus); + } else { + //standard + var copyPass = new THREE.ShaderPass(THREE.CopyShader); + copyPass.renderToScreen = true; + this.composer.addPass(renderModel); + this.composer.addPass(copyPass); + } + + }, + + /** + * Light up the scene + */ + setupLights: function () { + // Lights + this.camera.add(new THREE.PointLight(0xffffff, 1.5)); + + }, + + setLinesUserInput: function (askUser) { + this.linesUserInput = askUser; + }, + + /** + * Reset camera + */ + resetCamera: function () { + this.controls.reset(); + + var aabbMin = null; + var aabbMax = null; + + this.scene.traverse(function (child) { + if (child.hasOwnProperty("geometry")) { + child.geometry.computeBoundingBox(); + + var bb = child.geometry.boundingBox; + bb.translate(child.localToWorld(new THREE.Vector3())); + + // If min and max vectors are null, first values become + // default min and max + if (aabbMin == null && aabbMax == null) { + aabbMin = bb.min; + aabbMax = bb.max; + } + + // Compare other meshes, particles BB's to find min and max + else { + aabbMin.x = Math.min(aabbMin.x, bb.min.x); + aabbMin.y = Math.min(aabbMin.y, bb.min.y); + aabbMin.z = Math.min(aabbMin.z, bb.min.z); + aabbMax.x = Math.max(aabbMax.x, bb.max.x); + aabbMax.y = Math.max(aabbMax.y, bb.max.y); + aabbMax.z = Math.max(aabbMax.z, bb.max.z); + } + } + }); + + if (aabbMin != null && aabbMax != null) { + // Compute world AABB center + this.sceneCenter.x = (aabbMax.x + aabbMin.x) * 0.5; + this.sceneCenter.y = (aabbMax.y + aabbMin.y) * 0.5; + this.sceneCenter.z = (aabbMax.z + aabbMin.z) * 0.5; + + this.updateCamera(aabbMax, aabbMin); + } + }, + + /** + * Update camera with new position and place to lookat + */ + updateCamera: function (aabbMax, aabbMin) { + // Compute world AABB "radius" + var diag = new THREE.Vector3(); + diag = diag.subVectors(aabbMax, aabbMin); + var radius = diag.length() * 0.5; + + this.pointCameraTo(this.sceneCenter); + + // Compute offset needed to move the camera back that much needed to center AABB + var offset = radius / Math.sin(Math.PI / 180.0 * this.camera.fov * 0.5); + + var dir = this.camera.direction.clone(); + dir.multiplyScalar(offset); + + // Store camera position + this.camera.position.addVectors(dir, this.controls.target); + this.camera.updateProjectionMatrix(); + }, + + boundingBox: function (obj) { + if (obj instanceof THREE.Mesh) { + + var geometry = obj.geometry; + geometry.computeBoundingBox(); + return geometry.boundingBox; + + } + + if (obj instanceof THREE.Object3D) { + + var bb = new THREE.Box3(); + for (var i = 0; i < obj.children.length; i++) { + bb.union(this.boundingBox(obj.children[i])); + } + return bb; + } + }, + + shapeCenterOfGravity: function (obj) { + return this.boundingBox(obj).center(); + }, + + pointCameraTo: function (node) { + // Refocus camera to the center of the new object + var COG; + if (node instanceof THREE.Vector3) { + COG = node; + } else { + COG = this.shapeCenterOfGravity(node); + } + var v = new THREE.Vector3(); + v.subVectors(COG, this.controls.target); + this.camera.position.addVectors( + this.camera.position, v); + + // retrieve camera orientation + + this.camera.lookAt(COG); + this.controls.target.set(COG.x, COG.y, COG.z); + }, + + /** + * Sets up the HUD display with the scene stat's fps. + */ + setupStats: function () { + // Stats + if ($("#stats").length == 0) { + if (VARS != null) { + this.stats = new Stats(); + this.stats.domElement.style.float = 'right'; + this.stats.domElement.style.position = 'absolute'; + this.stats.domElement.style.top = '60px'; + this.stats.domElement.style.right = '5px'; + this.stats.domElement.style.zIndex = 100; + $('#controls').append(this.stats.domElement); + } + } + }, + + /** + * Displays HUD for FPS stats + */ + toggleStats: function (mode) { + if (mode) { + if ($("#stats").length == 0) { + this.setupStats(); + } else { + $("#stats").show(); + } + } else { + $("#stats").hide(); + } + }, + + + /** + * Adds debug axis to the scene + */ + setupAxis: function () { + // To use enter the axis length + this.scene.add(new THREE.AxisHelper(200)); + }, + + /** + * Renders objects in the scene + */ + renderCanvas: function () { + this.renderer.clear(); + this.composer.render(0.01); + }, + + /** + * Returns intersected objects from mouse click + * + * @returns {Array} a list of objects intersected by the current mouse coordinates + */ + getIntersectedObjects: function () { + // create a Ray with origin at the mouse position and direction into th scene (camera direction) + var vector = new THREE.Vector3(this.mouse.x, this.mouse.y, 1); + vector.unproject(this.camera); + + var raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize()); + + var visibleChildren = []; + this.scene.traverse(function (child) { + if (child.visible && !(child.clickThrough == true)) { + if (child.geometry != null && child.geometry != undefined) { + child.geometry.computeBoundingBox(); + visibleChildren.push(child); + } + } + }); + + // returns an array containing all objects in the scene with which the ray intersects + return raycaster.intersectObjects(visibleChildren); + }, + + + buildScene: function (instances) { + this.traverseInstances(instances); + this.scene.updateMatrixWorld(true); + this.resetCamera(); + }, + + + updateSceneWithNewInstances: function (instances) { + var updateCamera = false; + if (Object.keys(this.meshes).length === 0) { + updateCamera = true; + } + for (var g = 0; g < instances.length; g++) { + // add instance to scene + this.checkVisualInstance(instances[g]); + } + if (updateCamera) { + this.resetCamera(); + } + }, + + /** + * Traverse the instances building a visual object when needed + * + * @param instances - + * skeleton with instances and visual entities + */ + traverseInstances: function (instances, lines, thickness) { + for (var j = 0; j < instances.length; j++) { + this.checkVisualInstance(instances[j], lines, thickness); + } + }, + + /** + * Check if we need to create a visual object for a given instance and keeps iterating + * + * @param instances - + * skeleton with instances and visual entities + */ + checkVisualInstance: function (instance, lines, thickness) { + if (instance.hasCapability(GEPPETTO.Resources.VISUAL_CAPABILITY)) { + //since the visualcapability propagates up through the parents we can avoid visiting things that don't have it + if ((instance.getType().getMetaType() != GEPPETTO.Resources.ARRAY_TYPE_NODE) && instance.getVisualType()) { + this.buildVisualInstance(instance, lines, thickness); + } + // this block keeps traversing the instances + if (instance.getMetaType() == GEPPETTO.Resources.INSTANCE_NODE) { + this.traverseInstances(instance.getChildren(), lines, thickness); + } else if (instance.getMetaType() == GEPPETTO.Resources.ARRAY_INSTANCE_NODE) { + this.traverseInstances(instance, lines, thickness); + } + } + }, + + + buildVisualInstance: function (instance, lines, thickness) { + var meshes = this.generate3DObjects(instance, lines, thickness); + this.init3DObject(meshes, instance); + }, + + + /** + * Initializes a group of meshes that were created and adds them to the 3D scene + * + * @param {Object} + * meshes - The meshes that need to be initialized + */ + init3DObject: function (meshes, instance) { + var instancePath = instance.getInstancePath(); + var position = instance.getPosition(); + for (var m in meshes) { + var mesh = meshes[m]; + + mesh.instancePath = instancePath; + // if the model file is specifying a position for the loaded meshes then we translate them here + if (position != null) { + var p = new THREE.Vector3(position.x, position.y, position.z); + mesh.position.set(p.x, p.y, p.z); + mesh.matrixAutoUpdate = false; + mesh.applyMatrix(new THREE.Matrix4().makeTranslation(p.x, p.y, p.z)); + mesh.geometry.verticesNeedUpdate = true; + mesh.updateMatrix(); + // mesh.geometry.translate(position.x, position.y,position.z); + } + this.scene.add(mesh); + this.meshes[instancePath] = mesh; + this.meshes[instancePath].visible = true; + this.meshes[instancePath].ghosted = false; + this.meshes[instancePath].defaultOpacity = 1; + this.meshes[instancePath].selected = false; + this.meshes[instancePath].input = false; + this.meshes[instancePath].output = false; + + //Split anything that was splitted before + if (instancePath in this.splitMeshes) { + var splitMeshes = this.splitMeshes; + var elements = {}; + for (var splitMesh in splitMeshes) { + if (splitMeshes[splitMesh].instancePath == instancePath && splitMesh != instancePath) { + visualObject = splitMesh.substring(instancePath.length + 1); + elements[visualObject] = ""; + } + } + if (Object.keys(elements).length > 0) { + this.splitGroups(instance, elements); + } + } + } + }, + + /** + * + * @param instance + * @param lines + * @param thickness + * @returns {Array} + */ + generate3DObjects: function (instance, lines, thickness) { + var previous3DObject = this.meshes[instance.getInstancePath()]; + var color = undefined; + if (previous3DObject) { + color = previous3DObject.material.defaultColor; + // if an object already exists for this aspect we remove it. This could happen in case we are changing how an aspect + // is visualized, e.g. lines over tubes representation + this.scene.remove(previous3DObject); + var splitMeshes = this.splitMeshes; + for (var m in splitMeshes) { + if (m.indexOf(instance.getInstancePath()) != -1) { + this.scene.remove(splitMeshes[m]); + //splitMeshes[m] = null; + } + } + + } + var that = this; + //TODO This can be optimised, no need to create both + var materials = + { + "mesh": that.getMeshPhongMaterial(color), + "line": that.getLineMaterial(thickness, color) + }; + var instanceObjects = []; + var threeDeeObjList = this.walkVisTreeGen3DObjs(instance, materials, lines); + + // only merge if there are more than one object + if (threeDeeObjList.length > 1) { + var mergedObjs = this.merge3DObjects(threeDeeObjList, materials); + // investigate need to obj.dispose for obj in threeDeeObjList + if (mergedObjs != null) { + mergedObjs.instancePath = instance.getInstancePath(); + instanceObjects.push(mergedObjs); + } else { + for (var obj in threeDeeObjList) { + threeDeeObjList[obj].instancePath = instance.getInstancePath(); + instanceObjects.push(threeDeeObjList[obj]); + } + } + } + else if (threeDeeObjList.length == 1) { + // only one object in list, add it to local array and set + instanceObjects.push(threeDeeObjList[0]); + instanceObjects[0].instancePath = instance.getInstancePath(); + } + + return instanceObjects; + }, + + /** + * + * @param instance + * @param materials + * @param lines + * @returns {Array} + */ + walkVisTreeGen3DObjs: function (instance, materials, lines) { + var threeDeeObj = null; + var threeDeeObjList = []; + var visualType = instance.getVisualType(); + if (visualType == undefined) { + return threeDeeObjList; + } + else { + if ($.isArray(visualType)) { + //TODO if there is more than one visual type we need to display all of them + visualType = visualType[0]; + } + } + if (visualType.getMetaType() == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { + for (var v in visualType.getVariables()) { + var visualValue = visualType.getVariables()[v].getWrappedObj().initialValues[0].value; + threeDeeObj = this.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getVariables()[v].getId(), materials, lines); + if (threeDeeObj) { + threeDeeObjList.push(threeDeeObj); + } + } + } else { + var visualValue = visualType.getWrappedObj().defaultValue; + threeDeeObj = this.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getId(), materials, lines); + if (threeDeeObj) { + threeDeeObjList.push(threeDeeObj); + } + } + + return threeDeeObjList; + }, + + + /** + * + * @param objArray + * @param materials + * @returns {*} + */ + merge3DObjects: function (objArray, materials) { + var mergedMeshesPaths = []; + var ret = null; + var mergedLines; + var mergedMeshes; + objArray.forEach(function (obj) { + if (obj instanceof THREE.Line) { + if (mergedLines === undefined) { + mergedLines = new THREE.Geometry() + } + mergedLines.vertices.push(obj.geometry.vertices[0]); + mergedLines.vertices.push(obj.geometry.vertices[1]); + } + else if (obj.geometry.type == "Geometry") { + // This catches both Collada an OBJ + if (objArray.length > 1) { + throw Error("Merging of multiple OBJs or Colladas not supported"); + } + else { + ret = obj; + } + } + else { + if (mergedMeshes === undefined) { + mergedMeshes = new THREE.Geometry() + } + obj.geometry.dynamic = true; + obj.geometry.verticesNeedUpdate = true; + obj.updateMatrix(); + mergedMeshes.merge(obj.geometry, obj.matrix); + } + mergedMeshesPaths.push(obj.instancePath); + + }); + + if (mergedLines === undefined) { + // There are no line geometries, we just create a mesh for the merge of the solid geometries + // and apply the mesh material + ret = new THREE.Mesh(mergedMeshes, materials["mesh"]); + } else { + ret = new THREE.LineSegments(mergedLines, materials["line"]); + if (mergedMeshes != undefined) { + // we merge into a single mesh both types of geometries (from lines and 3D objects) + var tempmesh = new THREE.Mesh(mergedMeshes, materials["mesh"]); + ret.geometry.merge(tempmesh.geometry, tempmesh.matrix); + } + } + + if (ret != null && !Array.isArray(ret)) { + ret.mergedMeshesPaths = mergedMeshesPaths; + } + + return ret; + + }, + + + /** + * + * @param instance + * @param node + * @param id + * @param materials + * @param lines + * @returns {*} + */ + visualizationTreeNodeTo3DObj: function (instance, node, id, materials, lines) { + var threeObject = null; + + if (lines === undefined) { + // Unless it's being forced we use a threshold to decide whether to use lines or cylinders + if (!this.aboveLinesThreshold) { + //Unless we are already above the threshold... + this.aboveLinesThreshold = this.complexity > this.linesThreshold; + + if (this.aboveLinesThreshold) { + + if (this.linesUserInput && this.linesUserPreference == undefined) { + + //we need to ask the user + this.linesUserPreference = confirm("The model you are loading has a complex morphology, would you like to render it using lines instead of 3D shapes? Be careful, choosing to use 3D shapes might crash your browser!"); + + if (this.linesUserPreference) { + this.setAllGeometriesType(GEPPETTO.Resources.GeometryTypes.LINES); + } + else { + } + } + else { + this.setAllGeometriesType(GEPPETTO.Resources.GeometryTypes.LINES); + } + } + } + + if (this.aboveLinesThreshold && this.linesUserInput) { + lines = this.linesUserPreference; + } + else { + lines = this.aboveLinesThreshold; + } + } + + var material = lines ? materials["line"] : materials["mesh"]; + + switch (node.eClass) { + case GEPPETTO.Resources.PARTICLE: + threeObject = this.createParticle(node); + break; + + case GEPPETTO.Resources.CYLINDER: + if (lines) { + threeObject = this.create3DLineFromNode(node, material); + } else { + threeObject = this.create3DCylinderFromNode(node, material); + } + this.complexity++; + break; + + case GEPPETTO.Resources.SPHERE: + if (lines) { + threeObject = this.create3DLineFromNode(node, material); + } else { + threeObject = this.create3DSphereFromNode(node, material); + } + this.complexity++; + break; + case GEPPETTO.Resources.COLLADA: + threeObject = this.loadColladaModelFromNode(node); + this.complexity++; + break; + case GEPPETTO.Resources.OBJ: + threeObject = this.loadThreeOBJModelFromNode(node); + this.complexity++; + break; + } + if (threeObject) { + threeObject.visible = true; + // TODO: this is empty for collada and obj nodes + var instancePath = instance.getInstancePath() + "." + id; + threeObject.instancePath = instancePath; + threeObject.highlighted = false; + + // TODO: shouldn't that be the vistree? why is it also done at the loadEntity level?? + this.visualModelMap[instancePath] = threeObject; + } + return threeObject; + }, + + /** + * + * @param node + * @returns {*} + */ + loadColladaModelFromNode: function (node) { + var loader = new THREE.ColladaLoader(); + loader.options.convertUpAxis = true; + var scene = null; + loader.parse(node.collada, function (collada) { + scene = collada.scene; + scene.traverse(function (child) { + if (child instanceof THREE.Mesh) { + child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; + child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.material.wireframe = this.wireframe; + child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.geometry.computeVertexNormals(); + } + if (child instanceof THREE.SkinnedMesh) { + child.material.skinning = true; + child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; + child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.material.wireframe = this.wireframe; + child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.geometry.computeVertexNormals(); + } + }); + }); + return scene; + }, + + /** + * + * @param node + * @returns {*} + */ + loadThreeOBJModelFromNode: function (node) { + var manager = new THREE.LoadingManager(); + manager.onProgress = function (item, loaded, total) { + console.log(item, loaded, total); + }; + var loader = new THREE.OBJLoader(manager); + var scene = loader.parse(node.obj); + + scene.traverse(function (child) { + if (child instanceof THREE.Mesh) { + child.material.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); + child.material.wireframe = this.wireframe; + child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; + child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + child.geometry.computeVertexNormals(); + } + }); + + return scene; + }, + + /** + * + * @param node + * @returns {THREE.Vector3|*} + */ + createParticle: function (node) { + threeObject = new THREE.Vector3(node.position.x, node.position.y, node.position.z); + threeObject.visible = true; + threeObject.instancePath = node.instancePath; + threeObject.highlighted = false; + // TODO: does that need to be done? + this.visualModelMap[node.instancePath] = threeObject; + + return threeObject; + + }, + + /** + * + * @param node + * @param material + * @returns {THREE.Line} + */ + create3DLineFromNode: function (node, material) { + if (node.eClass == GEPPETTO.Resources.CYLINDER) { + var bottomBasePos = new THREE.Vector3(node.position.x, node.position.y, node.position.z); + var topBasePos = new THREE.Vector3(node.distal.x, node.distal.y, node.distal.z); + + var axis = new THREE.Vector3(); + axis.subVectors(topBasePos, bottomBasePos); + var midPoint = new THREE.Vector3(); + midPoint.addVectors(bottomBasePos, topBasePos).multiplyScalar(0.5); + + var geometry = new THREE.Geometry(); + geometry.vertices.push(bottomBasePos); + geometry.vertices.push(topBasePos); + var threeObject = new THREE.Line(geometry, material); + threeObject.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 2)); + threeObject.lookAt(axis); + threeObject.position.fromArray(midPoint.toArray()); + + threeObject.geometry.verticesNeedUpdate = true; + } else if (node.eClass == GEPPETTO.Resources.SPHERE) { + var sphere = new THREE.SphereGeometry(node.radius, 20, 20); + threeObject = new THREE.Mesh(sphere, material); + threeObject.position.set(node.position.x, node.position.y, node.position.z); + threeObject.geometry.verticesNeedUpdate = true; + } + return threeObject; + }, + + /** + * + * @param cylNode + * @param material + * @returns {THREE.Mesh} + */ + create3DCylinderFromNode: function (cylNode, material) { + + var bottomBasePos = new THREE.Vector3(cylNode.position.x, cylNode.position.y, cylNode.position.z); + var topBasePos = new THREE.Vector3(cylNode.distal.x, cylNode.distal.y, cylNode.distal.z); + + var axis = new THREE.Vector3(); + axis.subVectors(topBasePos, bottomBasePos); + var midPoint = new THREE.Vector3(); + midPoint.addVectors(bottomBasePos, topBasePos).multiplyScalar(0.5); + + var c = new THREE.CylinderGeometry(cylNode.topRadius, cylNode.bottomRadius, axis.length(), 20, 1, false); + c.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / 2)); + var threeObject = new THREE.Mesh(c, material); + + threeObject.lookAt(axis); + threeObject.position.fromArray(midPoint.toArray()); + + threeObject.geometry.verticesNeedUpdate = true; + return threeObject; + }, + + /** + * Modify the origin and radius of a sphere + * @returns {THREE.Mesh} + */ + modify3DSphere: function (object, x, y, z, radius) { + // Impossible to change the radius of a Sphere. + // Removing old object and creating a new one + this.scene.remove(object); + return this.add3DSphere(x, y, z, radius); + }, + + /** + * Add a 3D sphere to the scene at the given coordinates (4) points. + * It could be any geometry really. + * @returns {THREE.Mesh} + */ + add3DSphere: function (x, y, z, radius) { + if (this.aboveLinesThreshold) { + radius = 1; + } + + var material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide }); + material.nowireframe = true; + material.opacity = 0.6; + material.transparent = true; + material.color.setHex("0xff0000"); + + var sphereNode = { radius: radius, position: { x: x, y: y, z: z } } + var mesh = this.create3DSphereFromNode(sphereNode, material) + mesh.renderOrder = 1; + mesh.clickThrough = true; + this.scene.add(mesh); + return mesh; + }, + + + /** + * Add a 3D plane to the scene at the given coordinates (4) points. + * It could be any geometry really. + * @returns {THREE.Mesh} + */ + add3DPlane: function (x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, textureURL) { + + var geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3(x1, y1, z1),//vertex0 + new THREE.Vector3(x2, y2, z2),//1 + new THREE.Vector3(x3, y3, z3),//2 + new THREE.Vector3(x4, y4, z4)//3 + ); + geometry.faces.push( + new THREE.Face3(2, 1, 0),//use vertices of rank 2,1,0 + new THREE.Face3(3, 1, 2)//vertices[3],1,2... + ); + geometry.computeBoundingBox(); + + var max = geometry.boundingBox.max, + min = geometry.boundingBox.min; + var offset = new THREE.Vector2(0 - min.x, 0 - min.y); + var range = new THREE.Vector2(max.x - min.x, max.y - min.y); + var faces = geometry.faces; + + geometry.faceVertexUvs[0] = []; + + for (var i = 0; i < faces.length; i++) { + + var v1 = geometry.vertices[faces[i].a], + v2 = geometry.vertices[faces[i].b], + v3 = geometry.vertices[faces[i].c]; + + geometry.faceVertexUvs[0].push([ + new THREE.Vector2((v1.x + offset.x) / range.x, (v1.y + offset.y) / range.y), + new THREE.Vector2((v2.x + offset.x) / range.x, (v2.y + offset.y) / range.y), + new THREE.Vector2((v3.x + offset.x) / range.x, (v3.y + offset.y) / range.y) + ]); + } + geometry.uvsNeedUpdate = true; + geometry.dynamic = true; + + var material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide }); + material.nowireframe = true; + if (textureURL != undefined) { + var loader = new THREE.TextureLoader(); + // load a resource + loader.load( + // resource URL + textureURL, + // Function when resource is loaded + function (texture) { + //texture.minFilter = THREE.LinearFilter; + material.map = texture; + texture.flipY = false; + material.opacity = 0.3; + material.transparent = true; + material.needsUpdate = true; + + }, + // Function called when download progresses + function (xhr) { + console.log((xhr.loaded / xhr.total * 100) + '% loaded'); + }, + // Function called when download errors + function (xhr) { + console.log('An error happened'); + } + ); + + } + else { + material.opacity = 0.3; + material.transparent = true; + material.color.setHex("0xb0b0b0"); + } + + var mesh = new THREE.Mesh(geometry, material); + mesh.renderOrder = 1; + mesh.clickThrough = true; + this.scene.add(mesh); + return mesh; + }, + + /** + * Modify the coordinates (4) points of an existing plane. + * @returns {THREE.Mesh} + */ + modify3DPlane: function (object, x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) { + object.geometry.vertices[0].set(x1, y1, z1); + object.geometry.vertices[1].set(x2, y2, z2); + object.geometry.vertices[2].set(x3, y3, z3); + object.geometry.vertices[3].set(x4, y4, z4); + object.geometry.verticesNeedUpdate = true; + return object; + }, + + + /** + * + * @param sphereNode + * @param material + * @returns {THREE.Mesh|*} + */ + create3DSphereFromNode: function (sphereNode, material) { + + var sphere = new THREE.SphereGeometry(sphereNode.radius, 20, 20); + // sphere.applyMatrix(new THREE.Matrix4().makeScale(-1,1,1)); + var threeObject = new THREE.Mesh(sphere, material); + threeObject.position.set(sphereNode.position.x, sphereNode.position.y, sphereNode.position.z); + + threeObject.geometry.verticesNeedUpdate = true; + return threeObject; + }, + + /** + * + * @param thickness + * @returns {THREE.LineBasicMaterial} + */ + getLineMaterial: function (thickness, color) { + var options = {}; + if (thickness) { + options.linewidth = thickness; + } + if (color == undefined) { + color = GEPPETTO.Resources.COLORS.DEFAULT; + } + var material = new THREE.LineBasicMaterial(options); + material.color.setHex(color); + material.defaultColor = color; + material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + return material; + }, + + /** + * + * @param color + * @returns {THREE.MeshPhongMaterial} + */ + getMeshPhongMaterial: function (color) { + if (color == undefined) { + color = GEPPETTO.Resources.COLORS.DEFAULT; + } + var material = new THREE.MeshPhongMaterial( + { + opacity: 1, + shininess: 10, + shading: THREE.SmoothShading + }); + + material.color.setHex(color); + material.defaultColor = color; + material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + return material; + }, + + /** + * + * @returns {THREE.PointsMaterial} + */ + getParticleMaterial: function () { + var textureLoader = new THREE.TextureLoader(); + var pMaterial = new THREE.PointsMaterial( + { + size: 5, + map: textureLoader.load("geppetto/images/particle.png"), + blending: THREE.AdditiveBlending, + depthTest: false, + transparent: true + }); + pMaterial.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); + pMaterial.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; + pMaterial.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + pMaterial.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; + return pMaterial; + }, + + /** + * + * @param threeColor + * @param color + */ + setThreeColor: function (threeColor, color) { + if (color.indexOf && color.indexOf("rgb") == -1) { + threeColor.setHex(color); + + } else if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { + threeColor.r = color.r; + threeColor.g = color.g; + threeColor.b = color.b; + } else { + threeColor.set(color); + } + }, + + /** + * + * @param zoomParameters + */ + zoomToParameters: function (zoomParameters) { + // Compute world AABB center + this.sceneCenter.x = (zoomParameters.aabbMax.x + zoomParameters.aabbMin.x) * 0.5; + this.sceneCenter.y = (zoomParameters.aabbMax.y + zoomParameters.aabbMin.y) * 0.5; + this.sceneCenter.z = (zoomParameters.aabbMax.z + zoomParameters.aabbMin.z) * 0.5; + + this.updateCamera(zoomParameters.aabbMax, zoomParameters.aabbMin); + }, + + /** + * + * @param mesh + * @param zoomParameters + * @returns {*} + */ + addMeshToZoomParameters: function (mesh, zoomParameters) { + mesh.geometry.computeBoundingBox(); + aabbMin = mesh.geometry.boundingBox.min; + aabbMax = mesh.geometry.boundingBox.max; + + bb = mesh.geometry.boundingBox; + bb.translate(mesh.localToWorld(new THREE.Vector3())); + + // If min and max vectors are null, first values become default min and max + if (zoomParameters.aabbMin == undefined && zoomParameters.aabbMax == undefined) { + zoomParameters.aabbMin = bb.min; + zoomParameters.aabbMax = bb.max; + } else { + // Compare other meshes, particles BB's to find min and max + zoomParameters.aabbMin.x = Math.min(zoomParameters.aabbMin.x, bb.min.x); + zoomParameters.aabbMin.y = Math.min(zoomParameters.aabbMin.y, bb.min.y); + zoomParameters.aabbMin.z = Math.min(zoomParameters.aabbMin.z, bb.min.z); + zoomParameters.aabbMax.x = Math.max(zoomParameters.aabbMax.x, bb.max.x); + zoomParameters.aabbMax.y = Math.max(zoomParameters.aabbMax.y, bb.max.y); + zoomParameters.aabbMax.z = Math.max(zoomParameters.aabbMax.z, bb.max.z); + } + + return zoomParameters; + }, + + + /** + * Remove given entity from scene + * + * @param entity + */ + removeFromScene: function (entity) { + var path = entity.getPath(); + var mergedMesh = this.meshes[path]; + if (mergedMesh) { + this.scene.remove(mergedMesh); + delete this.meshes[path]; + } + var splitMesh = this.splitMeshes[path]; + if (splitMesh) { + if (path == splitMesh.instancePath) { + this.scene.remove(splitMesh); + } + delete this.splitMeshes[path]; + } + }, + + + /** + * Status of scene, populated or not + * + * @returns {Boolean} True or false depending whether scene is populated + * or not + */ + isScenePopulated: function () { + return !(_.isEmpty(this.visualModelMap)); + }, + + /** + * Has canvas been created? + * + * @returns {Boolean] True or false if canvas has been created or not + */ + isCanvasCreated: function () { + return this.canvasCreated; + }, + + /** + * @param x + * @param y + */ + incrementCameraPan: function (x, y) { + this.controls.incrementPanEnd(x, y); + }, + + /** + * @param x + * @param y + * @param z + */ + incrementCameraRotate: function (x, y, z) { + this.controls.incrementRotationEnd(x, y, z); + }, + + /** + * @param z + */ + incrementCameraZoom: function (z) { + this.controls.incrementZoomEnd(z); + }, + + /** + * @param x + * @param y + * @param z + */ + setCameraPosition: function (x, y, z) { + this.controls.setPosition(x, y, z); + }, + + /** + * @param rx + * @param ry + * @param rz + * @param radius + */ + setCameraRotation: function (rx, ry, rz, radius) { + this.controls.setRotation(rx, ry, rz, radius); + }, + + /** + * Rotate the camera around the selection + * + */ + autoRotate: function () { + var that = this; + if (this.rotate == null) { + this.movieMode(true); + this.rotate = setInterval(function () { that.incrementCameraRotate(0.01, 0) }, 100); + } + else { + this.movieMode(false); + clearInterval(this.rotate); + this.rotate = null; + } + }, + + animate: function () { + var that = this; + that.debugUpdate = that.needsUpdate; + // so that we log only the cycles when we are updating the scene + + that.controls.update(); + + that.isAnimated = true; + + requestAnimationFrame(function () { + that.animate(); + }); + that.renderCanvas(); + + if (that.stats) { + that.stats.update(); + } + + if (that.debugUpdate) { + GEPPETTO.log(GEPPETTO.Resources.UPDATE_FRAME_END); + } + }, + + + /** + * Get Meshes associated to an instance + * + * @param {String} + * instancePath - Path of the instance + */ + getRealMeshesForInstancePath: function (instancePath) { + var meshes = []; + if (instancePath in this.splitMeshes) { + for (var keySplitMeshes in this.splitMeshes) { + if (keySplitMeshes.startsWith(instancePath)) { + meshes.push(this.splitMeshes[keySplitMeshes]); + } + + } + } + else { + if (instancePath in this.meshes) { + meshes.push(this.meshes[instancePath]); + } + } + return meshes; + }, + + /** + * Selects an aspect given the path of it. Color changes to yellow, and opacity become 100%. + * + * @param {String} + * instancePath - Path of aspect of mesh to select + */ + selectInstance: function (instancePath, geometryIdentifier) { + if (geometryIdentifier != undefined && geometryIdentifier != "") { + instancePath = instancePath + "." + geometryIdentifier; + } + var meshes = this.getRealMeshesForInstancePath(instancePath); + if (meshes.length > 0) { + for (var meshesIndex in meshes) { + var mesh = meshes[meshesIndex]; + + if (!mesh.visible) { + this.merge(instancePath, true); + } + if (mesh.selected == false) { + if (mesh instanceof THREE.Object3D) { + mesh.traverse(function (child) { + if (child.hasOwnProperty("material")) { + this.threeEngine.setThreeColor(child.material.color, GEPPETTO.Resources.COLORS.SELECTED); + child.material.opacity = Math.max(0.5, child.material.defaultOpacity); + child.geometry.computeBoundingBox(); + this.controls.target.copy(child.position); + this.controls.target.add(child.geometry.boundingBox.getCenter()); + } + }); + } else { + this.threeEngine.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.SELECTED); + mesh.material.opacity = Math.max(0.5, mesh.material.defaultOpacity); + mesh.geometry.computeBoundingBox(); + //let's set the center of rotation to the selected mesh + this.controls.target.copy(mesh.position); + this.controls.target.add(mesh.geometry.boundingBox.getCenter()); + } + mesh.selected = true; + mesh.ghosted = false; + + + this.camera.updateProjectionMatrix(); + + } + if (GEPPETTO.isKeyPressed('z')) { + this.zoomTo([eval(instancePath)]); + } + + } + + return true; + } + return false; + }, + + + /** + * Deselect aspect, or mesh as far as tree js is concerned. + * + * @param {String} + * instancePath - Path of the mesh/aspect to select + */ + deselectInstance: function (instancePath) { + var meshes = this.getRealMeshesForInstancePath(instancePath); + if (meshes.length > 0) { + for (var meshesIndex in meshes) { + var mesh = meshes[meshesIndex]; + // match instancePath to mesh store in variables properties + if (!mesh.visible) { + this.merge(instancePath, false); + } + // make sure that path was selected in the first place + if (mesh.selected == true) { + var that = this; + if (mesh instanceof THREE.Object3D) { + mesh.traverse(function (child) { + if (child.hasOwnProperty("material")) { + that.setThreeColor(child.material.color, child.material.defaultColor); + child.material.opacity = child.material.defaultOpacity; + } + }); + mesh.selected = false; + } + } else { + this.setThreeColor(mesh.material.color, mesh.material.defaultColor); + mesh.material.opacity = mesh.material.defaultOpacity; + mesh.selected = false; + } + } + return true; + } + return false; + }, + + /** + * Show aspect, make it visible. + * + * @param {String} + * instancePath - Instance path of aspect to make visible + */ + showInstance: function (instancePath) { + var meshes = this.getRealMeshesForInstancePath(instancePath); + if (meshes.length > 0) { + for (var i = 0; i < meshes.length; i++) { + var mesh = meshes[i]; + if (mesh) { + mesh.traverse(function (object) { + object.visible = true; + }); + } + } + } + }, + + /** + * Hide instance + * + * @param {String} + * instancePath - Path of the aspect to make invisible + */ + hideInstance: function (instancePath) { + var meshes = this.getRealMeshesForInstancePath(instancePath); + for (var i = 0; i < meshes.length; i++) { + var mesh = meshes[i]; + if (mesh) { + mesh.traverse(function (object) { + object.visible = false; + }); + } + } + }, + + /** + * Change the color of a given aspect + * + * @param {String} + * instancePath - Instance path of aspect to change color + */ + setColor: function (instancePath, color) { + var meshes = this.getRealMeshesForInstancePath(instancePath); + if (meshes.length > 0) { + for (var i = 0; i < meshes.length; i++) { + var mesh = meshes[i]; + if (mesh) { + var that = this; + mesh.traverse(function (object) { + if (object.hasOwnProperty("material")) { + that.setThreeColor(object.material.color, color); + object.material.defaultColor = color; + } + }); + } + } + } + }, + + /** + * Assign random color to instance if leaf - if not leaf assign random colr to all leaf children recursively + * @param instance + */ + assignRandomColor: function (instance) { + var getRandomColor = function () { + var letters = '0123456789ABCDEF'; + var color = '0x'; + for (var i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; + }; + + var meshes = this.getRealMeshesForInstancePath(instance.getInstancePath()); + if (meshes.length > 0) { + for (var i = 0; i < meshes.length; i++) { + var mesh = meshes[i]; + if (mesh) { + var randomColor = getRandomColor(); + mesh.traverse(function (object) { + if (object.hasOwnProperty("material")) { + this.setThreeColor(object.material.color, randomColor); + object.material.defaultColor = randomColor; + } + }); + } + } + } + + + }, + + /** + * Change the default opacity for a given aspect. The opacity set with this command API will be persisted across different workflows, e.g. selection. + * + * @param {String} + * instancePath - Instance path of aspect to change opacity for + */ + setOpacity: function (instancePath, opacity) { + var mesh = this.meshes[instancePath]; + if (mesh != undefined) { + mesh.defaultOpacity = opacity; + if (opacity == 1) { + mesh.traverse(function (object) { + if (object.hasOwnProperty("material")) { + object.material.transparent = false; + object.material.opacity = 1; + object.material.defaultOpacity = 1; + } + }); + } else { + mesh.traverse(function (object) { + if (object.hasOwnProperty("material")) { + object.material.transparent = true; + object.material.opacity = opacity; + object.material.defaultOpacity = opacity; + } + }); + } + + return true; + } + return false; + }, + + /** + * Set the threshold (number of 3D primitives on the scene) above which we switch the visualization to lines + * for teh CompositeVisualTypes + * @param threshold + */ + setLinesThreshold: function (threshold) { + this.linesThreshold = threshold; + }, + + /** + * Change the type of geometry used to visualize the instance + * + * @param {String} + * instance - The instance to change the geometry type for + * @param {String} + * type - The geometry type, see GEPPETTO.Resources.GeometryTypes + * @param {String} + * thickness - Optional: the thickness to be used if the geometry is "lines" + */ + setGeometryType: function (instance, type, thickness) { + var lines = false; + if (type === GEPPETTO.Resources.GeometryTypes.LINES) { + lines = true; + } else if (type === GEPPETTO.Resources.GeometryTypes.TUBES) { + lines = false + } else if (type === GEPPETTO.Resources.GeometryTypes.CYLINDERS) { + lines = false + } else { + return false; + } + + this.traverseInstances([instance], lines, thickness); + + return true; + }, + + /** + * Set the type of geometry used to visualize all the instances in the scene + * @param type - The geometry type either "lines", "tubes" or "cylinders" + */ + setAllGeometriesType: function (type) { + var visualInstances = GEPPETTO.ModelFactory.getAllInstancesWithCapability(GEPPETTO.Resources.VISUAL_CAPABILITY, window.Instances); + for (var i = 0; i < visualInstances.length; i++) { + if (this.meshes[visualInstances[i].getInstancePath()]) { + var visualType = visualInstances[i].getVisualType(); + if (visualType) { + if (visualType.getWrappedObj().eClass == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { + this.setGeometryType(visualInstances[i], type); + } + } + } + } + }, + + + /** + * + * @param instance + */ + zoomToInstance: function (instance) { + this.controls.reset(); + + var zoomParameters = {}; + var mesh = this.meshes[instance.getInstancePath()]; + mesh.traverse(function (object) { + if (object.hasOwnProperty("geometry")) { + this.addMeshToZoomParameters(object, zoomParameters); + } + }); + + this.zoomToParameters(zoomParameters); + + }, + + /** + * Change color for meshes that are connected to other meshes. Color depends on whether that instance is an output, input or both + * + * @param {Instance} + * instance - The instance for which we want to show the connections + * @param {String} + * type - Type of connection, input or output (See GEPPETTO.Resources.INPUT/OUTPUT) + */ + highlightConnectedInstances: function (instance, type) { + + var inputs = {}; + var outputs = {}; + + var connections = instance.getConnections(type); + + + for (var c = 0; c < connections.length; c++) { + var connection = connections[c]; + + var otherEndPath = connection.getA().getPath() == instance.getInstancePath() ? + connection.getB().getPath() : + connection.getA().getPath(); + + var connectionType = connection.getA().getPath() == instance.getInstancePath() ? + GEPPETTO.Resources.OUTPUT : + GEPPETTO.Resources.INPUT; + + + // determine whether connection is input or output + if (connectionType == GEPPETTO.Resources.INPUT) { + //I want to change the colour the instances that are an input to the instance passed as a parameter + var mesh = this.meshes[connection.getA().getPath()]; //this is the instance input to the current one + if (outputs[otherEndPath]) { + this.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.INPUT_AND_OUTPUT); + } + else { + this.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.INPUT_TO_SELECTED); + } + inputs[otherEndPath] = connection.getInstancePath(); + } else if (connectionType == GEPPETTO.Resources.OUTPUT) { + //I want to change the colour the instances that are an output of the instance passed as a parameter + var mesh = this.meshes[connection.getB().getPath()]; //this is the instance output of the current on + if (inputs[otherEndPath]) { + this.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.INPUT_AND_OUTPUT); + } + else { + this.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.OUTPUT_TO_SELECTED); + } + outputs[otherEndPath] = connection.getInstancePath(); + } + } + }, + + /** + * Restore the original colour of the connected instances + * + * @param {Instance} + * instance - A connected instance + */ + restoreConnectedInstancesColour: function (instance) { + + var connections = instance.getConnections(); + + for (var c = 0; c < connections.length; c++) { + var connection = connections[c]; + + var mesh = connection.getA().getPath() == instance.getInstancePath() ? + this.meshes[connection.getB().getPath()] : + this.meshes[connection.getA().getPath()]; + + // if mesh is not selected, give it ghost or default color and opacity + if (!mesh.selected) { + // if there are nodes still selected, give it a ghost effect. If not nodes are + // selected, give the meshes old default color + if (G.getSelectionOptions().unselected_transparent) { + mesh.material.transparent = true; + mesh.material.opacity = GEPPETTO.Resources.OPACITY.GHOST; + mesh.ghosted = true; + } + this.setThreeColor(mesh.material.color, mesh.material.defaultColor); + + } + // if mesh is selected, make it look like so + else { + this.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.SELECTED); + mesh.material.transparent = true; + mesh.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; + } + } + }, + + /** + * + * @param instance + */ + showConnectionLines: function (instance) { + var connections = instance.getConnections(); + + var mesh = this.meshes[instance.getInstancePath()]; + var inputs = {}; + var outputs = {}; + var defaultOrigin = mesh.position.clone(); + + for (var c = 0; c < connections.length; c++) { + + var connection = connections[c]; + var type = connection.getA().getPath() == instance.getInstancePath() ? + GEPPETTO.Resources.OUTPUT : + GEPPETTO.Resources.INPUT; + + var otherEndPath = connection.getA().getPath() == instance.getInstancePath() ? + connection.getB().getPath() : + connection.getA().getPath(); + + + var otherEndMesh = this.meshes[otherEndPath]; + + var destination; + var origin; + + if (connection.getA().getPoint() == undefined) { + //same as before + origin = defaultOrigin; + } + else { + //the specified coordinate + var p = connection.getA().getPoint(); + if (type == GEPPETTO.Resources.OUTPUT) { + origin = new THREE.Vector3(p.x + mesh.position.x, p.y + mesh.position.y, p.z + mesh.position.z); + } + else if (type == GEPPETTO.Resources.INPUT) { + origin = new THREE.Vector3(p.x + otherEndMesh.position.x, p.y + otherEndMesh.position.y, p.z + otherEndMesh.position.z); + } + } + + if (connection.getB().getPoint() == undefined) { + //same as before + destination = otherEndMesh.position.clone();; + } + else { + //the specified coordinate + var p = connection.getB().getPoint(); + if (type == GEPPETTO.Resources.OUTPUT) { + destination = new THREE.Vector3(p.x + otherEndMesh.position.x, p.y + otherEndMesh.position.y, p.z + otherEndMesh.position.z); + } + else if (type == GEPPETTO.Resources.INPUT) { + destination = new THREE.Vector3(p.x + mesh.position.x, p.y + mesh.position.y, p.z + mesh.position.z); + } + } + + var geometry = new THREE.Geometry(); + + geometry.vertices.push(origin, destination); + geometry.verticesNeedUpdate = true; + geometry.dynamic = true; + + var colour = null; + + + if (type == GEPPETTO.Resources.INPUT) { + + colour = GEPPETTO.Resources.COLORS.INPUT_TO_SELECTED; + + // figure out if connection is both, input and output + if (outputs[otherEndPath]) { + colour = GEPPETTO.Resources.COLORS.INPUT_AND_OUTPUT; + } + + if (inputs[otherEndPath]) { + inputs[otherEndPath].push(connection.getInstancePath()); + } + else { + inputs[otherEndPath] = []; + inputs[otherEndPath].push(connection.getInstancePath()); + } + } + + else if (type == GEPPETTO.Resources.OUTPUT) { + + colour = GEPPETTO.Resources.COLORS.OUTPUT_TO_SELECTED; + // figure out if connection is both, input and output + if (inputs[otherEndPath]) { + colour = GEPPETTO.Resources.COLORS.INPUT_AND_OUTPUT; + } + + if (outputs[otherEndPath]) { + outputs[otherEndPath].push(connection.getInstancePath()); + } + else { + outputs[otherEndPath] = []; + outputs[otherEndPath].push(connection.getInstancePath()); + } + } + + var material = new THREE.LineDashedMaterial({ dashSize: 3, gapSize: 1 }); + material.color.setHex(colour); + + var line = new THREE.LineSegments(geometry, material); + line.updateMatrixWorld(true); + + + if (this.connectionLines[connection.getInstancePath()]) { + this.scene.remove(this.connectionLines[connection.getInstancePath()]); + } + + this.scene.add(line); + this.connectionLines[connection.getInstancePath()] = line; + } + }, + + /** + * Removes connection lines, all if nothing is passed in or just the ones passed in. + * + * @param instance - optional, instance for which we want to remove the connections + */ + removeConnectionLines: function (instance) { + if (instance != undefined) { + var connections = instance.getConnections(); + // get connections for given instance and remove only those + var lines = this.connectionLines; + for (var i = 0; i < connections.length; i++) { + if (lines.hasOwnProperty(connections[i].getInstancePath())) { + // remove the connection line from the scene + this.scene.remove(lines[connections[i].getInstancePath()]); + // remove the conneciton line from the GEPPETTO list of connection lines + delete lines[connections[i].getInstancePath()]; + } + } + } else { + // remove all connection lines + var lines = this.connectionLines; + for (var key in lines) { + if (lines.hasOwnProperty(key)) { + this.scene.remove(lines[key]); + } + } + this.connectionLines = []; + } + }, + + splitHighlightedMesh: function (targetObjects, aspects) { + var groups = {}; + for (a in aspects) { + // create object to hold geometries used for merging objects + // in groups + var geometryGroups = {}; + + var mergedMesh = this.meshes[a]; + + /* + * reset the aspect instance path group mesh, this is used to group /*visual objects that don't belong to any of the groups passed as parameter + */ + this.splitMeshes[a] = null; + geometryGroups[a] = new THREE.Geometry(); + var highlightedMesh = a + ".highlighted"; + this.splitMeshes[highlightedMesh] = null; + geometryGroups[highlightedMesh] = new THREE.Geometry(); + + // get map of all meshes that merged mesh was merging + var map = mergedMesh.mergedMeshesPaths; + + // loop through individual meshes, add them to group, set + // new material to them + for (v in map) { + var m = this.visualModelMap[map[v]]; + if (m.instancePath in targetObjects) { + // merged mesh into corresponding geometry + var geometry = geometryGroups[highlightedMesh]; + geometry.merge(m.geometry, m.matrix); + } else { + // merged mesh into corresponding geometry + var geometry = geometryGroups[a]; + geometry.merge(m.geometry, m.matrix); + } + } + + groups[a] = {}; + groups[a].color = mergedMesh.material.color; + groups[highlightedMesh] = {}; + var newGroups = {}; + newGroups[a] = {}; + newGroups[highlightedMesh] = {}; + this.createGroupMeshes(a, geometryGroups, newGroups); + } + return groups; + }, + + /** + * Highlight part of a mesh + * + * @param {String} + * path - Path of mesh to highlight + * @param {boolean} + * mode - Highlight or unhighlight + */ + highlight: function (targetObjects, aspects, mode) { + var splitHighlightedGroups = this.splitHighlightedMesh(targetObjects, aspects); + + for (groupName in splitHighlightedGroups) { + // get group mesh + var groupMesh = this.splitMeshes[groupName]; + + if (!(groupName in aspects)) { + if (mode) { + this.setThreeColor(groupMesh.material.color, GEPPETTO.Resources.COLORS.HIGHLIGHTED); + groupMesh.highlighted = true; + } else { + this.setThreeColor(groupMesh.material.color, groupMesh.material.defaultColor); + groupMesh.highlighted = false; + } + } else { + this.setThreeColor(groupMesh.material.color, splitHighlightedGroups[groupName].color.getHex()); + } + } + }, + + /** + * Split merged mesh into individual meshes + * + * @param {String} + * instancePath - Path of aspect, corresponds to original merged mesh + * @param {AspectSubTreeNode} + * visualizationTree - Aspect Visualization Tree with groups info for visual objects + * @param {object} + * groups - The groups that we need to split mesh into + */ + splitGroups: function (instance, groupElements) { + + var instancePath = instance.getInstancePath(); + + // retrieve the merged mesh + var mergedMesh = this.meshes[instancePath]; + // create object to hold geometries used for merging objects in + // groups + var geometryGroups = {}; + + /* + * reset the aspect instance path group mesh, this is used to group visual objects that don't belong to any of the groups passed as parameter + */ + this.splitMeshes[instancePath] = null; + geometryGroups[instancePath] = new THREE.Geometry(); + + // create map of geometry groups for groups + for (var groupElement in groupElements) { + var groupName = instancePath + "." + groupElement; + + var geometry = new THREE.Geometry(); + geometry.groupMerge = true; + + geometryGroups[groupName] = geometry; + } + + // get map of all meshes that merged mesh was merging + var map = mergedMesh.mergedMeshesPaths; + + // flag for keep track what visual objects were added to group + // meshes already + var added = false; + // loop through individual meshes, add them to group, set new + // material to them + + for (var v in map) { + if (v != undefined) { + var m = this.visualModelMap[map[v]]; + + eval(map[v].substring(0, map[v].lastIndexOf("."))); + var object = instance.getVisualType()[map[v].replace(instancePath + ".", "")]; + + // If it is a segment compare to the id otherwise check in the visual groups + if (object.getId() in groupElements) { + // true means don't add to mesh with non-groups visual objects + added = this.addMeshToGeometryGroup(instance, object.getId(), geometryGroups, m) + } else { + // get group elements list for object + var groupElementsReference = object.getInitialValue().value.groupElements; + for (var i = 0; i < groupElementsReference.length; i++) { + var objectGroup = GEPPETTO.ModelFactory.resolve(groupElementsReference[i].$ref).getId(); + if (objectGroup in groupElements) { + // true means don't add to mesh with non-groups visual objects + added = this.addMeshToGeometryGroup(instance, objectGroup, geometryGroups, m) + } + } + } + + // if visual object didn't belong to group, add it to mesh + // with remainder of them + if (!added) { + var geometry = geometryGroups[instancePath]; + if (m instanceof THREE.Line) { + geometry.vertices.push(m.geometry.vertices[0]); + geometry.vertices.push(m.geometry.vertices[1]); + } else { + // merged mesh into corresponding geometry + geometry.merge(m.geometry, m.matrix); + } + } + // reset flag for next visual object + added = false; + } + } + + groupElements[instancePath] = {}; + groupElements[instancePath].color = GEPPETTO.Resources.COLORS.SPLIT; + this.createGroupMeshes(instancePath, geometryGroups, groupElements); + }, + + /** + * Add mesh to geometry groups + * + * @param {String} + * instancePath - Path of aspect, corresponds to original merged mesh + * @param {String} + * id - local path to the group + * @param {object} + * groups - The groups that we need to split mesh into + * @param {object} + * m - current mesh + */ + addMeshToGeometryGroup: function (instance, id, geometryGroups, m) { + // name of group, mix of aspect path and group name + var groupName = instance.getInstancePath() + "." + id; + // retrieve corresponding geometry for this group + var geometry = geometryGroups[groupName]; + // only merge if flag is set to true + if (m instanceof THREE.Line) { + geometry.vertices.push(m.geometry.vertices[0]); + geometry.vertices.push(m.geometry.vertices[1]); + } else { + // merged mesh into corresponding geometry + geometry.merge(m.geometry, m.matrix); + } + return true; + }, + + /** + * Create group meshes for given groups, retrieves from map if already present + */ + createGroupMeshes: function (instancePath, geometryGroups, groups) { + var mergedMesh = this.meshes[instancePath]; + // switch visible flag to false for merged mesh and remove from scene + mergedMesh.visible = false; + this.scene.remove(mergedMesh); + + for (g in groups) { + var groupName = g; + if (groupName.indexOf(instancePath) <= -1) { + groupName = instancePath + "." + g; + } + + var groupMesh = this.splitMeshes[groupName]; + var geometryGroup = geometryGroups[groupName]; + + if (mergedMesh instanceof THREE.Line) { + var material = this.getLineMaterial(); + groupMesh = new THREE.LineSegments(geometryGroup, material); + } else { + var material = this.getMeshPhongMaterial(); + groupMesh = new THREE.Mesh(geometryGroup, material); + } + groupMesh.instancePath = instancePath; + groupMesh.geometryIdentifier = g; + groupMesh.geometry.dynamic = false; + groupMesh.position.copy(mergedMesh.position); + + + this.splitMeshes[groupName] = groupMesh; + + // Update visualization feature for a mesh + if (mergedMesh.ghosted) { + this.ghostEffect([groupMesh], true); + } + if (mergedMesh.selected) { + this.selectInstance(groupName); + } + groupMesh.selected = mergedMesh.selected; + + // add split mesh to scenne and set flag to visible + groupMesh.visible = true; + this.scene.add(groupMesh); + } + }, + + /** + * Merge mesh that was split before + * + * @param {String} + * aspectPath - Path to aspect that points to mesh + */ + merge: function (aspectPath, visible) { + // get mesh from map + var mergedMesh = this.meshes[aspectPath]; + + // if merged mesh is not visible, turn it on and turn split one + // off + if (!mergedMesh.visible) { + for (path in this.splitMeshes) { + // retrieve split mesh that is on the scene + var splitMesh = this.splitMeshes[path]; + if (splitMesh) { + if (aspectPath == splitMesh.instancePath) { + splitMesh.visible = false; + // remove split mesh from scene + this.scene.remove(splitMesh); + } + } + } + if (visible) { + // add merged mesh to scene and set flag to true + mergedMesh.visible = true; + this.scene.add(mergedMesh); + } + } + }, + + showVisualGroupsRaw: function (visualGroups, instance, meshesContainer) { + var instancePath = instance.getInstancePath(); + for (g in visualGroups) { + // retrieve visual group object + var visualGroup = visualGroups[g]; + + // get full group name to access group mesh + var groupName = g; + if (groupName.indexOf(instancePath) <= -1) { + groupName = instancePath + "." + g; + } + + // get group mesh + var groupMesh = meshesContainer[groupName]; + groupMesh.visible = true; + this.setThreeColor(groupMesh.material.color, visualGroup.color); + } + }, + + /** + * Shows a visual group + */ + showVisualGroups: function (visualGroups, mode, instances) { + for (var i = 0; i < instances.length; i++) { + var instance = instances[i]; + var instancePath = instance.getInstancePath(); + this.merge(instancePath, true); + if (mode) { + var mergedMesh = this.meshes[instancePath]; + var map = mergedMesh.mergedMeshesPaths; + //no mergedMeshesPaths means object hasn't been merged, single object + if (map != undefined || null) { + this.splitGroups(instance, visualGroups); + this.showVisualGroupsRaw(visualGroups, instance, this.splitMeshes); + + } else { + this.showVisualGroupsRaw(visualGroups, instance, this.meshes); + } + + } + } + }, + + isVisible: function (variables) { + var visible = false; + for (var i = 0; i < variables.length; i++) { + if (variables[i].isVisible()) { + visible = true; + break; + } + } + return visible; + }, + + isSelected: function (variables) { + var selected = false; + for (var i = 0; i < variables.length; i++) { + if (variables[i].hasOwnProperty('isSelected') && variables[i].isSelected()) { + selected = true; + break; + } + } + return selected; + }, + + /** + * Reinitializes the camera with the Y axis flipped + */ + flipCameraY: function () { + this.camera.up = new THREE.Vector3(0, -1, 0); + this.setupControls(); + this.resetCamera(); + }, + + flipCameraZ: function () { + this.camera.direction = new THREE.Vector3(0, 0, -1); + this.setupControls(); + this.resetCamera(); + }, + + movieMode: function (toggle) { + this.configureRenderer(toggle); + }, + + /** + * Resets the scene controller + */ + reset: function () { + this.complexity = 0; + this.aboveLinesThreshold = false; + } + + }; + + return ThreeDEngine; +}); + diff --git a/src/main/webapp/js/components/interface/3dCanvas/TrackballControls.js b/src/main/webapp/js/components/interface/3dCanvas/TrackballControls.js index fcfb0041a..3b21d78c9 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/TrackballControls.js +++ b/src/main/webapp/js/components/interface/3dCanvas/TrackballControls.js @@ -5,7 +5,9 @@ * @author Luca Antiga / http://lantiga.github.io */ var THREE = window.THREE || require('three'); -THREE.TrackballControls = function ( object, domElement ) { +THREE.TrackballControls = function ( object, domElement, viewerId ) { + + this.viewerId = viewerId; var _this = this; var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; @@ -163,12 +165,12 @@ THREE.TrackballControls = function ( object, domElement ) { var p = _this.object.position.toArray(); - GEPPETTO.Console.executeImplicitCommand('G.setCameraPosition('+p[0].toFixed(places)+','+p[1].toFixed(places)+','+p[2].toFixed(places)+')'); + GEPPETTO.Console.executeImplicitCommand(_this.viewerId+'.setCameraPosition('+p[0].toFixed(places)+','+p[1].toFixed(places)+','+p[2].toFixed(places)+')'); var u = _this.object.rotation.toArray(); var l = _eye.length(); - GEPPETTO.Console.executeImplicitCommand('G.setCameraRotation('+u[0].toFixed(places)+','+u[1].toFixed(places)+','+u[2].toFixed(places)+','+l.toFixed(places)+')'); + GEPPETTO.Console.executeImplicitCommand(_this.viewerId+'.setCameraRotation('+u[0].toFixed(places)+','+u[1].toFixed(places)+','+u[2].toFixed(places)+','+l.toFixed(places)+')'); _this.cameraByConsoleLock = true; _this.cameraChanged = false; diff --git a/src/main/webapp/js/geppettoModel/ModelFactory.js b/src/main/webapp/js/geppettoModel/ModelFactory.js index 7873fae97..64eab8612 100644 --- a/src/main/webapp/js/geppettoModel/ModelFactory.js +++ b/src/main/webapp/js/geppettoModel/ModelFactory.js @@ -442,8 +442,6 @@ define(function (require) { * Creates and populates initial instance tree skeleton with any instance that needs to be visualized */ createInstances: function (geppettoModel) { - // reset scene complexity index - GEPPETTO.SceneController.reset(); var instances = []; @@ -911,10 +909,7 @@ define(function (require) { instances[j].setVisualGroups(visualType.getVisualGroups()); } - // increase scene complexity counter - if (visualType.getMetaType() == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { - GEPPETTO.SceneController.complexity += visualType.getVariables().length; - } + } } @@ -1293,11 +1288,6 @@ define(function (require) { explodedInstance.extendApi(AVisualGroupCapability); explodedInstance.setVisualGroups(visualType.getVisualGroups()); } - - // increase scene complexity counter - if (visualType.getMetaType() == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { - GEPPETTO.SceneController.complexity += visualType.getVariables().length; - } } // check if it has connections and inject AConnectionCapability @@ -1371,10 +1361,6 @@ define(function (require) { newlyCreatedInstance.setVisualGroups(visualType.getVisualGroups()); } - // increase scene complexity counter - if (visualType.getMetaType() == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { - GEPPETTO.SceneController.complexity += visualType.getVariables().length; - } } // check if it has connections and inject AConnectionCapability diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index a4096fdab..32c2cbdcf 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -27,6 +27,9 @@ define(function (require) { maxSteps: 0, externalExperiments: {}, state:ExperimentStateEnum.STOPPED, + step : 0, + playTimerStep: 5, // timer step in milliseconds + playLoop: false, isPlayExperimentReady: function(){ return this.playExperimentReady; diff --git a/src/main/webapp/js/pages/geppetto/G.js b/src/main/webapp/js/pages/geppetto/G.js index 965d37237..260888398 100644 --- a/src/main/webapp/js/pages/geppetto/G.js +++ b/src/main/webapp/js/pages/geppetto/G.js @@ -27,33 +27,27 @@ define(function (require) { unselected_transparent: true }, litUpInstances: [], - + //TODO Design something better to hold abritrary status - timeWidget : {}, - timeWidgetVisible : false, - recordedVariablesWidget : {}, - recordedVariablesPlot : false, - enableColorPlottingActive : false, + timeWidget: {}, + timeWidgetVisible: false, + recordedVariablesWidget: {}, + recordedVariablesPlot: false, + enableColorPlottingActive: false, brightnessFunctionSet: false, - consoleFocused : true, - - isConsoleFocused : function(){ - return this.consoleFocused; - }, - - autoFocusConsole : function(mode){ - this.consoleFocused = mode; + consoleFocused: true, + + isConsoleFocused: function () { + return this.consoleFocused; }, - isBrightnessFunctionSet: function() { - return this.brightnessFunctionSet; + autoFocusConsole: function (mode) { + this.consoleFocused = mode; }, - + addWidget: function (type) { var newWidget = GEPPETTO.ComponentFactory.addWidget(type); return newWidget; - // var newWidget = GEPPETTO.ComponentFactory.addWidget(type); - // return newWidget; }, /** @@ -142,16 +136,11 @@ define(function (require) { * */ debug: function (mode) { - debugMode = mode; - - if (mode) { - GEPPETTO.toggleStats(true); - return GEPPETTO.Resources.DEBUG_ON; - } - else { - GEPPETTO.toggleStats(false); - return GEPPETTO.Resources.DEBUG_OFF; + this.debugMode = mode; + if(!GEPPETTO.Console.getConsole().showImplicitCommands){ + GEPPETTO.Console.toggleImplicitCommands(); } + return mode?GEPPETTO.Resources.DEBUG_ON:GEPPETTO.Resources.DEBUG_OFF; }, /** @@ -212,7 +201,7 @@ define(function (require) { return GEPPETTO.Resources.RUNNING_SCRIPT; }, - + /** * Show or hide console using command * @@ -234,7 +223,7 @@ define(function (require) { return returnMessage; }, - + /** * Show or hide help window using command @@ -264,31 +253,20 @@ define(function (require) { } return returnMessage; }, - - toggleTutorial : function() { - var returnMessage; - var modalVisible = $('#tutorial').is(':visible'); - - if (modalVisible) { - GEPPETTO.trigger(GEPPETTO.Events.Hide_Tutorial); - returnMessage = GEPPETTO.Resources.HIDE_TUTORIAL; - } - else { - GEPPETTO.trigger(GEPPETTO.Events.Show_Tutorial); - returnMessage = GEPPETTO.Resources.SHOW_TUTORIAL; - } - return returnMessage; - }, - /** - * - * Waits certain amount of time before running next command. Must be - * used inside a script. - * - * @command G.wait(ms) - */ - wait: function () { - return GEPPETTO.Resources.INVALID_WAIT_USE; + toggleTutorial: function () { + var returnMessage; + var modalVisible = $('#tutorial').is(':visible'); + + if (modalVisible) { + GEPPETTO.trigger(GEPPETTO.Events.Hide_Tutorial); + returnMessage = GEPPETTO.Resources.HIDE_TUTORIAL; + } + else { + GEPPETTO.trigger(GEPPETTO.Events.Show_Tutorial); + returnMessage = GEPPETTO.Resources.SHOW_TUTORIAL; + } + return returnMessage; }, /** @@ -336,107 +314,6 @@ define(function (require) { return debugMode; }, - /** - * Resets Camera to initial position - same as after loading. - * - * @command - G.resetCamera() - */ - resetCamera: function () { - GEPPETTO.resetCamera(); - - return GEPPETTO.Resources.CAMERA_RESET; - }, - - - /** - * Increments camera rotation. - * - * @command - G.incrementCameraRotate() - * @param {Integer} x - x coordinate of rotate increment vector - * @param {Integer} y - y coordinate of rotate increment vector - * @param {Integer} z - z coordinate of rotate increment vector - */ - autoRotate: function () { - if(this.rotate==null){ - GEPPETTO.Init.movieMode(true); - this.rotate=setInterval(function(){G.incrementCameraRotate(0.01, 0)}, 100); - } - else{ - GEPPETTO.Init.movieMode(false); - clearInterval(this.rotate); - this.rotate=null; - } - }, - - /** - * Increments camera pan. - * - * @command - G.incrementCameraPan() - * @param {Integer} x - x coordinate of pan increment vector - * @param {Integer} y - y coordinate of pan increment vector - */ - incrementCameraPan: function (x, y) { - GEPPETTO.incrementCameraPan(x, y); - - return GEPPETTO.Resources.CAMERA_PAN_INCREMENT; - }, - - /** - * Increments camera rotation. - * - * @command - G.incrementCameraRotate() - * @param {Integer} x - x coordinate of rotate increment vector - * @param {Integer} y - y coordinate of rotate increment vector - * @param {Integer} z - z coordinate of rotate increment vector - */ - incrementCameraRotate: function (x, y, z) { - GEPPETTO.incrementCameraRotate(x, y, z); - - return GEPPETTO.Resources.CAMERA_ROTATE_INCREMENT; - }, - - /** - * Increments camera zoom. - * - * @command - G.incrementCameraZoom() - * @param {Integer} z - z coordinate for zoom increment vector - */ - incrementCameraZoom: function (z) { - GEPPETTO.incrementCameraZoom(z); - - return GEPPETTO.Resources.CAMERA_ZOOM_INCREMENT; - }, - - /** - * Sets the camera position - * - * @command - G.setCameraPosition() - * @param {Integer} x - new x axis position for the camera - * @param {Integer} y - new y axis position for the camera - * @param {Integer} z - new z axis position for the camera - */ - setCameraPosition: function (x, y, z) { - GEPPETTO.setCameraPosition(x, y, z); - - return GEPPETTO.Resources.CAMERA_SET_POSITION; - }, - - /** - * Sets the camera rotation - * - * @command - G.setCameraRotation() - * @param {Integer} rx - x euler angle for the rotation - * @param {Integer} ry - y euler angle for the rotation - * @param {Integer} rz - z euler angle for the rotation - * @param {Integer} a - trackball's radius - */ - setCameraRotation: function (rx, ry, rz, a) { - GEPPETTO.setCameraRotation(rx, ry, rz, a); - - return GEPPETTO.Resources.CAMERA_SET_ROTATION; - }, - - /** * Callback to be called whenever a watched node changes * @@ -444,12 +321,12 @@ define(function (require) { * @param {Function} callback - Callback function to be called whenever _variable_ changes */ addOnNodeUpdatedCallback: function (node, callback) { - if(node !=null ||undefined){ - if (!this.listeners[node.getInstancePath()]) { - this.listeners[node.getInstancePath()] = []; - } - this.listeners[node.getInstancePath()].push(callback); - } + if (node != null || undefined) { + if (!this.listeners[node.getInstancePath()]) { + this.listeners[node.getInstancePath()] = []; + } + this.listeners[node.getInstancePath()].push(callback); + } }, /** @@ -483,20 +360,20 @@ define(function (require) { * @param {Function} colorfn - Converts time-series value to [r,g,b] */ addBrightnessFunction: function (instance, stateVariableInstances, colorfn) { - // Check if instance is instance + visualObjects or instance (hhcell.hhpop[0].soma or hhcell.hhpop[0]) - var newInstance = ""; - var visualObjects = []; - if (instance.getInstancePath() in GEPPETTO.getVARS().meshes){ - newInstance = instance; - } - else{ - newInstance = instance.getParent(); - visualObjects= [instance.getId()]; - } - - this.addBrightnessFunctionBulk(newInstance, visualObjects, [stateVariableInstances], colorfn); - }, - + // Check if instance is instance + visualObjects or instance (hhcell.hhpop[0].soma or hhcell.hhpop[0]) + var newInstance = ""; + var visualObjects = []; + if (instance.getInstancePath() in GEPPETTO.getVARS().meshes) { + newInstance = instance; + } + else { + newInstance = instance.getParent(); + visualObjects = [instance.getId()]; + } + + this.addBrightnessFunctionBulk(newInstance, visualObjects, [stateVariableInstances], colorfn); + }, + /** * Modulates the brightness of an aspect visualization, given a watched node * and a color function. The color function should receive @@ -507,52 +384,52 @@ define(function (require) { * @param {Function} colorfn - Converts time-series value to [r,g,b] */ addBrightnessFunctionBulkSimplified: function (instances, colorfn) { - // Check if instance is instance + visualObjects or instance (hhcell.hhpop[0].soma or hhcell.hhpop[0]) - for (var i=0; i 0){ - this.clearBrightnessFunctions(instances[0]); - } + while (instances.length > 0) { + this.clearBrightnessFunctions(instances[0]); + } // update flag if (this.litUpInstances.length == 0) { @@ -581,51 +458,51 @@ define(function (require) { * @param {Function} colorfn - Converts time-series value to [r,g,b] */ addBrightnessFunctionBulk: function (instance, visualObjects, stateVariableInstances, colorfn) { - var modulations = []; - if (visualObjects != null){ - if (visualObjects.length > 0 ){ - var elements = {}; - for (var voIndex in visualObjects){ - elements[visualObjects[voIndex]] = ""; - modulations.push(instance.getInstancePath() + "." + visualObjects[voIndex]); - - } - GEPPETTO.SceneController.splitGroups(instance, elements); - } - else{ - modulations.push(instance.getInstancePath()); - } - } - - var matchedMap = []; - //statevariableinstances come out of order, needs to sort into map to avoid nulls - for (var index in modulations){ - for(var i in stateVariableInstances){ - if(stateVariableInstances[i].getParent().getInstancePath()==modulations[index]){ - matchedMap[modulations[index]]=stateVariableInstances[i]; - } + var modulations = []; + if (visualObjects != null) { + if (visualObjects.length > 0) { + var elements = {}; + for (var voIndex in visualObjects) { + elements[visualObjects[voIndex]] = ""; + modulations.push(instance.getInstancePath() + "." + visualObjects[voIndex]); + + } + GEPPETTO.SceneController.splitGroups(instance, elements); } - } - - //add brightness listener for map of variables - for (var index in matchedMap){ - this.addBrightnessListener(index, matchedMap[index], colorfn); - } + else { + modulations.push(instance.getInstancePath()); + } + } + + var matchedMap = []; + //statevariableinstances come out of order, needs to sort into map to avoid nulls + for (var index in modulations) { + for (var i in stateVariableInstances) { + if (stateVariableInstances[i].getParent().getInstancePath() == modulations[index]) { + matchedMap[modulations[index]] = stateVariableInstances[i]; + } + } + } + + //add brightness listener for map of variables + for (var index in matchedMap) { + this.addBrightnessListener(index, matchedMap[index], colorfn); + } // update flag this.brightnessFunctionSet = true; }, - - addBrightnessListener: function(instance, modulation, colorfn){ + + addBrightnessListener: function (instance, modulation, colorfn) { GEPPETTO.trigger(GEPPETTO.Events.Lit_entities_changed); - this.addOnNodeUpdatedCallback(modulation, function (stateVariableInstance, step) { - if((stateVariableInstance.getTimeSeries() != undefined) && - (step 0) { - var selected = ""; - var geometryIdentifier = ""; - - // sort intersects - var compare = function (a, b) { - if (a.distance < b.distance) - return -1; - if (a.distance > b.distance) - return 1; - return 0; - }; - - intersects.sort(compare); - - var selectedIntersect; - // Iterate and get the first visible item (they are now ordered by proximity) - for (var i = 0; i < intersects.length; i++) { - // figure out if the entity is visible - var instancePath = ""; - if (intersects[i].object.hasOwnProperty("instancePath")) { - instancePath = intersects[i].object.instancePath; - geometryIdentifier = intersects[i].object.geometryIdentifier; - } - else { - //weak assumption: if the object doesn't have an instancePath its parent will - instancePath = intersects[i].object.parent.instancePath; - geometryIdentifier = intersects[i].object.parent.geometryIdentifier; - } - if(instancePath!=null||undefined){ - var visible = eval(instancePath + '.visible'); - if (intersects.length == 1 || i == intersects.length) { - //if there's only one element intersected we select it regardless of its opacity - if (visible) { - selected = instancePath; - selectedIntersect = intersects[i]; - break; - } - } - else { - //if there are more than one element intersected and opacity of the current one is less than 1 - //we skip it to realize a "pick through" - var opacity = GEPPETTO.getVARS().meshes[instancePath].defaultOpacity; - if ((opacity == 1 && visible) || GEPPETTO.isKeyPressed("ctrl")) { - selected = instancePath; - selectedIntersect = intersects[i]; - break; - } - else if (visible && opacity < 1 && opacity > 0) { - //if only transparent objects intersected select first or the next down if - //one is already selected in order to enable "burrow through" sample. - if (selected == "" && !eval(instancePath + '.selected')) { - selected = instancePath; - selectedIntersect = intersects[i]; - } - else { - if (eval(instancePath + '.selected') && i != intersects.length - 1) { - selected = ""; - } - } - } - } - } - } - - - if (selected != "") { - if (GEPPETTO.getVARS().meshes.hasOwnProperty(selected) || GEPPETTO.getVARS().splitMeshes.hasOwnProperty(selected)) { - if (!GEPPETTO.isKeyPressed("shift")) { - GEPPETTO.G.unSelectAll(); - } - - var selectedIntersectCoordinates = [selectedIntersect.point.x, selectedIntersect.point.y, selectedIntersect.point.z] - if(geometryIdentifier==undefined){ - geometryIdentifier=""; - } - GEPPETTO.Console.executeCommand(selected + '.select(' + false + ', ' + '"' + geometryIdentifier + '", [' + selectedIntersectCoordinates + '])'); - } - } - } - else if (GEPPETTO.isKeyPressed("ctrl")) { - GEPPETTO.G.unSelectAll(); - } - } - } - }, false); - - GEPPETTO.getVARS().renderer.domElement - .addEventListener( - 'mousemove', - function (event) { - GEPPETTO.getVARS().mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - GEPPETTO.getVARS().mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - }, false); - window - .addEventListener( - 'resize', - function () { - var container = $(GEPPETTO.getVARS().container), width = container.width(), height = container.height(); - - GEPPETTO.getVARS().camera.aspect = (width) / (height); - GEPPETTO.getVARS().camera.updateProjectionMatrix(); - GEPPETTO.getVARS().renderer.setSize(width, height); - GEPPETTO.getVARS().composer.setSize(width, height); - }, false); - - GEPPETTO.getVARS().listenersCreated = true; - } - }; - - // ============================================================================ - // Application logic. - // ============================================================================ GEPPETTO.Init = { - initialised : false, - /** * */ @@ -328,37 +56,10 @@ define(function (require) { * @returns {*|Object} */ initialize: function (containerp) { - GEPPETTO.getVARS().container = containerp; createChannel(); - setupScene(); - setupCamera(); - setupRenderer(); - setupLights(); - setupControls(); - setupListeners(); - this.initialised = true; - GEPPETTO.trigger(GEPPETTO.Events.Canvas_initialised); - return GEPPETTO.getVARS(); - }, + } - /** - * Reinitializes the camera with the Y axis flipped - */ - flipCameraY: function () { - GEPPETTO.getVARS().camera.up = new THREE.Vector3(0, -1, 0); - setupControls(); - GEPPETTO.resetCamera(); - }, - flipCameraZ: function () { - GEPPETTO.getVARS().camera.direction = new THREE.Vector3(0, 0, -1); - setupControls(); - GEPPETTO.resetCamera(); - }, - - movieMode: function(toggle){ - configureRenderer(toggle); - } }; }; }); diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js index bdb0d7cc9..d64fd0235 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js @@ -6,8 +6,8 @@ * @author giovanni@openworm.org (Giovanni Idili) * @author Jesus R. Martinez (jesus@metacell.us) */ -define(function(require) { - return function(GEPPETTO) { +define(function (require) { + return function (GEPPETTO) { var $ = require('jquery'), React = require('react'), @@ -20,26 +20,16 @@ define(function(require) { */ GEPPETTO.Main = { - StatusEnum: { - DEFAULT: 0, - CONTROLLING: 1, - OBSERVING: 2 - }, - idleTime: 0, disconnected: false, status: 0, statusWorker: null, - getVisitorStatus: function() { - return this.status; - }, - - getStatusWorker: function() { + getStatusWorker: function () { return this.statusWorker; }, - startStatusWorker: function() { + startStatusWorker: function () { //create web worker for checking status if (this.statusWorker != undefined) { this.statusWorker.terminate(); @@ -49,7 +39,7 @@ define(function(require) { this.statusWorker.postMessage(2000); //receives message from web worker - this.statusWorker.onmessage = function(event) { + this.statusWorker.onmessage = function (event) { if (window.Project != null || undefined) { var experiments = window.Project.getExperiments(); var pull = false; @@ -71,7 +61,7 @@ define(function(require) { /** * Initialize web socket communication */ - init: function() { + init: function () { GEPPETTO.MessageSocket.connect(GEPPETTO.MessageSocket.protocol + window.location.host + '/' + window.BUNDLE_CONTEXT_PATH + '/GeppettoServlet'); GEPPETTO.Console.debugLog(GEPPETTO.Resources.GEPPETTO_INITIALIZED); }, @@ -79,7 +69,7 @@ define(function(require) { /** * Idle check */ - idleCheck: function() { + idleCheck: function () { if (GEPPETTO.Main.idleTime > -1) { var allowedTime = 2, timeOut = 4; if (!GEPPETTO.Main.disconnected) { @@ -94,7 +84,7 @@ define(function(require) { $('#infomodal-text').html(GEPPETTO.Resources.IDLE_MESSAGE); $('#infomodal-btn').html("Yes"); - $('#infomodal-btn').html("Yes").click(function() { + $('#infomodal-btn').html("Yes").click(function () { $('#infomodal').modal('hide'); GEPPETTO.Main.idleTime = 0; @@ -123,7 +113,7 @@ define(function(require) { GEPPETTO.MessageSocket.close(); var webGLStarted = GEPPETTO.init(GEPPETTO.FE.createContainer()); - var webWorkersSupported = (typeof(Worker) !== "undefined") ? true : false; + var webWorkersSupported = (typeof (Worker) !== "undefined") ? true : false; if (!webGLStarted || !webWorkersSupported) { GEPPETTO.FE.notifyInitErrors(webGLStarted, webWorkersSupported); @@ -140,13 +130,11 @@ define(function(require) { // Application logic. // ============================================================================ - $(document).ready(function() { - //Create canvas - var webGLStarted = GEPPETTO.webGLAvailable(); - var webWorkersSupported = (typeof(Worker) !== "undefined") ? true : false; + $(document).ready(function () { + var webWorkersSupported = (typeof (Worker) !== "undefined") ? true : false; //make sure webgl started correctly - if (!webGLStarted || !webWorkersSupported) { + if (!webWorkersSupported) { GEPPETTO.FE.notifyInitErrors(webGLStarted, webWorkersSupported); } else { GEPPETTO.FE.initialEvents(); @@ -155,12 +143,12 @@ define(function(require) { setInterval(GEPPETTO.Main.idleCheck, 240000); // 1 minute var here = $(this); //Zero the idle timer on mouse movement. - here.mousemove(function(e) { + here.mousemove(function (e) { if (GEPPETTO.Main.idleTime > -1) { GEPPETTO.Main.idleTime = 0; } }); - here.keypress(function(e) { + here.keypress(function (e) { if (GEPPETTO.Main.idleTime > -1) { GEPPETTO.Main.idleTime = 0; } @@ -170,7 +158,7 @@ define(function(require) { GEPPETTO.Main.init(); var visibleExperiments = false; - $('#experimentsButton').click(function(e) { + $('#experimentsButton').click(function (e) { if (!visibleExperiments) { $('#console').hide(); $("#pythonConsole").hide(); @@ -185,7 +173,7 @@ define(function(require) { } }); - $('#consoleButton').click(function(e) { + $('#consoleButton').click(function (e) { $('#console').show(); $('#experiments').hide(); $("#pythonConsole").hide(); @@ -193,7 +181,7 @@ define(function(require) { visibleExperiments = false; }); - $('#pythonConsoleButton').click(function(e) { + $('#pythonConsoleButton').click(function (e) { $('#console').hide(); $('#experiments').hide(); $("#pythonConsole").show(); diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.js index c78dc9bd6..9e740d6b1 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.js @@ -6,413 +6,37 @@ define(function (require) { var $ = require('jquery'), _ = require('underscore'), Backbone = require('backbone'); + var THREEx = require('./THREEx.KeyboardState'); //Nothing to do with THREE //These two libraries are required here so that Geppetto can work properly in an iframe (as embedded website). //Otherwise, sometimes (randomly) these libraries are not loaded on time and some js commands failed and the web is not loaded properly. require('jquery-ui'); require('bootstrap'); - - - var isWebglEnabled = require('detector-webgl'); - - var THREE = require('three'); - require('../../components/interface/3dCanvas/TrackballControls'); - require('../../components/interface/3dCanvas/OBJLoader'); - var THREEx = require('../../components/interface/3dCanvas/THREEx.KeyboardState'); - THREE.ColladaLoader = require('imports?THREE=three!exports?THREE.ColladaLoader!../../../node_modules\/three\/examples\/js\/loaders\/ColladaLoader'); - THREE.ConvolutionShader = require('imports?THREE=three!exports?THREE.ConvolutionShader!../../../node_modules\/three\/examples\/js\/shaders\/ConvolutionShader'); - THREE.CopyShader = require('imports?THREE=three!exports?THREE.CopyShader!../../../node_modules\/three\/examples\/js\/shaders\/CopyShader'); - THREE.FilmShader = require('imports?THREE=three!exports?THREE.FilmShader!../../../node_modules\/three\/examples\/js\/shaders\/FilmShader'); - THREE.FocusShader = require('imports?THREE=three!exports?THREE.FocusShader!../../../node_modules\/three\/examples\/js\/shaders\/FocusShader'); - THREE.EffectComposer = require('imports?THREE=three!exports?THREE.EffectComposer!../../../node_modules\/three\/examples\/js\/postprocessing\/EffectComposer'); - THREE.MaskPass = require('imports?THREE=three!exports?THREE.MaskPass!../../../node_modules\/three\/examples\/js\/postprocessing\/MaskPass'); - THREE.RenderPass = require('imports?THREE=three!exports?THREE.RenderPass!../../../node_modules\/three\/examples\/js\/postprocessing\/RenderPass'); - THREE.BloomPass = require('imports?THREE=three!exports?THREE.BloomPass!../../../node_modules\/three\/examples\/js\/postprocessing\/BloomPass'); - THREE.ShaderPass = require('imports?THREE=three!exports?THREE.ShaderPass!../../../node_modules\/three\/examples\/js\/postprocessing\/ShaderPass'); - THREE.FilmPass = require('imports?THREE=three!exports?THREE.FilmPass!../../../node_modules\/three\/examples\/js\/postprocessing\/FilmPass'); - var Stats = require('stats.js'); - - var step = 0; - - var VARS = { - debug: false, - camera: null, - container: null, - controls: null, - scene: null, - meshes: {}, - splitMeshes: {}, - connectionLines: {}, - renderer: null, - clock: new THREE.Clock(), - stats: null, - gui: null, - projector: null, - keyboard: new THREEx.KeyboardState(), - needsUpdate: false, - metadata: {}, - customUpdate: null, - mouseClickListener: null, - rotationMode: false, - mouse: { - x: 0, - y: 0 - }, - visualModelMap: null, - idCounter: 0, - sceneCenter: new THREE.Vector3(), - cameraPosition: new THREE.Vector3(), - canvasCreated: false, - listenersCreated: false, - selected: [], - pickingEnabled: true, // flag to enable disable 3d picking - playTimerStep: 5, // timer step in milliseconds - playLoop: false, - backgroundColor: 0x101010, - }; + /** - * Initialize the engine + * Initialise Geppetto * * @class GEPPETTO */ var GEPPETTO = { - - /** - * Initialize Geppetto - * - * @param {HTML} - * containerp - HTML element to draw the 3D Scene - * @returns {Boolean} - */ - //AQP: Almost same code!!!!!!!!!!! - init: function (containerp) { - if (!isWebglEnabled) { - Detector.addGetWebGLMessage(); - return false; - } else { - VARS = GEPPETTO.Init.initialize(containerp); - return true; - } - }, - - /** - * - * @returns {Boolean} True or false, whether webgl is detected or not - */ - webGLAvailable: function () { - if (isWebglEnabled) { - return true; - } - else { - Detector.addGetWebGLMessage(); - return false; - } - // if (!Detector.webgl) { - // Detector.addGetWebGLMessage(); - // return false; - // } else { - // return true; - // } - }, - - /** - * Returns variables object used to store meshes, and other properties - * of the 3D scene - * - * @returns {Object} Object with important properties for the 3D Scene. - */ - getVARS: function () { - return VARS; - }, - - /** - * Set object local rotation, with respect to z (Euler angle) - * - * @param {AspectNode} - * aspect - the aspect containing the entity to rotate - * @param {String} - * entityName - the name of the entity to be rotated (in the - * 3d model) - * @param {Float} - * angle - the angle (radians) of the local rotation around z - */ - setLocalRotationZ: function (aspect, entityName, angle) { - // TODO: the first arg should be a vis tree - var threeObject = GEPPETTO.get3DObjectInVisualizationTree(aspect - .getInstancePath(), entityName); - if (threeObject != null) { - threeObject.rotation.z = angle; - } - }, - - /** - * Reset camera for scene. - */ - resetCamera: function () { - GEPPETTO.getVARS().controls.reset(); - - var aabbMin = null; - var aabbMax = null; - - GEPPETTO.getVARS().scene.traverse(function (child) { - if (child.hasOwnProperty("geometry")) { - child.geometry.computeBoundingBox(); - - var bb = child.geometry.boundingBox; - bb.translate(child.localToWorld(new THREE.Vector3())); - - // If min and max vectors are null, first values become - // default min and max - if (aabbMin == null && aabbMax == null) { - aabbMin = bb.min; - aabbMax = bb.max; - } - - // Compare other meshes, particles BB's to find min and max - else { - aabbMin.x = Math.min(aabbMin.x, bb.min.x); - aabbMin.y = Math.min(aabbMin.y, bb.min.y); - aabbMin.z = Math.min(aabbMin.z, bb.min.z); - aabbMax.x = Math.max(aabbMax.x, bb.max.x); - aabbMax.y = Math.max(aabbMax.y, bb.max.y); - aabbMax.z = Math.max(aabbMax.z, bb.max.z); - } - } - }); - - if (aabbMin != null && aabbMax != null) { - // Compute world AABB center - GEPPETTO.getVARS().sceneCenter.x = (aabbMax.x + aabbMin.x) * 0.5; - GEPPETTO.getVARS().sceneCenter.y = (aabbMax.y + aabbMin.y) * 0.5; - GEPPETTO.getVARS().sceneCenter.z = (aabbMax.z + aabbMin.z) * 0.5; - - GEPPETTO.updateCamera(aabbMax, aabbMin); - } - }, - - /** - * Update camera with new position and place to lookat - */ - updateCamera: function (aabbMax, aabbMin) { - // Compute world AABB "radius" - var diag = new THREE.Vector3(); - diag = diag.subVectors(aabbMax, aabbMin); - var radius = diag.length() * 0.5; - - GEPPETTO.pointCameraTo(GEPPETTO.getVARS().sceneCenter); - - // Compute offset needed to move the camera back that much needed to center AABB - var offset = radius / Math.sin(Math.PI / 180.0 * GEPPETTO.getVARS().camera.fov * 0.5); - - var dir = GEPPETTO.getVARS().camera.direction.clone(); - dir.multiplyScalar(offset); - - // Store camera position - GEPPETTO.getVARS().camera.position.addVectors(dir, GEPPETTO.getVARS().controls.target); - GEPPETTO.getVARS().camera.updateProjectionMatrix(); - }, - - boundingBox: function (obj) { - if (obj instanceof THREE.Mesh) { - - var geometry = obj.geometry; - geometry.computeBoundingBox(); - return geometry.boundingBox; - - } - - if (obj instanceof THREE.Object3D) { - - var bb = new THREE.Box3(); - for (var i = 0; i < obj.children.length; i++) { - bb.union(GEPPETTO.boundingBox(obj.children[i])); - } - return bb; - } - }, - - shapeCenterOfGravity: function (obj) { - return GEPPETTO.boundingBox(obj).center(); - }, - - /** */ - pointCameraTo: function (node) { - // Refocus camera to the center of the new object - var COG; - if (node instanceof THREE.Vector3) { - COG = node; - } else { - COG = GEPPETTO.shapeCenterOfGravity(node); - } - var v = new THREE.Vector3(); - v.subVectors(COG, GEPPETTO.getVARS().controls.target); - GEPPETTO.getVARS().camera.position.addVectors( - GEPPETTO.getVARS().camera.position, v); - - // retrieve camera orientation - - GEPPETTO.getVARS().camera.lookAt(COG); - GEPPETTO.getVARS().controls.target.set(COG.x, COG.y, COG.z); - }, - - /** - * Status of scene, populated or not - * - * @returns {Boolean} True or false depending whether scene is populated - * or not - */ - isScenePopulated: function () { - return !(_.isEmpty(GEPPETTO.getVARS().visualModelMap)); - }, - - /** - * Has canvas been created? - * - * @returns {Boolean] True or false if canvas has been created or not - */ - isCanvasCreated: function () { - return GEPPETTO.getVARS().canvasCreated; - }, - - /** - * Sets up the HUD display with the scene stat's fps. - */ - setupStats: function () { - // Stats - if ($("#stats").length == 0) { - if (VARS != null) { - GEPPETTO.getVARS().stats = new Stats(); - GEPPETTO.getVARS().stats.domElement.style.float = 'right'; - GEPPETTO.getVARS().stats.domElement.style.position = 'absolute'; - GEPPETTO.getVARS().stats.domElement.style.top = '60px'; - GEPPETTO.getVARS().stats.domElement.style.right = '5px'; - GEPPETTO.getVARS().stats.domElement.style.zIndex = 100; - $('#controls').append(GEPPETTO.getVARS().stats.domElement); - } - } - }, - - /** - * Displays HUD for FPS stats - */ - toggleStats: function (mode) { - if (mode) { - if ($("#stats").length == 0) { - GEPPETTO.setupStats(); - } else { - $("#stats").show(); - } - } else { - $("#stats").hide(); - } - }, - - - /** - * Adds debug axis to the scene - */ - setupAxis: function () { - // To use enter the axis length - GEPPETTO.getVARS().scene.add(new THREE.AxisHelper(200)); - }, - - /** - * Renders objects in the scene - */ - render: function () { - GEPPETTO.getVARS().renderer.clear(); - GEPPETTO.getVARS().composer.render(0.01); - }, - - /** - * Returns intersected objects from mouse click - * - * @returns {Array} a list of objects intersected by the current mouse - * coordinates - */ - getIntersectedObjects: function () { - // create a Ray with origin at the mouse position and direction into th scene (camera direction) - var vector = new THREE.Vector3(GEPPETTO.getVARS().mouse.x, GEPPETTO - .getVARS().mouse.y, 1); - vector.unproject(GEPPETTO.getVARS().camera); - - var raycaster = new THREE.Raycaster( - GEPPETTO.getVARS().camera.position, vector.sub( - GEPPETTO.getVARS().camera.position).normalize()); - - var visibleChildren = []; - GEPPETTO.getVARS().scene.traverse(function (child) { - if (child.visible && !(child.clickThrough == true)) { - if (child.geometry != null || undefined) { - child.geometry.computeBoundingBox(); - } - visibleChildren.push(child); - } - }); - - // returns an array containing all objects in the scene with which - // the ray intersects - return raycaster.intersectObjects(visibleChildren); - }, - + debug : false, + keyboard : new THREEx.KeyboardState(), /** * @param{String} key - The pressed key * @returns {boolean} True if the key is pressed */ isKeyPressed: function (key) { - return GEPPETTO.getVARS().keyboard.pressed(key); - }, - - /** - * @param x - * @param y - */ - incrementCameraPan: function (x, y) { - GEPPETTO.getVARS().controls.incrementPanEnd(x, y); - }, - - /** - * @param x - * @param y - * @param z - */ - incrementCameraRotate: function (x, y, z) { - GEPPETTO.getVARS().controls.incrementRotationEnd(x, y, z); - }, - - /** - * @param z - */ - incrementCameraZoom: function (z) { - GEPPETTO.getVARS().controls.incrementZoomEnd(z); + return this.keyboard.pressed(key); }, - /** - * @param x - * @param y - * @param z - */ - setCameraPosition: function (x, y, z) { - GEPPETTO.getVARS().controls.setPosition(x, y, z); - }, - - /** - * @param rx - * @param ry - * @param rz - * @param radius - */ - setCameraRotation: function (rx, ry, rz, radius) { - GEPPETTO.getVARS().controls.setRotation(rx, ry, rz, radius); - }, /** * @param msg */ log: function (msg) { - if (GEPPETTO.getVARS().debug) { + if (GEPPETTO.debug) { var d = new Date(); var curr_hour = d.getHours(); var curr_min = d.getMinutes(); @@ -449,7 +73,6 @@ define(function (require) { require('../../common/GEPPETTO.Resources')(GEPPETTO); require('./GEPPETTO.Events')(GEPPETTO); require('./GEPPETTO.Init')(GEPPETTO); - require('../../components/interface/3dCanvas/GEPPETTO.SceneFactory')(GEPPETTO); require('../../components/interface/3dCanvas/GEPPETTO.SceneController')(GEPPETTO); require('./GEPPETTO.FE')(GEPPETTO); require('../../common/GEPPETTO.UserController')(GEPPETTO); diff --git a/src/main/webapp/js/components/interface/3dCanvas/THREEx.KeyboardState.js b/src/main/webapp/js/pages/geppetto/THREEx.KeyboardState.js similarity index 100% rename from src/main/webapp/js/components/interface/3dCanvas/THREEx.KeyboardState.js rename to src/main/webapp/js/pages/geppetto/THREEx.KeyboardState.js diff --git a/src/main/webapp/js/pages/geppetto/geppetto.ejs b/src/main/webapp/js/pages/geppetto/geppetto.ejs index 9cbc6f532..08317a702 100644 --- a/src/main/webapp/js/pages/geppetto/geppetto.ejs +++ b/src/main/webapp/js/pages/geppetto/geppetto.ejs @@ -42,7 +42,6 @@ $content
                -
                From d066f0546b196b130f74722bb913fe244f3aac30 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 2 May 2017 17:21:30 +0100 Subject: [PATCH 145/339] More work on 3d canvas extraction --- .../components/interface/3dCanvas/Canvas.js | 18 +- .../3dCanvas/GEPPETTO.SceneController.js | 43 ++- .../interface/3dCanvas/ThreeDEngine.js | 247 +++++++++++++++++- 3 files changed, 289 insertions(+), 19 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index e376cfe30..776a011f6 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -15,7 +15,6 @@ define(function (require) { var canvasComponent = React.createClass({ engine: null, container: null, - selected: [], backgroundColor: 0x101010, @@ -44,6 +43,17 @@ define(function (require) { return this; }, + /** + * Deselects an instance + * + * @param {String} instancePath - Path of instance to select + * @param {String} geometryIdentifier - Identifier of the geometry that was clicked + */ + deselectInstance: function (instancePath) { + this.engine.deselectInstance(instancePath); + return this; + }, + assignRandomColor: function (instance) { this.engine.assignRandomColor(instance); return this; @@ -233,8 +243,8 @@ define(function (require) { */ setContainerDimensions: function () { var containerSelector = $(this.container); - var height = containerSelector.parent().height() - 40 - var width = containerSelector.parent().width() - 30 + var height = containerSelector.parent().height(); + var width = containerSelector.parent().width(); containerSelector.height(height); containerSelector.width(width); return [width, height]; @@ -250,9 +260,9 @@ define(function (require) { Detector.addGetWebGLMessage(); } else { this.container = $("#" + this.props.id + "_component").get(0); + this.setContainerDimensions(); this.engine = new ThreeDEngine(this.container, this.props.id); GEPPETTO.SceneController.add3DCanvas(this); - this.setContainerDimensions(); var that = this; $("#" + this.props.id).on("dialogresizestop resizeEnd", function (event, ui) { var [width, height] = that.setContainerDimensions() diff --git a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js index 2a7195d45..e0afa69c4 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js @@ -50,6 +50,35 @@ define(function (require) { return selected; }, + /** + * + * Returns all the selected instances + * + * @command G.getSelection() + * @returns {Array} Returns list of all instances selected + */ + getSelection: function () { + var selection = this.traverseSelection(window.Instances); + return selection; + }, + + /** + * Helper method that traverses through run time tree looking for selected entities. + */ + traverseSelection: function (instances) { + var selection = []; + if (instances != null || undefined) { + for (var e = 0; e < instances.length; e++) { + var instance = instances[e]; + if (instance.selected) { + selection.push(instance); + } + selection = selection.concat(this.traverseSelection(instance.getChildren())); + } + } + return selection; + }, + select: function (instances) { for (var i = 0; i < instances.length; i++) { instances[i].select(); @@ -64,7 +93,7 @@ define(function (require) { }, /** - * Selects an instance given its. + * Selects an instance in all existing canvas * * @param {String} instancePath - Path of instance to select * @param {String} geometryIdentifier - Identifier of the geometry that was clicked @@ -75,6 +104,18 @@ define(function (require) { } }, + /** + * Deselects an instance in all existing canvas + * + * @param {String} instancePath - Path of instance to select + * @param {String} geometryIdentifier - Identifier of the geometry that was clicked + */ + deselectInstance: function (instancePath) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].deselectInstance(instancePath); + } + }, + /** * * @param instances diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index 8f266a40a..05dc890bc 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -6,6 +6,11 @@ */ define(['jquery'], function () { + var Instance = require('../../../geppettoModel/model/Instance'); + var ArrayInstance = require('../../../geppettoModel/model/ArrayInstance'); + var Type = require('../../../geppettoModel/model/Type'); + var Variable = require('../../../geppettoModel/model/Variable'); + var THREE = require('three'); require('./TrackballControls'); require('./OBJLoader'); @@ -169,7 +174,7 @@ define(['jquery'], function () { } else { //if there are more than one element intersected and opacity of the current one is less than 1 //we skip it to realize a "pick through" - var opacity = this.meshes[instancePath].defaultOpacity; + var opacity = that.meshes[instancePath].defaultOpacity; if ((opacity == 1 && visible) || GEPPETTO.isKeyPressed("ctrl")) { selected = instancePath; selectedIntersect = intersects[i]; @@ -194,7 +199,7 @@ define(['jquery'], function () { if (selected != "") { if (that.meshes.hasOwnProperty(selected) || that.splitMeshes.hasOwnProperty(selected)) { if (!GEPPETTO.isKeyPressed("shift")) { - that.unSelectAll(); + that.deselectAll(); } var selectedIntersectCoordinates = [selectedIntersect.point.x, selectedIntersect.point.y, selectedIntersect.point.z] @@ -205,7 +210,7 @@ define(['jquery'], function () { } } } else if (GEPPETTO.isKeyPressed("ctrl")) { - that.unSelectAll(); + that.deselectAll(); } } } @@ -236,13 +241,14 @@ define(['jquery'], function () { var width = $(this.container).width(); var height = $(this.container).height(); this.renderer.setPixelRatio(window.devicePixelRatio); - this.renderer.setSize(width, height); + this.renderer.autoClear = false; this.container.appendChild(this.renderer.domElement); var renderModel = new THREE.RenderPass(this.scene, this.camera); this.composer = new THREE.EffectComposer(this.renderer); + this.setSize(width, height); if (shaders) { var effectBloom = new THREE.BloomPass(0.75); @@ -1399,6 +1405,7 @@ define(['jquery'], function () { * instancePath - Path of aspect of mesh to select */ selectInstance: function (instancePath, geometryIdentifier) { + var that = this; if (geometryIdentifier != undefined && geometryIdentifier != "") { instancePath = instancePath + "." + geometryIdentifier; } @@ -1414,15 +1421,15 @@ define(['jquery'], function () { if (mesh instanceof THREE.Object3D) { mesh.traverse(function (child) { if (child.hasOwnProperty("material")) { - this.threeEngine.setThreeColor(child.material.color, GEPPETTO.Resources.COLORS.SELECTED); + that.setThreeColor(child.material.color, GEPPETTO.Resources.COLORS.SELECTED); child.material.opacity = Math.max(0.5, child.material.defaultOpacity); child.geometry.computeBoundingBox(); - this.controls.target.copy(child.position); - this.controls.target.add(child.geometry.boundingBox.getCenter()); + that.controls.target.copy(child.position); + that.controls.target.add(child.geometry.boundingBox.getCenter()); } }); } else { - this.threeEngine.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.SELECTED); + this.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.SELECTED); mesh.material.opacity = Math.max(0.5, mesh.material.defaultOpacity); mesh.geometry.computeBoundingBox(); //let's set the center of rotation to the selected mesh @@ -1442,9 +1449,40 @@ define(['jquery'], function () { } - return true; } - return false; + + var instance = eval(instancePath); + + // Behaviour: help exploration of networks by ghosting and not highlighting non connected or selected + if (instance.getConnections().length > 0) { + // allOtherMeshes will contain a list of all the non connected entities in the scene + var allOtherMeshes = $.extend({}, this.meshes); + // look on the simulation selection options and perform necessary operations + if (G.getSelectionOptions().show_inputs && G.getSelectionOptions().show_outputs) { + var meshes = this.highlightInstances(true); + for (var i in meshes) { + delete allOtherMeshes[meshes[i]]; + } + } + else if (G.getSelectionOptions().show_inputs) { + var inputs = this.highlightInstances(true, GEPPETTO.Resources.INPUT); + for (var i in inputs) { + delete allOtherMeshes[inputs[i]]; + } + } + else if (G.getSelectionOptions().show_outputs) { + var outputs = this.highlightInstances(true, GEPPETTO.Resources.OUTPUT); + for (var o in outputs) { + delete allOtherMeshes[outputs[o]]; + } + } + if (G.getSelectionOptions().draw_connection_lines) { + this.showConnectionLines(instancePath, true); + } + if (G.getSelectionOptions().unselected_transparent) { + this.unselectedTransparent(allOtherMeshes, true); + } + } }, @@ -1481,11 +1519,161 @@ define(['jquery'], function () { mesh.selected = false; } } - return true; } - return false; + + if (G.getSelectionOptions().show_inputs && G.getSelectionOptions().show_outputs) { + this.highlightInstances(instancePath, false); + } + else if (G.getSelectionOptions().show_inputs) { + this.highlightInstances(instancePath, false, GEPPETTO.Resources.INPUT); + } + else if (G.getSelectionOptions().show_outputs) { + this.highlightInstances(instancePath, false, GEPPETTO.Resources.OUTPUT); + } + + if (G.getSelectionOptions().draw_connection_lines) { + this.showConnectionLines(instancePath, false); + } + + // TODO: trigger highlight on the ones still selected + + var selection = GEPPETTO.SceneController.getSelection(); + + // NOTE: do this down here, ghost effect won't be removed if stuff is still highlighted + if (G.getSelectionOptions().unselected_transparent) { + if (selection != undefined && selection.length > 0) { + // else (there is something selected) make this transparent + var mesh = {}; + mesh[instancePath] = this.meshes[instancePath]; + if (mesh[instancePath] != undefined) { + this.unselectedTransparent(mesh, true); + } + } else { + // if nothing else is selected do remove ghost effect + this.unselectedTransparent(false); + } + } + + }, + + /** + * Show output connections for this object. + + * @command AVisualCapability.highlightInstances() + * @param {boolean} mode - Show or hide output connections + */ + highlightInstances: function (path, mode, type) { + if (mode == null || mode == undefined) { + mode = true; + } + var entity = eval(path); + if (entity instanceof Instance || entity instanceof ArrayInstance) { + //show/hide connections + if (mode) { + this.highlightConnectedInstances(entity, type); + } + else { + this.restoreConnectedInstancesColour(entity); + } + } else if (entity instanceof Type || entity instanceof Variable) { + // fetch all instances for the given type or variable and call hide on each + var instances = GEPPETTO.ModelFactory.getAllInstancesOf(entity); + for (var j = 0; j < instances.length; j++) { + if (instances[j].hasCapability('VisualCapability')) { + this.highlightInstances(instances[j].getInstancePath(), mode, type); + } + } + } + }, + + /** + * Deselects all selected instancies + * + */ + deselectAll: function () { + var selection = GEPPETTO.SceneController.getSelection(); + if (selection.length > 0) { + for (var key in selection) { + var entity = selection[key]; + entity.deselect(); + } + } + + if (G.getSelectionOptions().unselected_transparent) { + this.unselectedTransparent(false); + } + return GEPPETTO.Resources.DESELECT_ALL; }, + + /** + * Make unselected instances transparent or not + * + * @param {boolean} + * apply - Turn on or off the transparency + */ + unselectedTransparent: function (apply) { + GEPPETTO.SceneController.unselectedTransparent(this.meshes, apply); + GEPPETTO.SceneController.unselectedTransparent(this.splitMeshes, apply); + }, + + /** + * Make unselected instances transparent or not + * + * @param {Array} + * meshes - Array of meshes to apply the transparency to + * @param {boolean} + * apply - Transparency effect on or off + */ + unselectedTransparent: function (meshes, apply) { + for (var v in meshes) { + var mesh = meshes[v]; + if (mesh != null && mesh.visible) { + if (apply && (!mesh.ghosted) && (!mesh.selected)) { + if (mesh instanceof THREE.Object3D) { + mesh.ghosted = true; + mesh.traverse(function (object) { + if (object.hasOwnProperty("material")) { + if (object.visible) { + object.ghosted = true; + object.material.transparent = true; + object.material.opacity = GEPPETTO.Resources.OPACITY.GHOST; + } + } + }); + } else { + mesh.ghosted = true; + mesh.material.transparent = true; + mesh.material.opacity = GEPPETTO.Resources.OPACITY.GHOST; + } + } else if ((!apply) && (mesh.ghosted)) { + if (mesh instanceof THREE.Object3D) { + mesh.ghosted = false; + mesh.traverse(function (object) { + if (object.hasOwnProperty("material")) { + if (object.visible) { + object.ghosted = false; + object.material.opacity = object.material.defaultOpacity; + if (object.material.opacity == 1) { + object.material.transparent = false; + } + } + } + }); + } else { + mesh.ghosted = false; + mesh.material.opacity = mesh.material.defaultOpacity; + if (mesh.material.opacity == 1) { + mesh.material.transparent = false; + } + } + } + } + + } + }, + + /** * Show aspect, make it visible. * @@ -1779,11 +1967,43 @@ define(['jquery'], function () { } }, + /** + * Show connection lines for this instance. + + * @command AVisualCapability.showConnectionLines() + * @param {boolean} mode - Show or hide connection lines + */ + showConnectionLines: function (instancePath, mode) { + if (mode == null || mode == undefined) { + mode = true; + } + var entity = eval(instancePath); + if (entity instanceof Instance || entity instanceof ArrayInstance) { + //show or hide connection lines + if (mode) { + this.showConnectionLinesForInstance(entity); + } + else { + this.removeConnectionLines(entity); + } + } else if (entity instanceof Type || entity instanceof Variable) { + // fetch all instances for the given type or variable and call hide on each + var instances = GEPPETTO.ModelFactory.getAllInstancesOf(entity); + for (var j = 0; j < instances.length; j++) { + if (instances[j].hasCapability('VisualCapability')) { + this.showConnectionLines(instances[j], mode); + } + } + } + }, + + /** + * * * @param instance */ - showConnectionLines: function (instance) { + showConnectionLinesForInstance: function (instance) { var connections = instance.getConnections(); var mesh = this.meshes[instance.getInstancePath()]; @@ -1802,7 +2022,6 @@ define(['jquery'], function () { connection.getB().getPath() : connection.getA().getPath(); - var otherEndMesh = this.meshes[otherEndPath]; var destination; From 0dda83e6fed6cde9449ee3fe1250128511604a4d Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 2 May 2017 17:22:01 +0100 Subject: [PATCH 146/339] Update visual capability --- .../capabilities/AVisualCapability.js | 132 +----------------- 1 file changed, 2 insertions(+), 130 deletions(-) diff --git a/src/main/webapp/js/geppettoModel/capabilities/AVisualCapability.js b/src/main/webapp/js/geppettoModel/capabilities/AVisualCapability.js index 815d74c51..40e12ee37 100644 --- a/src/main/webapp/js/geppettoModel/capabilities/AVisualCapability.js +++ b/src/main/webapp/js/geppettoModel/capabilities/AVisualCapability.js @@ -255,48 +255,11 @@ define(function (require) { if (this instanceof Instance || this instanceof ArrayInstance) { if (!this.selected) { - //first, before doing anything, we check what is currently selected - - if (G.getSelectionOptions().unselected_transparent) { - //something is already selected, we make everything not selected transparent - GEPPETTO.SceneController.setGhostEffect(true); - } - // set selection flag local to the instance and add to geppetto selection list this.selected = true; GEPPETTO.SceneController.selectInstance(this.getInstancePath(), geometryIdentifier); message = GEPPETTO.Resources.SELECTING_ASPECT + this.getInstancePath(); - // Behaviour: help exploration of networks by ghosting and not highlighting non connected or selected - if (this.getConnections().length > 0) { - // allOtherMeshes will contain a list of all the non connected entities in the scene - var allOtherMeshes = $.extend({}, GEPPETTO.getVARS().meshes); - // look on the simulation selection options and perform necessary operations - if (G.getSelectionOptions().show_inputs && G.getSelectionOptions().show_outputs) { - var meshes = this.highlightInstances(true); - for (var i in meshes) { - delete allOtherMeshes[meshes[i]]; - } - } - else if (G.getSelectionOptions().show_inputs) { - var inputs = this.highlightInstances(true, GEPPETTO.Resources.INPUT); - for (var i in inputs) { - delete allOtherMeshes[inputs[i]]; - } - } - else if (G.getSelectionOptions().show_outputs) { - var outputs = this.highlightInstances(true, GEPPETTO.Resources.OUTPUT); - for (var o in outputs) { - delete allOtherMeshes[outputs[o]]; - } - } - if (G.getSelectionOptions().draw_connection_lines) { - this.showConnectionLines(true); - } - if (G.getSelectionOptions().unselected_transparent) { - GEPPETTO.SceneController.ghostEffect(allOtherMeshes, true); - } - } //signal selection has changed in simulation pass instance GEPPETTO.trigger(GEPPETTO.Events.Select, this, geometryIdentifier, point); } else { @@ -344,38 +307,6 @@ define(function (require) { message = GEPPETTO.Resources.DESELECTING_ASPECT + this.getInstancePath(); GEPPETTO.SceneController.deselectInstance(this.getInstancePath()); this.selected = false; - - if (G.getSelectionOptions().show_inputs && G.getSelectionOptions().show_outputs) { - this.highlightInstances(false); - } - else if (G.getSelectionOptions().show_inputs) { - this.highlightInstances(false, GEPPETTO.Resources.INPUT); - } - else if (G.getSelectionOptions().show_outputs) { - this.highlightInstances(false, GEPPETTO.Resources.OUTPUT); - } - - if (G.getSelectionOptions().draw_connection_lines) { - this.showConnectionLines(false); - } - - // TODO: trigger highlight on the ones still selected - - // NOTE: do this down here, ghost effect won't be removed if stuff is still highlighted - if (G.getSelectionOptions().unselected_transparent) { - if (G.getSelection() != undefined && G.getSelection().length > 0) { - // else (there is something selected) make this ghosted - var mesh = {}; - mesh[this.getInstancePath()] = GEPPETTO.getVARS().meshes[this.getInstancePath()]; - if (mesh[this.getInstancePath()] != undefined) { - GEPPETTO.SceneController.ghostEffect(mesh, true); - } - } else { - // if nothing else is selected do remove ghost effect - GEPPETTO.SceneController.setGhostEffect(false); - } - } - //trigger event that selection has been changed GEPPETTO.trigger(GEPPETTO.Events.Select, this); } else { @@ -464,69 +395,10 @@ define(function (require) { } return message; - }, - - - /** - * Show output connections for this object. - - * @command AVisualCapability.highlightInstances() - * @param {boolean} mode - Show or hide output connections - */ - highlightInstances: function (mode, type) { - if (mode == null || mode == undefined) { - mode = true; - } - - if (this instanceof Instance || this instanceof ArrayInstance) { - //show/hide connections - if (mode) { - GEPPETTO.SceneController.highlightConnectedInstances(this, type); - } - else { - GEPPETTO.SceneController.restoreConnectedInstancesColour(this); - } - } else if (this instanceof Type || this instanceof Variable) { - // fetch all instances for the given type or variable and call hide on each - var instances = GEPPETTO.ModelFactory.getAllInstancesOf(this); - for (var j = 0; j < instances.length; j++) { - if (instances[j].hasCapability(this.capabilityId)) { - instances[j].highlightInstances(mode, type); - } - } - } - }, - - - /** - * Show connection lines for this instance. + } - * @command AVisualCapability.showConnectionLines() - * @param {boolean} mode - Show or hide connection lines - */ - showConnectionLines: function (mode) { - if (mode == null || mode == undefined) { - mode = true; - } - if (this instanceof Instance || this instanceof ArrayInstance) { - //show or hide connection lines - if (mode) { - GEPPETTO.SceneController.showConnectionLines(this); - } - else { - GEPPETTO.SceneController.removeConnectionLines(this); - } - } else if (this instanceof Type || this instanceof Variable) { - // fetch all instances for the given type or variable and call hide on each - var instances = GEPPETTO.ModelFactory.getAllInstancesOf(this); - for (var j = 0; j < instances.length; j++) { - if (instances[j].hasCapability(this.capabilityId)) { - instances[j].showConnectionLines(mode); - } - } - } - } + } }); From 367c4dcc40f482322df03d211c729879dfb855d1 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 17:43:43 +0100 Subject: [PATCH 147/339] fix camera control position when in the background --- .../style/less/components/controls/camera-controls.less | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/style/less/components/controls/camera-controls.less b/src/main/webapp/style/less/components/controls/camera-controls.less index 113baf46e..046fd94cd 100644 --- a/src/main/webapp/style/less/components/controls/camera-controls.less +++ b/src/main/webapp/style/less/components/controls/camera-controls.less @@ -5,8 +5,10 @@ -o-transform: rotate(@deg); } -#camera-controls { - margin-top: 50px; +#mainContainer { + .position-toolbar { + top: 50px; + } } .position-toolbar { From 346645938dddf451e1d051462fd13e910c41d8bf Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 17:44:04 +0100 Subject: [PATCH 148/339] refactor parameters for addwidget --- src/main/webapp/js/pages/geppetto/G.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/pages/geppetto/G.js b/src/main/webapp/js/pages/geppetto/G.js index 7927b2cbd..5498b5436 100644 --- a/src/main/webapp/js/pages/geppetto/G.js +++ b/src/main/webapp/js/pages/geppetto/G.js @@ -56,8 +56,8 @@ define(function (require) { * @param isStateless * @returns {*} */ - addWidget: function (type, isStateless) { - var newWidget = GEPPETTO.ComponentFactory.addWidget(type, isStateless); + addWidget: function (type, properties, callback) { + var newWidget = GEPPETTO.ComponentFactory.addWidget(type, properties, callback); return newWidget; }, From a0e9fcbf5a26a722660eb18b0161033d439414cf Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 17:44:46 +0100 Subject: [PATCH 149/339] unify getavailableid for widget and components --- .../webapp/js/components/ComponentFactory.js | 150 ++++++++++-------- .../webapp/js/components/NewWidgetFactory.js | 32 ---- .../webapp/js/components/WidgetController.js | 68 +------- 3 files changed, 87 insertions(+), 163 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index a184915ce..87f9cab62 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -1,34 +1,3 @@ -/******************************************************************************* - * - * Copyright (c) 2011, 2016 OpenWorm. - * http://openworm.org - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the MIT License - * which accompanies this distribution, and is available at - * http://opensource.org/licenses/MIT - * - * Contributors: - * OpenWorm - http://openworm.org/people.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - *******************************************************************************/ define(function (require) { @@ -43,7 +12,7 @@ define(function (require) { GEPPETTO.ComponentFactory = { //All the components potentially instantiable go here - var components = { + components : { 'FORM':'interface/form/Form', 'PANEL':'controls/panel/Panel', 'LOGO':'interface/logo/Logo', @@ -91,72 +60,125 @@ define(function (require) { getComponents: function(){ return this.componentsMap; }, + + camelize(str) { + return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) { + if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces + return index == 0 ? match.toUpperCase() : match.toLowerCase(); + }); + }, + + /** + * Get an available id for an specific widget + * + * @module WidgetUtility + * @param {String} prefix + * @param {Array} widgetsList + * @returns {String} id - Available id for a widget + */ + getAvailableComponentId (componentType) { + var index = 0; + var id = ""; + var available; + + var components = [] + if (componentType in this.componentsMap){ + components = this.componentsMap[componentType]; + } + + do { + index++; + id = componentType + index; + available = true; + + for (var componentsIndex in components) { + if (components[componentsIndex].props.id.toUpperCase() == id.toUpperCase()) { + available = false; + break; + } + } + } + while (available == false); + + return this.camelize(id); + }, addComponent: function(componentType, properties, container, callback){ var that=this; require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ + + if (properties === undefined){ + properties = {}; + } + + if (!("id" in properties)){ + properties["id"] = that.getAvailableComponentId(componentType); + } + var component = React.createFactory(loadedModule)(properties); - var renderedComponent = that.renderComponent(component, container); + var renderedComponent = window[properties.id] = that.renderComponent(component, container); if(callback!=undefined){ callback(renderedComponent); } // create id for the component being rendered - var componentID = that.createComponentID(componentType,1); + //var componentID = that.createComponentID(componentType,1); // assign unique id to component - renderedComponent.id = componentID; + //renderedComponent.id = componentID; // keep track of components in dictionary by id - that.componentsMap[componentID] = renderedComponent; + //that.componentsMap[componentID] = renderedComponent; // create autocomplete tags for the component - window[componentID] = renderedComponent; - GEPPETTO.Console.updateTags(componentID, renderedComponent); + //window[componentID] = renderedComponent; + if (!(componentType in that.componentsMap)){ + that.componentsMap[componentType] = [] + } + that.componentsMap[componentType].push(renderedComponent); + GEPPETTO.Console.updateTags(componentType, renderedComponent); return renderedComponent; }); }, - /**Creates unique ID's for the components being created*/ - createComponentID : function(componentType,index){ - var componentID = componentType.charAt(0).toUpperCase() + componentType.slice(1).toLowerCase() + index.toString(); - - if(componentID in this.componentsMap){ - return this.createComponentID(componentType, ++index); - } + addWidget: function(componentType, properties, callback){ - return componentID; - }, - - addWidget: function(componentID, properties, callback){ - - if (componentID in this.components){ - if (properties === undefined){ - properties = {}; - } - // if (componentID in this.componentsShortcut){ - // componentID = this.componentsShortcut[componentID]; - // } + if (componentType in this.components){ var that=this; - require(["./" + GEPPETTO.ComponentFactory.components[componentID]], function(loadedModule){ - - var widgetController = GEPPETTO.NewWidgetFactory.getController(componentID); + require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ + + if (properties === undefined){ + properties = {}; + } + // if (componentType in this.componentsShortcut){ + // componentType = this.componentsShortcut[componentType]; + // } + if (!("id" in properties)){ - properties["id"] = widgetController.getAvailableWidgetId(); + properties["id"] = that.getAvailableComponentId(componentType); } var component = React.createFactory(addWidget(loadedModule))(properties); var renderedComponent = window[properties.id] = that.renderComponent(component, document.getElementById('widgetContainer'), callback); - + + if (!(componentType in that.componentsMap)){ + that.componentsMap[componentType] = [] + } + that.componentsMap[componentType].push(renderedComponent); + + var widgetController = GEPPETTO.NewWidgetFactory.getController(componentType); widgetController.registerWidget(renderedComponent) return renderedComponent; }); } else{ - var newWidget = GEPPETTO.WidgetFactory.addWidget(componentID); - return newWidget; + var isStateless = false; + if (properties !== undefined && isStateless in properties){ + isStateless = properties["isStateless"]; + } + return GEPPETTO.WidgetFactory.addWidget(componentType, isStateless); } }, diff --git a/src/main/webapp/js/components/NewWidgetFactory.js b/src/main/webapp/js/components/NewWidgetFactory.js index 6b194e8d6..4d677944e 100644 --- a/src/main/webapp/js/components/NewWidgetFactory.js +++ b/src/main/webapp/js/components/NewWidgetFactory.js @@ -1,35 +1,3 @@ -/******************************************************************************* - * - * Copyright (c) 2011, 2016 OpenWorm. - * http://openworm.org - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the MIT License - * which accompanies this distribution, and is available at - * http://opensource.org/licenses/MIT - * - * Contributors: - * OpenWorm - http://openworm.org/people.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - *******************************************************************************/ - define(function (require) { return function (GEPPETTO) { diff --git a/src/main/webapp/js/components/WidgetController.js b/src/main/webapp/js/components/WidgetController.js index 2af2ed97c..b71488cb8 100644 --- a/src/main/webapp/js/components/WidgetController.js +++ b/src/main/webapp/js/components/WidgetController.js @@ -1,34 +1,3 @@ -/******************************************************************************* - * - * Copyright (c) 2011, 2016 OpenWorm. - * http://openworm.org - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the MIT License - * which accompanies this distribution, and is available at - * http://opensource.org/licenses/MIT - * - * Contributors: - * OpenWorm - http://openworm.org/people.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - *******************************************************************************/ class WidgetController { constructor(componentID) { @@ -44,12 +13,7 @@ class WidgetController { } } - camelize(str) { - return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) { - if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces - return index == 0 ? match.toUpperCase() : match.toLowerCase(); - }); - } + registerWidget(widget){ var that = this; @@ -113,36 +77,6 @@ class WidgetController { return comments; } - /** - * Get an available id for an specific widget - * - * @module WidgetUtility - * @param {String} prefix - * @param {Array} widgetsList - * @returns {String} id - Available id for a widget - */ - getAvailableWidgetId () { - var index = 0; - var id = ""; - var available; - - do { - index++; - id = this.componentID + index; - available = true; - - for (var p in this.widgets) { - if (this.widgets[p].getId().toUpperCase() == id.toUpperCase()) { - available = false; - break; - } - } - } - while (available == false); - - return this.camelize(id); - } - /** * Removes existing plotting widgets */ From 2233fbcfb90d6a473399e44a81d16239473fd9ec Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 18:24:01 +0100 Subject: [PATCH 150/339] remove comments and pass callback as a parameter to render --- .../webapp/js/components/ComponentFactory.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 87f9cab62..8f027bc9c 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -110,27 +110,13 @@ define(function (require) { if (properties === undefined){ properties = {}; } - if (!("id" in properties)){ properties["id"] = that.getAvailableComponentId(componentType); } var component = React.createFactory(loadedModule)(properties); - var renderedComponent = window[properties.id] = that.renderComponent(component, container); - if(callback!=undefined){ - callback(renderedComponent); - } - - // create id for the component being rendered - //var componentID = that.createComponentID(componentType,1); - // assign unique id to component - //renderedComponent.id = componentID; - - // keep track of components in dictionary by id - //that.componentsMap[componentID] = renderedComponent; + var renderedComponent = window[properties.id] = that.renderComponent(component, container, callback); - // create autocomplete tags for the component - //window[componentID] = renderedComponent; if (!(componentType in that.componentsMap)){ that.componentsMap[componentType] = [] } From b62c25957317b4ca81648e36bc8454a2a12125ec Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 2 May 2017 18:28:11 +0100 Subject: [PATCH 151/339] load connections before setting data if any data is saved in view-state --- .../js/components/widgets/connectivity/Connectivity.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index 32ac7f3a2..a9e9a6b43 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -449,7 +449,11 @@ define(function (require) { } } - this.setData(obj, deserializedOptions); + var that = this; + // resolve connections and pass the line below as a callback + Model.neuroml.resolveAllImportTypes(function(){ + that.setData(obj, deserializedOptions); + }); } // after setting view through setView, reset dirty flag From c0dc88070aa50b8ee48f8574d000693cf9062b4e Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 2 May 2017 18:43:46 +0100 Subject: [PATCH 152/339] merging redudant code in add widget and add component --- .../webapp/js/components/ComponentFactory.js | 52 +++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 8f027bc9c..b47e6bce2 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -102,8 +102,8 @@ define(function (require) { return this.camelize(id); }, - - addComponent: function(componentType, properties, container, callback){ + + _createComponent: function(componentType, properties, container, callback, isWidget){ var that=this; require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ @@ -114,50 +114,38 @@ define(function (require) { properties["id"] = that.getAvailableComponentId(componentType); } - var component = React.createFactory(loadedModule)(properties); + var type = loadedModule; + if (isWidget){ + type = addWidget(loadedModule); + } + var component = React.createFactory(type)(properties); var renderedComponent = window[properties.id] = that.renderComponent(component, container, callback); if (!(componentType in that.componentsMap)){ that.componentsMap[componentType] = [] } that.componentsMap[componentType].push(renderedComponent); - GEPPETTO.Console.updateTags(componentType, renderedComponent); + if (isWidget){ + var widgetController = GEPPETTO.NewWidgetFactory.getController(componentType); + widgetController.registerWidget(renderedComponent) + } + else{ + GEPPETTO.Console.updateTags(componentType, renderedComponent); + } + return renderedComponent; }); - + }, + + addComponent: function(componentType, properties, container, callback){ + this._createComponent(componentType, properties, container, callback, false); }, addWidget: function(componentType, properties, callback){ if (componentType in this.components){ - - var that=this; - require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ - - if (properties === undefined){ - properties = {}; - } - // if (componentType in this.componentsShortcut){ - // componentType = this.componentsShortcut[componentType]; - // } - - if (!("id" in properties)){ - properties["id"] = that.getAvailableComponentId(componentType); - } - - var component = React.createFactory(addWidget(loadedModule))(properties); - var renderedComponent = window[properties.id] = that.renderComponent(component, document.getElementById('widgetContainer'), callback); - - if (!(componentType in that.componentsMap)){ - that.componentsMap[componentType] = [] - } - that.componentsMap[componentType].push(renderedComponent); - - var widgetController = GEPPETTO.NewWidgetFactory.getController(componentType); - widgetController.registerWidget(renderedComponent) - return renderedComponent; - }); + this._createComponent(componentType, properties, document.getElementById('widgetContainer'), callback, true); } else{ var isStateless = false; From 98670647d4b83970f1b0a0b97620369a0102754a Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 2 May 2017 11:29:49 -0700 Subject: [PATCH 153/339] more detailed tests --- .../js/pages/tests/casperjs/CoreTests.js | 186 +++++++++++++++++- 1 file changed, 182 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index 27fbe1ab9..fc9500593 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -170,7 +170,7 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { }); }); -function hhcellTest(test){ +function hhcellTest(test,name){ casper.then(function () { this.echo("Opening controls panel"); this.evaluate(function() { @@ -188,7 +188,7 @@ function hhcellTest(test){ casper.evaluate(function(){ $("#stateVariablesFilterBtn").click(); - }) + }); casper.wait(500,function(){ this.evaluate(function(){ @@ -203,6 +203,50 @@ function hhcellTest(test){ }); this.echo("I've waited for Plot1 to come up"); test.assertEquals(plots, 1,"Amount of plot widgets in existence passed"); + + this.evaluate(function(){ + $("#anyProjectFilterBtn").click(); + }); + + this.wait(500,function(){ + var rows = casper.evaluate(function() { + var rows = $(".standard-row").length; + return rows; + }); + test.assertEquals(rows, 10, "Correct amount of rows for Global filter"); + + casper.evaluate(function() { + $("#controlpanel").hide(); + }); + + test.assertExists('i.fa-search', "Spotlight button exists") + this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + this.waitUntilVisible('div#spotlight', function () { + test.assertVisible('div#spotlight', "Spotlight opened"); + + //type in the spotlight + this.sendKeys('input#typeahead', "hhcell.hhpop[0].v", {keepFocus: true}); + //press enter + this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); + + casper.waitUntilVisible('div#spotlight', function () { + casper.then(function () { + this.echo("Waiting to see if the Plot variables button becomes visible"); + casper.waitUntilVisible('button#plot', function () { + test.assertVisible('button#plot', "Plot variables icon correctly visible"); + this.echo("Plot variables button became visible correctly"); + this.evaluate(function(){ + $("#plot").click(); + }); + this.waitUntilVisible('div[id="Plot2"]', function () { + this.echo("Plot 2 came up correctly"); + }); + }, null, 8000); + }); + }); + }); + }); }); }); }, null, 500); @@ -242,7 +286,48 @@ function c302Test(test){ this.waitUntilVisible('div[id="Plot2"]', function () { this.echo("I've waited for Plot2 to come up"); - test.assertEquals(plots, 2,"Amount of plot widgets in existence passed"); + this.evaluate(function(){ + $("#anyProjectFilterBtn").click(); + }); + this.wait(500,function(){ + var rows = casper.evaluate(function() { + var rows = $(".standard-row").length; + return rows; + }); + test.assertEquals(rows, 10, "Correct amount of rows for Global filter"); + + casper.evaluate(function() { + $("#controlpanel").hide(); + }); + + test.assertExists('i.fa-search', "Spotlight button exists") + this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + this.waitUntilVisible('div#spotlight', function () { + test.assertVisible('div#spotlight', "Spotlight opened"); + + //type in the spotlight + this.sendKeys('input#typeahead', "c302.ADAL[0].v", {keepFocus: true}); + //press enter + this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); + + casper.waitUntilVisible('div#spotlight', function () { + casper.then(function () { + this.echo("Waiting to see if the Plot variables button becomes visible"); + casper.waitUntilVisible('button#plot', function () { + test.assertVisible('button#plot', "Plot variables icon correctly visible"); + this.echo("Plot variables button became visible correctly"); + this.evaluate(function(){ + $("#plot").click(); + }); + this.waitUntilVisible('div[id="Plot3"]', function () { + this.echo("Plot 3 came up correctly"); + }); + }, null, 8000); + }); + }); + }); + }); }); }); }, null, 500); @@ -283,9 +368,41 @@ function acnetTest(test){ }); this.echo("I've waited for Plot1 to come up"); test.assertEquals(plots, 1,"Amount of plot widgets in existence passed"); + + casper.evaluate(function() { + $("#controlpanel").hide(); + }); + + test.assertExists('i.fa-search', "Spotlight button exists") + this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + this.waitUntilVisible('div#spotlight', function () { + test.assertVisible('div#spotlight', "Spotlight opened"); + + //type in the spotlight + this.sendKeys('input#typeahead', "acnet2.pyramidals_48[1].soma_0.v", {keepFocus: true}); + //press enter + this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); + + casper.waitUntilVisible('div#spotlight', function () { + casper.then(function () { + this.echo("Waiting to see if the Plot variables button becomes visible"); + casper.waitUntilVisible('button#plot', function () { + test.assertVisible('button#plot', "Plot variables icon correctly visible"); + this.echo("Plot variables button became visible correctly"); + this.evaluate(function(){ + $("#plot").click(); + }); + this.waitUntilVisible('div[id="Plot2"]', function () { + this.echo("Plot 2 came up correctly"); + }); + }, null, 8000); + }); + }); + }); }); }); - }, null, 500); + }, null, 1000); }); } @@ -297,6 +414,67 @@ function ca1Test(test){ test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial camera controls"); + + casper.then(function () { + this.echo("Opening controls panel"); + this.evaluate(function() { + $("#controlPanelBtn").click(); + }); + + this.waitUntilVisible('div#controlpanel', function () { + test.assertVisible('div#controlpanel', "The control panel is correctly open."); + + var rows = casper.evaluate(function() { + var rows = $(".standard-row").length; + return rows; + }); + test.assertEquals(rows, 3, "The control panel opened with right amount of rows"); + + this.evaluate(function(){ + $("#anyProjectFilterBtn").click(); + }); + + this.wait(500,function(){ + var rows = casper.evaluate(function() { + var rows = $(".standard-row").length; + return rows; + }); + test.assertEquals(rows, 3, "Correct amount of rows for Global filter"); + + casper.evaluate(function() { + $("#controlpanel").hide(); + }); + + test.assertExists('i.fa-search', "Spotlight button exists") + this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + this.waitUntilVisible('div#spotlight', function () { + test.assertVisible('div#spotlight', "Spotlight opened"); + + //type in the spotlight + this.sendKeys('input#typeahead', "ca1.CA1_CG[0].Seg0_apical_dendrite_22_1158.v", {keepFocus: true}); + //press enter + this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); + + casper.waitUntilVisible('div#spotlight', function () { + casper.then(function () { + this.echo("Waiting to see if the Plot variables button becomes visible"); + casper.waitUntilVisible('button#plot', function () { + test.assertVisible('button#plot', "Plot variables icon correctly visible"); + this.echo("Plot variables button became visible correctly"); +// this.evaluate(function(){ +// $("#plot").click(); +// }); +// this.waitUntilVisible('div[id="Plot1"]', function () { +// this.echo("Plot 1 came up correctly"); +// }, null, 20000); + }, null, 8000); + }); + }); + }); + }); + }, null, 500); + }); }, null, 30000); } From b23923b1943540ebf040771266a5cbc8df03fc0a Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 2 May 2017 21:14:31 +0100 Subject: [PATCH 154/339] Add method to display all entitities --- src/main/webapp/js/components/WidgetController.js | 4 ++-- .../js/components/interface/3dCanvas/Canvas.js | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/js/components/WidgetController.js b/src/main/webapp/js/components/WidgetController.js index b71488cb8..f390eff96 100644 --- a/src/main/webapp/js/components/WidgetController.js +++ b/src/main/webapp/js/components/WidgetController.js @@ -68,8 +68,8 @@ class WidgetController { * @param {String} file */ getFileComments(file) { - var fileContent = require("raw-loader!./" + GEPPETTO.ComponentFactory.components[this.componentID] + ".js"); - //var fileContent = ""; + //var fileContent = require("raw-loader!./" + GEPPETTO.ComponentFactory.components[this.componentID] + ".js"); + var fileContent = ""; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var comments = []; diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 776a011f6..3d2662379 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -24,11 +24,13 @@ define(function (require) { }, displayAllInstances: function () { - //TODO: Implement this which will add all models and start listening when new models are added or removed - //ON GEPPETTO.Events.Instances_created - //GEPPETTO.SceneController.updateSceneWithNewInstances(newInstances); - //ON GEPPETTO.Events.Instance_deleted - //GEPPETTO.SceneController.removeFromScene(instance); + var that = this; + GEPPETTO.on(GEPPETTO.Events.Instances_created,function(instances){ + that.engine.updateSceneWithNewInstances(instances); + }); + GEPPETTO.on(GEPPETTO.Events.Instance_deleted,function(instance){ + that.engine.removeFromScene(); + }); return this; }, From ee01c5eac3c8f0f43a96af535107b3078166c766 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 2 May 2017 21:35:18 +0100 Subject: [PATCH 155/339] Check if an instance exists --- .../interface/3dCanvas/ThreeDEngine.js | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index 05dc890bc..c3b4d14f2 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -663,14 +663,14 @@ define(['jquery'], function () { if (visualType.getMetaType() == GEPPETTO.Resources.COMPOSITE_VISUAL_TYPE_NODE) { for (var v in visualType.getVariables()) { var visualValue = visualType.getVariables()[v].getWrappedObj().initialValues[0].value; - threeDeeObj = this.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getVariables()[v].getId(), materials, lines); + threeDeeObj = this.create3DObjectFromInstance(instance, visualValue, visualType.getVariables()[v].getId(), materials, lines); if (threeDeeObj) { threeDeeObjList.push(threeDeeObj); } } } else { var visualValue = visualType.getWrappedObj().defaultValue; - threeDeeObj = this.visualizationTreeNodeTo3DObj(instance, visualValue, visualType.getId(), materials, lines); + threeDeeObj = this.create3DObjectFromInstance(instance, visualValue, visualType.getId(), materials, lines); if (threeDeeObj) { threeDeeObjList.push(threeDeeObj); } @@ -752,7 +752,7 @@ define(['jquery'], function () { * @param lines * @returns {*} */ - visualizationTreeNodeTo3DObj: function (instance, node, id, materials, lines) { + create3DObjectFromInstance: function (instance, node, id, materials, lines) { var threeObject = null; if (lines === undefined) { @@ -1405,6 +1405,9 @@ define(['jquery'], function () { * instancePath - Path of aspect of mesh to select */ selectInstance: function (instancePath, geometryIdentifier) { + if (!this.hasInstance(instancePath)) { + return; + } var that = this; if (geometryIdentifier != undefined && geometryIdentifier != "") { instancePath = instancePath + "." + geometryIdentifier; @@ -1493,6 +1496,9 @@ define(['jquery'], function () { * instancePath - Path of the mesh/aspect to select */ deselectInstance: function (instancePath) { + if (!this.hasInstance(instancePath)) { + return; + } var meshes = this.getRealMeshesForInstancePath(instancePath); if (meshes.length > 0) { for (var meshesIndex in meshes) { @@ -1681,6 +1687,9 @@ define(['jquery'], function () { * instancePath - Instance path of aspect to make visible */ showInstance: function (instancePath) { + if (!this.hasInstance(instancePath)) { + return; + } var meshes = this.getRealMeshesForInstancePath(instancePath); if (meshes.length > 0) { for (var i = 0; i < meshes.length; i++) { @@ -1701,6 +1710,9 @@ define(['jquery'], function () { * instancePath - Path of the aspect to make invisible */ hideInstance: function (instancePath) { + if (!this.hasInstance(instancePath)) { + return; + } var meshes = this.getRealMeshesForInstancePath(instancePath); for (var i = 0; i < meshes.length; i++) { var mesh = meshes[i]; @@ -1719,6 +1731,9 @@ define(['jquery'], function () { * instancePath - Instance path of aspect to change color */ setColor: function (instancePath, color) { + if (!this.hasInstance(instancePath)) { + return; + } var meshes = this.getRealMeshesForInstancePath(instancePath); if (meshes.length > 0) { for (var i = 0; i < meshes.length; i++) { @@ -1741,6 +1756,9 @@ define(['jquery'], function () { * @param instance */ assignRandomColor: function (instance) { + if (!this.hasInstance(instance)) { + return; + } var getRandomColor = function () { var letters = '0123456789ABCDEF'; var color = '0x'; @@ -1776,6 +1794,9 @@ define(['jquery'], function () { * instancePath - Instance path of aspect to change opacity for */ setOpacity: function (instancePath, opacity) { + if (!this.hasInstance(instancePath)) { + return; + } var mesh = this.meshes[instancePath]; if (mesh != undefined) { mesh.defaultOpacity = opacity; @@ -1822,6 +1843,9 @@ define(['jquery'], function () { * thickness - Optional: the thickness to be used if the geometry is "lines" */ setGeometryType: function (instance, type, thickness) { + if (!this.hasInstance(instance)) { + return; + } var lines = false; if (type === GEPPETTO.Resources.GeometryTypes.LINES) { lines = true; @@ -1862,6 +1886,9 @@ define(['jquery'], function () { * @param instance */ zoomToInstance: function (instance) { + if (!this.hasInstance(instance)) { + return; + } this.controls.reset(); var zoomParameters = {}; @@ -1885,7 +1912,9 @@ define(['jquery'], function () { * type - Type of connection, input or output (See GEPPETTO.Resources.INPUT/OUTPUT) */ highlightConnectedInstances: function (instance, type) { - + if (!this.hasInstance(instance)) { + return; + } var inputs = {}; var outputs = {}; @@ -1929,6 +1958,11 @@ define(['jquery'], function () { } }, + hasInstance: function (instance) { + var instancePath = typeof instance == "string" ? instance : instance.getInstancePath(); + return this.meshes[instancePath] != undefined; + }, + /** * Restore the original colour of the connected instances * @@ -1936,9 +1970,13 @@ define(['jquery'], function () { * instance - A connected instance */ restoreConnectedInstancesColour: function (instance) { + if (!this.hasInstance(instance)) { + return; + } var connections = instance.getConnections(); + for (var c = 0; c < connections.length; c++) { var connection = connections[c]; @@ -1946,6 +1984,8 @@ define(['jquery'], function () { this.meshes[connection.getB().getPath()] : this.meshes[connection.getA().getPath()]; + + // if mesh is not selected, give it ghost or default color and opacity if (!mesh.selected) { // if there are nodes still selected, give it a ghost effect. If not nodes are From 30346ac58b077f2f8d53d2ff4ddad1256745eb84 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 2 May 2017 18:42:52 -0700 Subject: [PATCH 156/339] adds a external unit controller, for adding units that Math.js don't handle, qunit test added too --- .../js/common/GEPPETTO.UnitsController.js | 46 ++++++++++++++++++ .../webapp/js/components/widgets/plot/Plot.js | 14 ++++-- src/main/webapp/js/pages/geppetto/GEPPETTO.js | 1 + .../webapp/js/pages/tests/qunit/CoreTests.js | 47 ++++++++++++++++++- 4 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 src/main/webapp/js/common/GEPPETTO.UnitsController.js diff --git a/src/main/webapp/js/common/GEPPETTO.UnitsController.js b/src/main/webapp/js/common/GEPPETTO.UnitsController.js new file mode 100644 index 000000000..29e70ea1f --- /dev/null +++ b/src/main/webapp/js/common/GEPPETTO.UnitsController.js @@ -0,0 +1,46 @@ + +/** + * Customizes units for Math.js library + * + */ +define(function(require) +{ + var math = require('mathjs'); + return function(GEPPETTO) + { + GEPPETTO.UnitsController = + { + unitsMap : [], + + /**Adds external unit to local map: + * unit : String representing unit to be added to map + * label : String used to associated the unit label with + * e.g "kilometer" for unit : "km" + */ + addUnit : function(unit,label){ + this.unitsMap[unit] = label; + }, + + getUnitLabel : function(unit){ + var label; + if(unit!=undefined && unit != null){ + label = this.unitsMap[unit]; + } + return label; + }, + + getUnitsMap : function(){ + return this.unitsMap; + }, + + hasUnit : function(unit){ + var match = this.unitsMap[unit]; + if(match!=undefined || match !=null){ + return true; + } + return false; + } + }; + + }; +}); diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index b55a7aaa4..55104f23e 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -835,11 +835,17 @@ define(function (require) { var unitLabel = unitSymbol; if (unitSymbol != undefined && unitSymbol != null && unitSymbol != "") { - var mathUnit = math.unit(1, unitSymbol); - - var formattedUnitName = (mathUnit.units.length > 0) ? mathUnit.units[0].unit.base.key : ""; - (mathUnit.units.length > 1) ? formattedUnitName += " OVER " + mathUnit.units[1].unit.base.key : ""; + var formattedUnitName = ""; + unitSymbol = unitSymbol.replace(/\s/g,''); + if(GEPPETTO.UnitsController.hasUnit(unitSymbol)){ + formattedUnitName =GEPPETTO.UnitsController.getUnitLabel(unitSymbol); + }else{ + var mathUnit = math.unit(1, unitSymbol); + formattedUnitName = (mathUnit.units.length > 0) ? mathUnit.units[0].unit.base.key : ""; + (mathUnit.units.length > 1) ? formattedUnitName += " OVER " + mathUnit.units[1].unit.base.key : ""; + } + if (formattedUnitName != "") { formattedUnitName = formattedUnitName.replace(/_/g, " "); formattedUnitName = formattedUnitName.charAt(0).toUpperCase() + formattedUnitName.slice(1).toLowerCase(); diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.js index 911ad988b..27484f77a 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.js @@ -449,6 +449,7 @@ define(function (require) { require('../../common/GEPPETTO.UserController')(GEPPETTO); require('./GEPPETTO.Flows')(GEPPETTO); require('../../common/GEPPETTO.ScriptRunner')(GEPPETTO); + require('../../common/GEPPETTO.UnitsController')(GEPPETTO); require('../../components/interface/jsEditor/GEPPETTO.JSEditor')(GEPPETTO); require('../../components/interface/jsConsole/GEPPETTO.Console')(GEPPETTO); require('../../common/GEPPETTO.Utility')(GEPPETTO); diff --git a/src/main/webapp/js/pages/tests/qunit/CoreTests.js b/src/main/webapp/js/pages/tests/qunit/CoreTests.js index f9d79d8ec..6f08a6a47 100644 --- a/src/main/webapp/js/pages/tests/qunit/CoreTests.js +++ b/src/main/webapp/js/pages/tests/qunit/CoreTests.js @@ -7,7 +7,7 @@ */ define(function (require) { var QUnit = require("qunitjs"); - + require('../../../components/ComponentFactory')(GEPPETTO); /** * Closes socket and clears handlers. Method is called from each test. */ @@ -75,6 +75,51 @@ define(function (require) { assert.equal($("#" + pop.getId()).html(), null, "Test destroy()"); }); + + QUnit.test("Test Plot Widget", function (assert) { + G.addWidget(Widgets.PLOT); + + assert.equal(GEPPETTO.WidgetFactory.getController(Widgets.PLOT).getWidgets().length, 1, "Plot widget."); + + var plot = GEPPETTO.WidgetFactory.getController(GEPPETTO.Widgets.PLOT).getWidgets()[0]; + + assert.equal(plot.isVisible(), true, "Test Default Visibility"); + + plot.hide(); + + assert.equal(plot.isVisible(), false, "Test hide()"); + + plot.show(); + + assert.equal(plot.isVisible(), true, "Test show()"); + + plot.destroy(); + + assert.equal($("#" + plot.getId()).html(), null, "Test destroy()"); + }); + + QUnit.test("Test UnitsControllers", function (assert) { + G.addWidget(Widgets.PLOT); + + assert.equal(GEPPETTO.WidgetFactory.getController(Widgets.PLOT).getWidgets().length, 1, "Plot widget."); + + var plot = GEPPETTO.WidgetFactory.getController(GEPPETTO.Widgets.PLOT).getWidgets()[0]; + + assert.equal(plot.isVisible(), true, "Test Default Visibility"); + + var initialLabel = plot.getUnitLabel("S / m2"); + + assert.equal(initialLabel,"Electric conductance over surface (S/m2)", "Test Math.js unit"); + + GEPPETTO.UnitsController.addUnit("S/m2","Electric conductance OVER density"); + initialLabel = plot.getUnitLabel("S / m2"); + + assert.equal(initialLabel,"Electric conductance over density (S/m2)", "Test Math.js unit"); + + plot.destroy(); + + assert.equal($("#" + plot.getId()).html(), null, "Test destroy()"); + }); QUnit.test("Test VARIABLEVISUALISER Widget", function (assert) { G.addWidget(Widgets.VARIABLEVISUALISER); From 3ee9d9f9e09a7ec3422814b6a952bfea0280102a Mon Sep 17 00:00:00 2001 From: johnidol Date: Wed, 3 May 2017 11:04:23 +0100 Subject: [PATCH 157/339] remove double return statement (I don't like them unless they are necessary, in particular in very small methods) --- src/main/webapp/js/common/GEPPETTO.UnitsController.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.UnitsController.js b/src/main/webapp/js/common/GEPPETTO.UnitsController.js index 29e70ea1f..201fdd9d8 100644 --- a/src/main/webapp/js/common/GEPPETTO.UnitsController.js +++ b/src/main/webapp/js/common/GEPPETTO.UnitsController.js @@ -34,11 +34,14 @@ define(function(require) }, hasUnit : function(unit){ + var hasUnit = false; + var match = this.unitsMap[unit]; if(match!=undefined || match !=null){ - return true; + hasUnit = true; } - return false; + + return hasUnit; } }; From 58b61711db646079f068fa165f610705aa2b54c2 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 3 May 2017 09:17:06 -0700 Subject: [PATCH 158/339] allows entering units to controller with extra white spaces --- src/main/webapp/js/common/GEPPETTO.UnitsController.js | 10 ++++++++-- src/main/webapp/js/components/widgets/plot/Plot.js | 1 - src/main/webapp/js/pages/tests/qunit/CoreTests.js | 6 +++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.UnitsController.js b/src/main/webapp/js/common/GEPPETTO.UnitsController.js index 29e70ea1f..181ff789c 100644 --- a/src/main/webapp/js/common/GEPPETTO.UnitsController.js +++ b/src/main/webapp/js/common/GEPPETTO.UnitsController.js @@ -18,11 +18,13 @@ define(function(require) * e.g "kilometer" for unit : "km" */ addUnit : function(unit,label){ + unit = unit.replace(/\s/g,''); this.unitsMap[unit] = label; }, getUnitLabel : function(unit){ var label; + unit = unit.replace(/\s/g,''); if(unit!=undefined && unit != null){ label = this.unitsMap[unit]; } @@ -34,11 +36,15 @@ define(function(require) }, hasUnit : function(unit){ + unit = unit.replace(/\s/g,''); + + var hasUnit = false; var match = this.unitsMap[unit]; if(match!=undefined || match !=null){ - return true; + hasUnit = true; } - return false; + + return hasUnit; } }; diff --git a/src/main/webapp/js/components/widgets/plot/Plot.js b/src/main/webapp/js/components/widgets/plot/Plot.js index 55104f23e..c08f8128a 100644 --- a/src/main/webapp/js/components/widgets/plot/Plot.js +++ b/src/main/webapp/js/components/widgets/plot/Plot.js @@ -836,7 +836,6 @@ define(function (require) { if (unitSymbol != undefined && unitSymbol != null && unitSymbol != "") { var formattedUnitName = ""; - unitSymbol = unitSymbol.replace(/\s/g,''); if(GEPPETTO.UnitsController.hasUnit(unitSymbol)){ formattedUnitName =GEPPETTO.UnitsController.getUnitLabel(unitSymbol); }else{ diff --git a/src/main/webapp/js/pages/tests/qunit/CoreTests.js b/src/main/webapp/js/pages/tests/qunit/CoreTests.js index 6f08a6a47..88e8a8fb8 100644 --- a/src/main/webapp/js/pages/tests/qunit/CoreTests.js +++ b/src/main/webapp/js/pages/tests/qunit/CoreTests.js @@ -109,11 +109,15 @@ define(function (require) { var initialLabel = plot.getUnitLabel("S / m2"); - assert.equal(initialLabel,"Electric conductance over surface (S/m2)", "Test Math.js unit"); + assert.equal(initialLabel,"Electric conductance over surface (S / m2)", "Test Math.js unit"); GEPPETTO.UnitsController.addUnit("S/m2","Electric conductance OVER density"); initialLabel = plot.getUnitLabel("S / m2"); + assert.equal(initialLabel,"Electric conductance over density (S / m2)", "Test Math.js unit"); + + initialLabel = plot.getUnitLabel("S/m2"); + assert.equal(initialLabel,"Electric conductance over density (S/m2)", "Test Math.js unit"); plot.destroy(); From 1cbc1c283822560b469082bddf8c808476f2a07a Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Wed, 3 May 2017 17:59:33 +0100 Subject: [PATCH 159/339] Canvas initialization in default extension --- .../extensions/geppetto-default/ComponentsInitialization.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index 81de2eff1..86a527738 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -17,6 +17,11 @@ define(function (require) { return [1, (1-(x-0.75)*4), 0]; } }; + + //Canvas initialisation + GEPPETTO.ComponentFactory.addComponent('CANVAS3D', {}, document.getElementById("sim"), function () { + this.displayAllInstances(); + }); //Logo initialization GEPPETTO.ComponentFactory.addComponent('LOGO', {logo: 'gpt-gpt_logo'}, document.getElementById("geppettologo")); From 992a237146dd45940a00fddd9088645f208c3239 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 3 May 2017 12:40:07 -0700 Subject: [PATCH 160/339] don't show plot button for state variables if not recorded, don't show watch for non modifiable projects --- .../interface/spotlight/spotlight.js | 20 ++++++++++++++++--- .../js/pages/tests/casperjs/CoreTests.js | 6 ------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/webapp/js/components/interface/spotlight/spotlight.js b/src/main/webapp/js/components/interface/spotlight/spotlight.js index fd57d6347..636970f0d 100644 --- a/src/main/webapp/js/components/interface/spotlight/spotlight.js +++ b/src/main/webapp/js/components/interface/spotlight/spotlight.js @@ -926,17 +926,31 @@ define(function (require) { if ((instanceToCheck.getCapabilities().indexOf(groupName) != -1) || (instanceToCheck.getType().getMetaType() == groupName)) { if(modifiable){ - tbar.append(that.createButtonGroup(groupName, groupDef, instance)); + var copiedObject = jQuery.extend({}, groupDef); + delete copiedObject["plot"]; + tbar.append(that.createButtonGroup(groupName, copiedObject, instance)); }else{ //don't load default toolbar for these two types if modifiable flag set to false, if(groupName!="StateVariableCapability" && groupName!="ParameterCapability"){ - tbar.append(that.createButtonGroup(groupName, groupDef, instance)); + if(modifiable){ + var copiedObject = jQuery.extend({}, groupDef); + delete copiedObject["plot"]; + delete copiedObject["plot"]; + } + tbar.append(that.createButtonGroup(groupName, copiedObject, instance)); }else{ //don't show watch button if no permissions if(groupName == "StateVariableCapability"){ //make copy since modifying original removes it through session var copiedObject = jQuery.extend({}, groupDef); - delete copiedObject["watch"]; + if(instanceToCheck.watched){ + delete copiedObject["watch"]; + }else{ + if(!modifiable){ + delete copiedObject["watch"]; + } + delete copiedObject["plot"]; + } tbar.append(that.createButtonGroup(groupName, copiedObject, instance)); } } diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index fc9500593..c47446a08 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -462,12 +462,6 @@ function ca1Test(test){ casper.waitUntilVisible('button#plot', function () { test.assertVisible('button#plot', "Plot variables icon correctly visible"); this.echo("Plot variables button became visible correctly"); -// this.evaluate(function(){ -// $("#plot").click(); -// }); -// this.waitUntilVisible('div[id="Plot1"]', function () { -// this.echo("Plot 1 came up correctly"); -// }, null, 20000); }, null, 8000); }); }); From b1a97c203dead507e42500856ceeb84d622faca2 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 3 May 2017 14:15:18 -0700 Subject: [PATCH 161/339] adding test to acnet --- .../js/pages/tests/casperjs/CoreTests.js | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index c47446a08..e464244c9 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -71,7 +71,7 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); c302Test(test); - },null,30000); + },null,450000); }); /**Tests Acnet project**/ @@ -389,6 +389,7 @@ function acnetTest(test){ this.echo("Waiting to see if the Plot variables button becomes visible"); casper.waitUntilVisible('button#plot', function () { test.assertVisible('button#plot', "Plot variables icon correctly visible"); + test.assertDoesntExist('button#watch', "Watch button correctly hidden"); this.echo("Plot variables button became visible correctly"); this.evaluate(function(){ $("#plot").click(); @@ -399,6 +400,31 @@ function acnetTest(test){ }, null, 8000); }); }); + + this.mouseEvent('click', 'i.fa-search', "attempting to close spotlight"); + + casper.waitWhileVisible(function(){ + this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + this.waitUntilVisible('div#spotlight', function () { + test.assertVisible('div#spotlight', "Spotlight opened"); + + //type in the spotlight + this.sendKeys('input#typeahead', + "acnet2.pyramidals_48[0].biophys.membraneProperties.Ca_pyr_soma_group.gDensity", {keepFocus: true}); + //press enter + this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); + + casper.wait(1000, function () { + casper.then(function () { + this.echo("Waiting to see if the Plot and watch variable buttons becomes visible"); + test.assertDoesntExist('button#plot', "Plot variables icon correctly invisible"); + test.assertDoesntExist('button#watch', "Watch button correctly hidden"); + this.echo("Variables button are hidden correctly"); + }); + }); + }); + },null,1000); }); }); }); From 86125deaf9159478e2939584ba4edb8c12c752be Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 15:09:59 +0100 Subject: [PATCH 162/339] More canvas refactoring. --- .../frontend/messages/OutboundMessages.java | 1 - .../messages/TransportMessageFactory.java | 2 - .../webapp/js/common/GEPPETTO.Resources.js | 36 -- .../communication/GEPPETTO.GlobalHandler.js | 32 +- .../webapp/js/communication/MessageHandler.js | 37 +- .../webapp/js/communication/MessageSocket.js | 5 +- .../controls/modals/ModalFactory.js | 119 +++++++ .../components/interface/3dCanvas/Canvas.js | 86 +++-- .../interface/3dCanvas/ColorController.js | 201 +++++++++++ .../3dCanvas/GEPPETTO.SceneController.js | 274 -------------- .../interface/3dCanvas/SceneController.js | 336 ++++++++++++++++++ .../interface/3dCanvas/ThreeDEngine.js | 232 +++++++++--- .../experimentsTable/ExperimentsTable.js | 12 +- .../webapp/js/geppettoModel/ModelFactory.js | 3 - .../geppettoModel/model/VisualGroupElement.js | 29 +- .../geppettoProject/ExperimentsController.js | 4 +- .../js/geppettoProject/model/ProjectNode.js | 2 +- src/main/webapp/js/pages/geppetto/G.js | 325 ++--------------- .../js/pages/geppetto/GEPPETTO.Events.js | 100 +++--- .../webapp/js/pages/geppetto/GEPPETTO.FE.js | 207 ----------- .../webapp/js/pages/geppetto/GEPPETTO.Init.js | 65 ---- .../webapp/js/pages/geppetto/GEPPETTO.Main.js | 86 +++-- src/main/webapp/js/pages/geppetto/GEPPETTO.js | 14 +- src/main/webapp/js/pages/geppetto/main.js | 32 -- src/main/webapp/webpack.config.js | 6 +- 25 files changed, 1106 insertions(+), 1140 deletions(-) create mode 100644 src/main/webapp/js/components/controls/modals/ModalFactory.js create mode 100644 src/main/webapp/js/components/interface/3dCanvas/ColorController.js delete mode 100644 src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js create mode 100644 src/main/webapp/js/components/interface/3dCanvas/SceneController.js delete mode 100644 src/main/webapp/js/pages/geppetto/GEPPETTO.FE.js delete mode 100644 src/main/webapp/js/pages/geppetto/GEPPETTO.Init.js diff --git a/src/main/java/org/geppetto/frontend/messages/OutboundMessages.java b/src/main/java/org/geppetto/frontend/messages/OutboundMessages.java index 02c10aff0..ff469aba7 100644 --- a/src/main/java/org/geppetto/frontend/messages/OutboundMessages.java +++ b/src/main/java/org/geppetto/frontend/messages/OutboundMessages.java @@ -13,7 +13,6 @@ public enum OutboundMessages { SERVER_AVAILABLE("server_available"), EXPERIMENT_RUNNING("experiment_running"), INFO_MESSAGE("info_message"), - RELOAD_CANVAS("reload_canvas"), SIMULATION_CONFIGURATION("simulation_configuration"), ERROR("generic_error"), ERROR_RUNNING_EXPERIMENT("error_running_experiment"), diff --git a/src/main/java/org/geppetto/frontend/messages/TransportMessageFactory.java b/src/main/java/org/geppetto/frontend/messages/TransportMessageFactory.java index 6e6a2a412..438d7dca6 100644 --- a/src/main/java/org/geppetto/frontend/messages/TransportMessageFactory.java +++ b/src/main/java/org/geppetto/frontend/messages/TransportMessageFactory.java @@ -34,8 +34,6 @@ public static GeppettoTransportMessage getTransportMessage(String requestID, Out List> params = new ArrayList>(); switch(type){ - case RELOAD_CANVAS: - break; case ERROR: params.add(new SimpleEntry("message", update)); break; diff --git a/src/main/webapp/js/common/GEPPETTO.Resources.js b/src/main/webapp/js/common/GEPPETTO.Resources.js index 073af4cdd..0fb202a50 100644 --- a/src/main/webapp/js/common/GEPPETTO.Resources.js +++ b/src/main/webapp/js/common/GEPPETTO.Resources.js @@ -1,36 +1,3 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2011, 2013 OpenWorm. - * http://openworm.org - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the MIT License - * which accompanies this distribution, and is available at - * http://opensource.org/licenses/MIT - * - * Contributors: - * OpenWorm - http://openworm.org/people.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - *******************************************************************************/ - /** * * Global class that stores resource strings @@ -318,9 +285,6 @@ define(function (require) { UNACTIVE_EXPERIMENT_UPLOAD: "Unable to upload results for experiment that isn't active", - /** - * GEPPETTO.Main resources - */ GEPPETTO_INITIALIZED: 'Geppetto Initialised', SIMULATION_OBSERVED: 'Sent: Simulation being observed', diff --git a/src/main/webapp/js/communication/GEPPETTO.GlobalHandler.js b/src/main/webapp/js/communication/GEPPETTO.GlobalHandler.js index be5087d3c..92e2300eb 100644 --- a/src/main/webapp/js/communication/GEPPETTO.GlobalHandler.js +++ b/src/main/webapp/js/communication/GEPPETTO.GlobalHandler.js @@ -1,4 +1,3 @@ - /** * Handles general incoming messages, excluding Simulation */ @@ -8,11 +7,7 @@ define(function (require) { var messageTypes = { - /* - * Messages handle by GlobalHandler - */ CLIENT_ID: "client_id", - RELOAD_CANVAS: "reload_canvas", ERROR_LOADING_SIM: "error_loading_simulation", ERROR_LOADING_PROJECT: "error_loading_project", ERROR_DOWNLOADING_MODEL: "error_downloading_model", @@ -41,44 +36,31 @@ define(function (require) { GEPPETTO.UserController.setUserPrivileges(user_privileges); }; - // clear canvas, used when loading a new model or re-loading previous - // one - messageHandler[messageTypes.RELOAD_CANVAS] = function () { - GEPPETTO.Console.debugLog(GEPPETTO.Resources.CLEAR_CANVAS); - var webGLStarted = GEPPETTO.init(GEPPETTO.FE.createContainer()); - var webWorkersSupported = (typeof(Worker) !== "undefined") ? true : false; - - if (!webGLStarted || !webWorkersSupported) { - GEPPETTO.FE.notifyInitErrors(webGLStarted, webWorkersSupported); - GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); - } - }; - // Error loading simulation, invalid url or simulation file messageHandler[messageTypes.ERROR_LOADING_SIM] = function (payload) { GEPPETTO.trigger('geppetto:error', payload.message); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.INVALID_SIMULATION_FILE, payload.message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.INVALID_SIMULATION_FILE, payload.message); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; // Error loading simulation, invalid url or simulation file messageHandler[messageTypes.ERROR_LOADING_PROJECT] = function (payload) { GEPPETTO.trigger('geppetto:error', payload.message); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.ERROR_LOADING_PROJECT, payload.message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.ERROR_LOADING_PROJECT, payload.message); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; // Error loading simulation, invalid url or simulation file messageHandler[messageTypes.ERROR_DOWNLOADING_MODEL] = function (payload) { GEPPETTO.trigger('geppetto:error', payload.message); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.ERROR_DOWNLOADING_MODEL, payload.message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.ERROR_DOWNLOADING_MODEL, payload.message); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; // Error loading simulation, invalid url or simulation file messageHandler[messageTypes.ERROR_DOWNLOADING_RESULTS] = function (payload) { GEPPETTO.trigger('geppetto:error', payload.message); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.ERROR_DOWNLOADING_RESULTS, payload.message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.ERROR_DOWNLOADING_RESULTS, payload.message); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; @@ -86,14 +68,14 @@ define(function (require) { messageHandler[messageTypes.INFO_MESSAGE] = function (payload) { var message = JSON.parse(payload.message); GEPPETTO.trigger('geppetto:info', message); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.INCOMING_MESSAGE, message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.INCOMING_MESSAGE, message); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; messageHandler[messageTypes.ERROR] = function (payload) { var error = JSON.parse(payload.message); GEPPETTO.trigger('geppetto:error', error.msg); - GEPPETTO.FE.errorDialog(GEPPETTO.Resources.ERROR, error.message, error.code, error.exception); + GEPPETTO.ModalFactory.errorDialog(GEPPETTO.Resources.ERROR, error.message, error.code, error.exception); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; @@ -114,7 +96,7 @@ define(function (require) { // Simulation server became available messageHandler[messageTypes.SERVER_AVAILABLE] = function (payload) { - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.SERVER_AVAILABLE, payload.message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.SERVER_AVAILABLE, payload.message); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index fe8a73fe7..cf017c57d 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -73,7 +73,7 @@ define(function(require) { } GEPPETTO.trigger('geppetto:error', error.msg); - GEPPETTO.FE.errorDialog(GEPPETTO.Resources.ERROR, error.message, error.code, error.exception); + GEPPETTO.ModalFactory.errorDialog(GEPPETTO.Resources.ERROR, error.message, error.code, error.exception); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); }; @@ -193,7 +193,7 @@ define(function(require) { //handles the case where service doesn't support feature and shows message messageHandler[messageTypes.NO_FEATURE] = function() { //Updates the simulation controls visibility - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.NO_FEATURE, payload.message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.NO_FEATURE, payload.message); }; //received model tree from server @@ -276,6 +276,10 @@ define(function(require) { } }, + /** + * + * @param payload + */ persistProject: function(payload) { var message = JSON.parse(payload.update); var projectID = message.projectID; @@ -292,6 +296,10 @@ define(function(require) { GEPPETTO.Console.log("The project has been persisted [id=" + projectID + "]."); }, + /** + * + * @param payload + */ loadProject: function(payload) { // we remove anything from any previous loaded project if there was one GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.LOADING_PROJECT); @@ -306,11 +314,15 @@ define(function(require) { window.Project = GEPPETTO.ProjectFactory.createProjectNode(project, persisted); window.Project.readOnly = update.isReadOnly; - GEPPETTO.Init.initEventListeners(); + GEPPETTO.trigger(GEPPETTO.Events.Project_loaded); GEPPETTO.Console.log(GEPPETTO.Resources.PROJECT_LOADED); }, + /** + * + * @param payload + */ loadModel: function(payload) { console.time(GEPPETTO.Resources.PARSING_MODEL); @@ -438,6 +450,11 @@ define(function(require) { GEPPETTO.Console.log(GEPPETTO.Resources.IMPORT_TYPE_RESOLVED); }, + /** + * + * @param typePath + * @param callback + */ resolveImportValue: function(typePath, callback) { var params = {}; params["experimentId"] = Project.getActiveExperiment().getId(); @@ -463,6 +480,7 @@ define(function(require) { GEPPETTO.Console.log(GEPPETTO.Resources.IMPORT_VALUE_RESOLVED); }, + /** * Augments the instances array with some utilities methods for ease of access */ @@ -549,6 +567,10 @@ define(function(require) { }; }, + /** + * + * @param payload + */ loadExperiment: function(payload) { console.time(GEPPETTO.Resources.LOADING_EXPERIMENT); @@ -575,6 +597,11 @@ define(function(require) { console.timeEnd(GEPPETTO.Resources.LOADING_EXPERIMENT); }, + /** + * + * @param payload + * @returns {*} + */ createExperiment: function(payload) { var experiment = JSON.parse(payload.experiment_created); @@ -592,6 +619,10 @@ define(function(require) { return newExperiment; }, + /** + * + * @param payload + */ deleteExperiment: function(payload) { var data = JSON.parse(payload.update); var experiment = null; diff --git a/src/main/webapp/js/communication/MessageSocket.js b/src/main/webapp/js/communication/MessageSocket.js index e514ffba5..aad7d0847 100644 --- a/src/main/webapp/js/communication/MessageSocket.js +++ b/src/main/webapp/js/communication/MessageSocket.js @@ -75,9 +75,6 @@ define(function (require) { } GEPPETTO.MessageSocket.socket.onopen = function () { - //enable load simulation button, and welcome message buttons - GEPPETTO.FE.postSocketConnection(); - GEPPETTO.Console.debugLog(GEPPETTO.Resources.WEBSOCKET_OPENED); //attach the handlers once socket is opened @@ -118,7 +115,7 @@ define(function (require) { GEPPETTO.MessageSocket.failsafe = true; GEPPETTO.MessageSocket.connect(GEPPETTO.MessageSocket.protocol + window.location.host + '/' + window.BUNDLE_CONTEXT_PATH + '/GeppettoServlet'); } else { - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.WEBSOCKET_CONNECTION_ERROR, message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.WEBSOCKET_CONNECTION_ERROR, message); } }; }, diff --git a/src/main/webapp/js/components/controls/modals/ModalFactory.js b/src/main/webapp/js/components/controls/modals/ModalFactory.js new file mode 100644 index 000000000..387323fd4 --- /dev/null +++ b/src/main/webapp/js/components/controls/modals/ModalFactory.js @@ -0,0 +1,119 @@ +define(['jquery'], function () { + + var React = require('react'); + var InfoModal = require('./InfoModal'); + var ErrorModal = require('./ErrorModal'); + var InputModal = require('./InputModal'); + var ReactDOM = require('react-dom'); + + + function ModalFactory() { + /* + * Due to a bootstrap bug, multiple modals can't be open at same + * time. This line allows multiple modals to be open + * simultaneously without going in an infinite loop. + */ + $.fn.modal.Constructor.prototype.enforceFocus = function () { + }; + }; + + ModalFactory.prototype = + { + + constructor: ModalFactory, + + /** + * Basic Dialog box with message to display. + * + * @method + * + * @param title - + * Title of message + * @param msg - + * Message to display + */ + infoDialog: function (title, msg) { + var infoFactory = React.createFactory(InfoModal); + + ReactDOM.render( + infoFactory( + { + show: true, + keyboard: false, + title: title, + text: msg, + }), + + document.getElementById('modal-region') + ); + }, + + + /** + * Basic Dialog box with message to display. + * + * @method + * + * @param title - + * Title of message + * @param msg - + * Message to display + */ + inputDialog: function (title, msg, aLabel, aClick, bLabel, bClick) { + var infoFactory = React.createFactory(InputModal); + + ReactDOM.render( + infoFactory( + { + show: true, + keyboard: false, + title: title, + text: msg, + aLabel: aLabel, + aClick: aClick, + bLabel: bLabel, + bClick: bClick + }), + + document.getElementById('modal-region') + ); + }, + + /** + * Dialog box to display error messages. + * + * @method + * + * @param title - + * Notifying error + * @param msg - + * Message to display for error + * @param code - + * Error code of message + * @param source - + * Source error to display + * @param exception - + * Exception to display + */ + errorDialog: function (title, message, code, exception) { + var errorModalFactory = React.createFactory(ErrorModal); + + ReactDOM.render( + errorModalFactory( + { + show: true, + keyboard: false, + title: title, + message: message, + code: code, + exception: exception + }), + document.getElementById('modal-region') + ); + } + + + }; + + return ModalFactory; +}); diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 3d2662379..8def24dc2 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -17,20 +17,28 @@ define(function (require) { container: null, backgroundColor: 0x101010, - + /** + * Displays all the passed instances in this canvas component + * @param instances an array of instances + * @returns {canvasComponent} + */ display: function (instances) { this.engine.buildScene(instances); return this; }, + /** + * Displays all the instances available in the current model in this canvas + * @returns {canvasComponent} + */ displayAllInstances: function () { var that = this; - GEPPETTO.on(GEPPETTO.Events.Instances_created,function(instances){ - that.engine.updateSceneWithNewInstances(instances); - }); - GEPPETTO.on(GEPPETTO.Events.Instance_deleted,function(instance){ - that.engine.removeFromScene(); - }); + GEPPETTO.on(GEPPETTO.Events.Instances_created, function (instances) { + that.engine.updateSceneWithNewInstances(instances); + }); + GEPPETTO.on(GEPPETTO.Events.Instance_deleted, function (instance) { + that.engine.removeFromScene(instance); + }); return this; }, @@ -45,17 +53,22 @@ define(function (require) { return this; }, + /** - * Deselects an instance - * - * @param {String} instancePath - Path of instance to select - * @param {String} geometryIdentifier - Identifier of the geometry that was clicked + * Deselects an instance given its path + * @param instancePath + * @returns {canvasComponent} */ deselectInstance: function (instancePath) { this.engine.deselectInstance(instancePath); return this; }, + /** + * + * @param instance + * @returns {canvasComponent} + */ assignRandomColor: function (instance) { this.engine.assignRandomColor(instance); return this; @@ -71,7 +84,7 @@ define(function (require) { }, /** - * Sets whether to use wireframe or not to visualize any instance. + * Sets whether to use wireframe or not to visualize any instance. */ setWireframe: function (wireframe) { this.engine.setWireframe(wireframe); @@ -131,7 +144,7 @@ define(function (require) { * @param threshold */ setLinesThreshold: function (threshold) { - this.engine.setLinesThreshold(ithreshold); + this.engine.setLinesThreshold(threshold); return this; }, @@ -160,17 +173,35 @@ define(function (require) { }, /** - * Light up the passed instance + * Associate a color function to a group of instances * - * @param {Instance} - * instance - the instance to be lit - * @param {Instance} - * colorfn - a function to map the intensity to a color - * @param {Float} - * intensity - the lighting intensity from 0 (no illumination) to 1 (full illumination) + * @param instances - The instances we want to change the color of + * @param colorfn - The function to be used to modulate the color + * @return {canvasComponent} */ - lightUpEntity: function (instance, colorfn, intensity) { - this.engine.lightUpEntity(instance, colorfn, intensity); + addColorFunction: function (instances, colorfn) { + this.engine.colorController.addColorFunction(instances, colorfn); + return this; + }, + + /** + * Remove a previously associated color function + * + * @param instances + * @return {canvasComponent} + */ + removeColorFunction: function (instances) { + this.engine.colorController.removeColorFunction(instances); + return this; + }, + + /** + * Shows the visual groups associated to the passed instance + * @param instance + * @returns {canvasComponent} + */ + showVisualGroupsForInstance: function (instance) { + this.engine.showVisualGroupsForInstance(instance); return this; }, @@ -232,7 +263,9 @@ define(function (require) { }, /** - * Reset camera + * Resets the camera + * + * @returns {canvasComponent} */ resetCamera: function () { this.engine.resetCamera(); @@ -252,7 +285,10 @@ define(function (require) { return [width, height]; }, - + /** + * + * @returns {boolean} + */ shouldComponentUpdate() { return false; }, @@ -277,7 +313,7 @@ define(function (require) { render: function () { return (
                - +
                ) } diff --git a/src/main/webapp/js/components/interface/3dCanvas/ColorController.js b/src/main/webapp/js/components/interface/3dCanvas/ColorController.js new file mode 100644 index 000000000..aa2dab622 --- /dev/null +++ b/src/main/webapp/js/components/interface/3dCanvas/ColorController.js @@ -0,0 +1,201 @@ +/** + * This controller is used by the 3d engine to modulate the color of the meshes + */ +define(['jquery'], function () { + + + function ColorController(engine) { + this.engine = engine; + this.listeners = {}; + this.litUpInstances = []; + this.colorFunctionSet = false; + + }; + + + ColorController.prototype = { + + constructor: ColorController, + + isColorFunctionSet: function () { + return this.colorFunctionSet; + }, + + /** + * Callback to be called whenever a watched node changes + * + * @param {Instance} node - node to couple callback to + * @param {Function} callback - Callback function to be called whenever _variable_ changes + */ + addOnNodeUpdatedCallback: function (node, callback) { + if (node != null || undefined) { + if (!this.listeners[node.getInstancePath()]) { + this.listeners[node.getInstancePath()] = []; + } + this.listeners[node.getInstancePath()].push(callback); + } + }, + + /** + * Clears callbacks coupled to changes in a node + * + * @param {Instance} node - node to which callbacks are coupled + */ + clearOnNodeUpdateCallback: function (node) { + this.listeners[node.getInstancePath()] = null; + }, + + + /** + * Modulates the color of an instance given a color function. + * The color function should receive the value of the watched node and output [r,g,b]. + * + * @param {Instance} instance - The instance to be lit + * @param {Instance} modulation - Variable which modulates the color + * @param {Function} colorfn - Converts time-series value to [r,g,b] + */ + addColorFunction: function (instances, colorfn) { + // Check if instance is instance + visualObjects or instance (hhcell.hhpop[0].soma or hhcell.hhpop[0]) + for (var i = 0; i < instances.length; ++i) { + this.litUpInstances.push(instances[i]); + } + var compositeToLit = {}; + var visualObjectsToLit = {}; + var variables = {}; + var currentCompositePath = undefined; + + for (var i = 0; i < instances.length; i++) { + var composite = undefined; + var multicompartment = false; + + composite = instances[i].getParent(); + + while (composite.getMetaType() != GEPPETTO.Resources.ARRAY_ELEMENT_INSTANCE_NODE) { + if (composite.getParent() == null) { + throw "Unsupported model to use this function"; + } else { + composite = composite.getParent(); + multicompartment = true; + } + } + + var currentCompositePath = composite.getInstancePath(); + if (!compositeToLit.hasOwnProperty(currentCompositePath)) { + compositeToLit[currentCompositePath] = composite; + visualObjectsToLit[currentCompositePath] = []; + variables[currentCompositePath] = []; + + } + + if (multicompartment) { + for (var j = 0; j < composite.getChildren().length; ++j) { + visualObjectsToLit[currentCompositePath].push(composite.getChildren()[j].getId()); + } + } + variables[currentCompositePath].push(instances[i]); + + } + + for (var i in Object.keys(compositeToLit)) { + var path = Object.keys(compositeToLit)[i]; + this.addColorFunctionBulk(compositeToLit[path], visualObjectsToLit[path], variables[path], colorfn); + } + + }, + + /** + * Removes color functions + * + * @param {Instance} instance - The instance to be lit + */ + removeColorFunction: function (instances) { + while (instances.length > 0) { + this.clearColorFunctions(instances[0]); + } + + // update flag + if (this.litUpInstances.length == 0) { + this.colorFunctionSet = false; + } + }, + + /** + * Modulates the color of an aspect visualization, given a watched node + * and a color function. The color function should receive + * the value of the watched node and output [r,g,b]. + * + * @param {Instance} instance - The instance to be lit + * @param {Instance} modulation - Variable which modulates the color + * @param {Function} colorfn - Converts time-series value to [r,g,b] + */ + addColorFunctionBulk: function (instance, visualObjects, stateVariableInstances, colorfn) { + var modulations = []; + if (visualObjects != null) { + if (visualObjects.length > 0) { + var elements = {}; + for (var voIndex in visualObjects) { + elements[visualObjects[voIndex]] = ""; + modulations.push(instance.getInstancePath() + "." + visualObjects[voIndex]); + + } + GEPPETTO.SceneController.splitGroups(instance, elements); + } + else { + modulations.push(instance.getInstancePath()); + } + } + + var matchedMap = []; + //statevariableinstances come out of order, needs to sort into map to avoid nulls + for (var index in modulations) { + for (var i in stateVariableInstances) { + if (stateVariableInstances[i].getParent().getInstancePath() == modulations[index]) { + matchedMap[modulations[index]] = stateVariableInstances[i]; + } + } + } + + //add color listener for map of variables + for (var index in matchedMap) { + this.addColorListener(index, matchedMap[index], colorfn); + } + + // update flag + this.colorFunctionSet = true; + }, + + /** + * + * @param instance + * @param modulation + * @param colorfn + */ + addColorListener: function (instance, modulation, colorfn) { + GEPPETTO.trigger(GEPPETTO.Events.Lit_entities_changed); + this.addOnNodeUpdatedCallback(modulation, function (stateVariableInstance, step) { + if ((stateVariableInstance.getTimeSeries() != undefined) && + (step < stateVariableInstance.getTimeSeries().length)) { + GEPPETTO.SceneController.lightUpEntity(instance, colorfn, stateVariableInstance.getTimeSeries()[step]); + } + }); + }, + + /** + * + * @param varnode + */ + clearColorFunctions: function (varnode) { + var i = this.litUpInstances.indexOf(varnode); + this.litUpInstances.splice(i, 1); + GEPPETTO.trigger(GEPPETTO.Events.Lit_entities_changed); + if (this.litUpInstances.length == 0) { + this.colorFunctionSet = false; + } + this.clearOnNodeUpdateCallback(varnode); + }, + + }; + + return ColorController; +}); + diff --git a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js deleted file mode 100644 index e0afa69c4..000000000 --- a/src/main/webapp/js/components/interface/3dCanvas/GEPPETTO.SceneController.js +++ /dev/null @@ -1,274 +0,0 @@ -/** - * Global controller for all 3d canvas components. - * Methods called in this controller will apply to all the 3D canvas components. - * - * - * @author Matteo Cantarelli - */ -define(function (require) { - return function (GEPPETTO) { - - require('../../../common/GEPPETTO.Resources')(GEPPETTO); - - GEPPETTO.SceneController = - { - linesThreshold: 2000, - aboveLinesThreshold: false, - canvasComponents: [], - wireframe: false, - - - add3DCanvas: function (canvasComponent) { - this.canvasComponents.push(canvasComponent); - }, - - apply: function (args) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i][args[0]].apply(this, arguments); - } - }, - - isVisible: function (variables) { - var visible = false; - for (var i = 0; i < variables.length; i++) { - if (variables[i].isVisible()) { - visible = true; - break; - } - } - return visible; - }, - - isSelected: function (variables) { - var selected = false; - for (var i = 0; i < variables.length; i++) { - if (variables[i].hasOwnProperty('isSelected') && variables[i].isSelected()) { - selected = true; - break; - } - } - return selected; - }, - - /** - * - * Returns all the selected instances - * - * @command G.getSelection() - * @returns {Array} Returns list of all instances selected - */ - getSelection: function () { - var selection = this.traverseSelection(window.Instances); - return selection; - }, - - /** - * Helper method that traverses through run time tree looking for selected entities. - */ - traverseSelection: function (instances) { - var selection = []; - if (instances != null || undefined) { - for (var e = 0; e < instances.length; e++) { - var instance = instances[e]; - if (instance.selected) { - selection.push(instance); - } - selection = selection.concat(this.traverseSelection(instance.getChildren())); - } - } - return selection; - }, - - select: function (instances) { - for (var i = 0; i < instances.length; i++) { - instances[i].select(); - } - - }, - - deselect: function (instances) { - for (var i = 0; i < instances.length; i++) { - instances[i].deselect(); - } - }, - - /** - * Selects an instance in all existing canvas - * - * @param {String} instancePath - Path of instance to select - * @param {String} geometryIdentifier - Identifier of the geometry that was clicked - */ - selectInstance: function (instancePath, geometryIdentifier) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].selectInstance(instancePath, geometryIdentifier); - } - }, - - /** - * Deselects an instance in all existing canvas - * - * @param {String} instancePath - Path of instance to select - * @param {String} geometryIdentifier - Identifier of the geometry that was clicked - */ - deselectInstance: function (instancePath) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].deselectInstance(instancePath); - } - }, - - /** - * - * @param instances - */ - show: function (instances) { - for (var i = 0; i < instances.length; i++) { - instances[i].show(); - } - }, - - /** - * - * @param instances - */ - hide: function (instances) { - for (var i = 0; i < instances.length; i++) { - instances[i].hide(); - } - }, - - assignRandomColor: function (instance) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].assignRandomColor(instance); - } - }, - - /** - * Zoom to the passed instances in all the canvas - * @param instances - */ - zoomTo: function (instances) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].zoomTo(instances); - } - }, - - /** - * Sets whether to use wireframe or not to visualize any instance. - * Applies to all existing canvas. - */ - setWireframe: function (wireframe) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].setWireframe(wireframe); - } - }, - - /** - * Show an instance in all the existing canvas - * - * @param {String} - * instancePath - Instance path of the instance to make visible - */ - showInstance: function (instancePath) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].showInstance(instancePath); - } - }, - - /** - * Hide an instance in all the existing canvas - * - * @param {String} - * instancePath - Path of the instance to hide - */ - hideInstance: function (instancePath) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].hideInstance(instancePath); - } - }, - - /** - * Change the color of a given instance in all the existing canvas - * - * @param {String} - * instancePath - Instance path of the instance to change color - * @param {String} - * color - The color to set - */ - setColor: function (instancePath, color) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].setColor(instancePath, color); - } - }, - - /** - * Change the default opacity for a given instance in all existing canvas. - * - * @param {String} - * instancePath - Instance path of the instance to set the opacity of - * @param {String} - * opacity - The value of the opacity between 0 and 1 - */ - setOpacity: function (instancePath, opacity) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].setOpacity(instancePath, opacity); - } - }, - - /** - * Set the threshold (number of 3D primitives on the scene) above which we switch the visualization to lines - * for all the existing canvas - * @param threshold - */ - setLinesThreshold: function (threshold) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].setLinesThreshold(ithreshold); - } - }, - - /** - * Change the type of geometry used to visualize a given instance in all the existing canvas - * - * @param {String} - * instance - The instance to change the geometry type for - * @param {String} - * type - The geometry type, see GEPPETTO.Resources.GeometryTypes - * @param {String} - * thickness - Optional: the thickness to be used if the geometry is "lines" - */ - setGeometryType: function (instance, type, thickness) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].setGeometryType(instance, type, thickness); - } - }, - - - /** - * Activates a visual group - */ - showVisualGroups: function (visualGroup, mode, instances) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].showVisualGroups(visualGroup, mode, instances); - } - }, - - /** - * Light up the entity in all canvas - * - * @param {Instance} - * instance - the instance to be lit - * @param {Instance} - * colorfn - a function to map the intensity to a color - * @param {Float} - * intensity - the lighting intensity from 0 (no illumination) to 1 (full illumination) - */ - lightUpEntity: function (instance, colorfn, intensity) { - for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].lightUpEntity(instance, colorfn, intensity); - } - } - - - } - } -}) - ; diff --git a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js new file mode 100644 index 000000000..44097ce47 --- /dev/null +++ b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js @@ -0,0 +1,336 @@ +/** + * Global controller for all 3d canvas components. + * Methods called in this controller will apply to all the 3D canvas components. + * + * + * @author Matteo Cantarelli + */ +define(['jquery'], function () { + + function SceneController() { + }; + + SceneController.prototype = + { + + constructor: SceneController, + + canvasComponents: [], + + /** + * + * @param canvasComponent + */ + add3DCanvas: function (canvasComponent) { + this.canvasComponents.push(canvasComponent); + }, + + + /** + * + * @param variables + * @returns {boolean} + */ + isVisible: function (variables) { + var visible = false; + for (var i = 0; i < variables.length; i++) { + if (variables[i].isVisible()) { + visible = true; + break; + } + } + return visible; + }, + + /** + * + * @param variables + * @returns {boolean} + */ + isSelected: function (variables) { + var selected = false; + for (var i = 0; i < variables.length; i++) { + if (variables[i].hasOwnProperty('isSelected') && variables[i].isSelected()) { + selected = true; + break; + } + } + return selected; + }, + + /** + * + * Returns all the selected instances + * + * @command G.getSelection() + * @returns {Array} Returns list of all instances selected + */ + getSelection: function () { + var selection = this.traverseSelection(window.Instances); + return selection; + }, + + /** + * Helper method that traverses through run time tree looking for selected entities. + */ + traverseSelection: function (instances) { + var selection = []; + if (instances != null || undefined) { + for (var e = 0; e < instances.length; e++) { + var instance = instances[e]; + if (instance.selected) { + selection.push(instance); + } + selection = selection.concat(this.traverseSelection(instance.getChildren())); + } + } + return selection; + }, + + /** + * Selects the passed instances + * + * @param instances An array of instances + */ + select: function (instances) { + for (var i = 0; i < instances.length; i++) { + instances[i].select(); + } + + }, + + /** + * Deselects the passed instances + * + * @param instances An array of instances + */ + deselect: function (instances) { + for (var i = 0; i < instances.length; i++) { + instances[i].deselect(); + } + }, + + /** + * Deselects all selected instances + * + * @return {string} + */ + deselectAll: function () { + var selection = this.getSelection(); + if (selection.length > 0) { + for (var key in selection) { + var entity = selection[key]; + entity.deselect(); + } + } + return GEPPETTO.Resources.DESELECT_ALL; + }, + + /** + * Selects an instance in all existing canvas + * + * @param {String} instancePath - Path of instance to select + * @param {String} geometryIdentifier - Identifier of the geometry that was clicked + */ + selectInstance: function (instancePath, geometryIdentifier) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].selectInstance(instancePath, geometryIdentifier); + } + }, + + /** + * Deselects an instance in all existing canvas + * + * @param {String} instancePath - Path of instance to select + * @param {String} geometryIdentifier - Identifier of the geometry that was clicked + */ + deselectInstance: function (instancePath) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].deselectInstance(instancePath); + } + }, + + /** + * + * @param instances + */ + show: function (instances) { + for (var i = 0; i < instances.length; i++) { + instances[i].show(); + } + }, + + /** + * + * @param instances + */ + hide: function (instances) { + for (var i = 0; i < instances.length; i++) { + instances[i].hide(); + } + }, + + /** + * + * @param instance + */ + assignRandomColor: function (instance) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].assignRandomColor(instance); + } + }, + + /** + * Zoom to the passed instances in all the canvas + * @param instances + */ + zoomTo: function (instances) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].zoomTo(instances); + } + }, + + /** + * Sets whether to use wireframe or not to visualize any instance. + * Applies to all existing canvas. + * @param wireframe + */ + setWireframe: function (wireframe) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setWireframe(wireframe); + } + }, + + /** + * Show an instance in all the existing canvas + * + * @param {String} + * instancePath - Instance path of the instance to make visible + */ + showInstance: function (instancePath) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].showInstance(instancePath); + } + }, + + /** + * Hide an instance in all the existing canvas + * + * @param {String} + * instancePath - Path of the instance to hide + */ + hideInstance: function (instancePath) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].hideInstance(instancePath); + } + }, + + /** + * Change the color of a given instance in all the existing canvas + * + * @param {String} + * instancePath - Instance path of the instance to change color + * @param {String} + * color - The color to set + */ + setColor: function (instancePath, color) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setColor(instancePath, color); + } + }, + + /** + * Change the default opacity for a given instance in all existing canvas. + * + * @param {String} + * instancePath - Instance path of the instance to set the opacity of + * @param {String} + * opacity - The value of the opacity between 0 and 1 + */ + setOpacity: function (instancePath, opacity) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setOpacity(instancePath, opacity); + } + }, + + /** + * Set the threshold (number of 3D primitives on the scene) above which we switch the visualization to lines + * for all the existing canvas + * @param threshold + */ + setLinesThreshold: function (threshold) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setLinesThreshold(ithreshold); + } + }, + + /** + * Change the type of geometry used to visualize a given instance in all the existing canvas + * + * @param {String} + * instance - The instance to change the geometry type for + * @param {String} + * type - The geometry type, see GEPPETTO.Resources.GeometryTypes + * @param {String} + * thickness - Optional: the thickness to be used if the geometry is "lines" + */ + setGeometryType: function (instance, type, thickness) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].setGeometryType(instance, type, thickness); + } + }, + + + /** + * + * @param instance + */ + showVisualGroupsForInstance: function (instance) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].showVisualGroupsForInstance(instance); + } + }, + + /** + * Activates a visual group + * @param visualGroup + * @param mode + * @param instances + */ + showVisualGroups: function (visualGroup, mode, instances) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].showVisualGroups(visualGroup, mode, instances); + } + }, + + /** + * Associate a color function to a group of instances for all the existing canvas + * + * @param instances - The instances we want to change the color of + * @param colorfn - The function to be used to modulate the color + * @return {canvasComponent} + */ + addColorFunction: function (instances, colorfn) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].addColorFunction(instances, colorfn); + } + }, + + /** + * Remove a previously associated color function for all existing canvas + * + * @param instances + * @return {canvasComponent} + */ + removeColorFunction: function (instances) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].removeColorFunction(instances); + } + }, + + + }; + + return SceneController; +}); + diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index c3b4d14f2..e376d2c68 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -1,9 +1,9 @@ /* -* The 3D engine used by the 3D canvas component. This class is internal, any method you -* use from inside here might break between versions. Methods maintained are the exposed ones -* through the 3D canvas component. -* -*/ + * The 3D engine used by the 3D canvas component. This class is internal, any method you + * use from inside here might break between versions. Methods maintained are the exposed ones + * through the 3D canvas component. + * + */ define(['jquery'], function () { var Instance = require('../../../geppettoModel/model/Instance'); @@ -29,6 +29,7 @@ define(['jquery'], function () { function ThreeDEngine(container, viewerId) { this.container = container; + this.colorController = require('./ColorController'); this.viewerId = viewerId; //Engine components @@ -41,7 +42,7 @@ define(['jquery'], function () { this.sceneCenter = new THREE.Vector3(); this.cameraPosition = new THREE.Vector3(); - this.mouse = { x: 0, y: 0 }; + this.mouse = {x: 0, y: 0}; //The content of the scene this.meshes = {}; @@ -70,14 +71,15 @@ define(['jquery'], function () { this.animate(); }; + ThreeDEngine.prototype = { constructor: ThreeDEngine, /** - * Sets up the controls used by the camera to make it able to zoom and - * pan. - */ + * Sets up the controls used by the camera to make it able to zoom and + * pan. + */ setupControls: function () { // Controls this.controls = new THREE.TrackballControls(this.camera, this.renderer.domElement, this.viewerId); @@ -223,6 +225,11 @@ define(['jquery'], function () { }, + /** + * + * @param width + * @param height + */ setSize: function (width, height) { this.camera.aspect = width / height; this.camera.updateProjectionMatrix(); @@ -230,6 +237,10 @@ define(['jquery'], function () { this.composer.setSize(width, height); }, + /** + * + * @param shaders + */ configureRenderer: function (shaders) { if (shaders == undefined) { @@ -283,6 +294,10 @@ define(['jquery'], function () { }, + /** + * + * @param askUser + */ setLinesUserInput: function (askUser) { this.linesUserInput = askUser; }, @@ -354,6 +369,11 @@ define(['jquery'], function () { this.camera.updateProjectionMatrix(); }, + /** + * + * @param obj + * @returns {*} + */ boundingBox: function (obj) { if (obj instanceof THREE.Mesh) { @@ -373,10 +393,19 @@ define(['jquery'], function () { } }, + /** + * + * @param obj + * @returns {*} + */ shapeCenterOfGravity: function (obj) { return this.boundingBox(obj).center(); }, + /** + * + * @param node + */ pointCameraTo: function (node) { // Refocus camera to the center of the new object var COG; @@ -473,6 +502,10 @@ define(['jquery'], function () { }, + /** + * + * @param instances + */ buildScene: function (instances) { this.traverseInstances(instances); this.scene.updateMatrixWorld(true); @@ -480,6 +513,10 @@ define(['jquery'], function () { }, + /** + * + * @param instances + */ updateSceneWithNewInstances: function (instances) { var updateCamera = false; if (Object.keys(this.meshes).length === 0) { @@ -528,6 +565,12 @@ define(['jquery'], function () { }, + /** + * + * @param instance + * @param lines + * @param thickness + */ buildVisualInstance: function (instance, lines, thickness) { var meshes = this.generate3DObjects(instance, lines, thickness); this.init3DObject(meshes, instance); @@ -610,10 +653,10 @@ define(['jquery'], function () { var that = this; //TODO This can be optimised, no need to create both var materials = - { - "mesh": that.getMeshPhongMaterial(color), - "line": that.getLineMaterial(thickness, color) - }; + { + "mesh": that.getMeshPhongMaterial(color), + "line": that.getLineMaterial(thickness, color) + }; var instanceObjects = []; var threeDeeObjList = this.walkVisTreeGen3DObjs(instance, materials, lines); @@ -983,7 +1026,7 @@ define(['jquery'], function () { }, /** - * Add a 3D sphere to the scene at the given coordinates (4) points. + * Add a 3D sphere to the scene at the given coordinates (4) points. * It could be any geometry really. * @returns {THREE.Mesh} */ @@ -992,13 +1035,13 @@ define(['jquery'], function () { radius = 1; } - var material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide }); + var material = new THREE.MeshBasicMaterial({side: THREE.DoubleSide}); material.nowireframe = true; material.opacity = 0.6; material.transparent = true; material.color.setHex("0xff0000"); - var sphereNode = { radius: radius, position: { x: x, y: y, z: z } } + var sphereNode = {radius: radius, position: {x: x, y: y, z: z}} var mesh = this.create3DSphereFromNode(sphereNode, material) mesh.renderOrder = 1; mesh.clickThrough = true; @@ -1008,7 +1051,7 @@ define(['jquery'], function () { /** - * Add a 3D plane to the scene at the given coordinates (4) points. + * Add a 3D plane to the scene at the given coordinates (4) points. * It could be any geometry really. * @returns {THREE.Mesh} */ @@ -1050,7 +1093,7 @@ define(['jquery'], function () { geometry.uvsNeedUpdate = true; geometry.dynamic = true; - var material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide }); + var material = new THREE.MeshBasicMaterial({side: THREE.DoubleSide}); material.nowireframe = true; if (textureURL != undefined) { var loader = new THREE.TextureLoader(); @@ -1093,7 +1136,7 @@ define(['jquery'], function () { }, /** - * Modify the coordinates (4) points of an existing plane. + * Modify the coordinates (4) points of an existing plane. * @returns {THREE.Mesh} */ modify3DPlane: function (object, x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) { @@ -1270,26 +1313,6 @@ define(['jquery'], function () { } }, - - /** - * Status of scene, populated or not - * - * @returns {Boolean} True or false depending whether scene is populated - * or not - */ - isScenePopulated: function () { - return !(_.isEmpty(this.visualModelMap)); - }, - - /** - * Has canvas been created? - * - * @returns {Boolean] True or false if canvas has been created or not - */ - isCanvasCreated: function () { - return this.canvasCreated; - }, - /** * @param x * @param y @@ -1341,7 +1364,9 @@ define(['jquery'], function () { var that = this; if (this.rotate == null) { this.movieMode(true); - this.rotate = setInterval(function () { that.incrementCameraRotate(0.01, 0) }, 100); + this.rotate = setInterval(function () { + that.incrementCameraRotate(0.01, 0) + }, 100); } else { this.movieMode(false); @@ -1350,6 +1375,9 @@ define(['jquery'], function () { } }, + /** + * + */ animate: function () { var that = this; that.debugUpdate = that.needsUpdate; @@ -1788,11 +1816,11 @@ define(['jquery'], function () { }, /** - * Change the default opacity for a given aspect. The opacity set with this command API will be persisted across different workflows, e.g. selection. - * - * @param {String} - * instancePath - Instance path of aspect to change opacity for - */ + * Change the default opacity for a given aspect. The opacity set with this command API will be persisted across different workflows, e.g. selection. + * + * @param {String} + * instancePath - Instance path of aspect to change opacity for + */ setOpacity: function (instancePath, opacity) { if (!this.hasInstance(instancePath)) { return; @@ -1958,6 +1986,11 @@ define(['jquery'], function () { } }, + /** + * + * @param instance + * @returns {boolean} + */ hasInstance: function (instance) { var instancePath = typeof instance == "string" ? instance : instance.getInstancePath(); return this.meshes[instancePath] != undefined; @@ -1966,8 +1999,8 @@ define(['jquery'], function () { /** * Restore the original colour of the connected instances * - * @param {Instance} * instance - A connected instance + * @param instance */ restoreConnectedInstancesColour: function (instance) { if (!this.hasInstance(instance)) { @@ -1985,7 +2018,6 @@ define(['jquery'], function () { this.meshes[connection.getA().getPath()]; - // if mesh is not selected, give it ghost or default color and opacity if (!mesh.selected) { // if there are nodes still selected, give it a ghost effect. If not nodes are @@ -2009,11 +2041,12 @@ define(['jquery'], function () { /** - * Show connection lines for this instance. + * Show connection lines for this instance. - * @command AVisualCapability.showConnectionLines() - * @param {boolean} mode - Show or hide connection lines - */ + * @command AVisualCapability.showConnectionLines() + * @param {boolean} mode - Show or hide connection lines + * @param instancePath + */ showConnectionLines: function (instancePath, mode) { if (mode == null || mode == undefined) { mode = true; @@ -2039,7 +2072,7 @@ define(['jquery'], function () { }, /** - * + * * * @param instance */ @@ -2084,7 +2117,8 @@ define(['jquery'], function () { if (connection.getB().getPoint() == undefined) { //same as before - destination = otherEndMesh.position.clone();; + destination = otherEndMesh.position.clone(); + ; } else { //the specified coordinate @@ -2141,7 +2175,7 @@ define(['jquery'], function () { } } - var material = new THREE.LineDashedMaterial({ dashSize: 3, gapSize: 1 }); + var material = new THREE.LineDashedMaterial({dashSize: 3, gapSize: 1}); material.color.setHex(colour); var line = new THREE.LineSegments(geometry, material); @@ -2187,6 +2221,12 @@ define(['jquery'], function () { } }, + /** + * + * @param targetObjects + * @param aspects + * @returns {{}} + */ splitHighlightedMesh: function (targetObjects, aspects) { var groups = {}; for (a in aspects) { @@ -2274,7 +2314,9 @@ define(['jquery'], function () { * groups - The groups that we need to split mesh into */ splitGroups: function (instance, groupElements) { - + if (!this.hasInstance(instance)) { + return; + } var instancePath = instance.getInstancePath(); // retrieve the merged mesh @@ -2366,6 +2408,9 @@ define(['jquery'], function () { * m - current mesh */ addMeshToGeometryGroup: function (instance, id, geometryGroups, m) { + if (!this.hasInstance(instance)) { + return; + } // name of group, mix of aspect path and group name var groupName = instance.getInstancePath() + "." + id; // retrieve corresponding geometry for this group @@ -2383,8 +2428,14 @@ define(['jquery'], function () { /** * Create group meshes for given groups, retrieves from map if already present + * @param instancePath + * @param geometryGroups + * @param groups */ createGroupMeshes: function (instancePath, geometryGroups, groups) { + if (!this.hasInstance(instancePath)) { + return; + } var mergedMesh = this.meshes[instancePath]; // switch visible flag to false for merged mesh and remove from scene mergedMesh.visible = false; @@ -2432,12 +2483,13 @@ define(['jquery'], function () { /** * Merge mesh that was split before * - * @param {String} * aspectPath - Path to aspect that points to mesh + * @param instancePath + * @param visible */ - merge: function (aspectPath, visible) { + merge: function (instancePath, visible) { // get mesh from map - var mergedMesh = this.meshes[aspectPath]; + var mergedMesh = this.meshes[instancePath]; // if merged mesh is not visible, turn it on and turn split one // off @@ -2446,7 +2498,7 @@ define(['jquery'], function () { // retrieve split mesh that is on the scene var splitMesh = this.splitMeshes[path]; if (splitMesh) { - if (aspectPath == splitMesh.instancePath) { + if (instancePath == splitMesh.instancePath) { splitMesh.visible = false; // remove split mesh from scene this.scene.remove(splitMesh); @@ -2461,6 +2513,48 @@ define(['jquery'], function () { } }, + /** + * + * @param visualGroups + * @param instance + * @param meshesContainer + */ + showVisualGroupsForInstance: function (instance) { + if (!this.hasInstance(instance)) { + return; + } + var instancePath = instance.getInstancePath(); + // retrieve the merged mesh + var mergedMesh = this.meshes[instancePath]; + + // get map of all meshes that merged mesh was merging + var map = mergedMesh.mergedMeshesPaths; + + var elements = {} + for (var v in map) { + if (v != undefined) { + eval(map[v].substring(0, map[v].lastIndexOf("."))); + var object = instance.getVisualType()[map[v].replace(instancePath + ".", "")]; + // get group elements list for object + var groupElementsReference = object.getInitialValue().value.groupElements; + for (var i = 0; i < groupElementsReference.length; i++) { + var objectGroup = GEPPETTO.ModelFactory.resolve(groupElementsReference[i].$ref).getId(); + if (objectGroup == this.getId()) { + elements[object.getId()] = {'color': this.getColor()} + } + } + } + } + + this.showVisualGroupsRaw(elements, instance, this.splitMeshes); + }, + + /** + * + * @param visualGroups + * @param instance + * @param meshesContainer + */ showVisualGroupsRaw: function (visualGroups, instance, meshesContainer) { var instancePath = instance.getInstancePath(); for (g in visualGroups) { @@ -2482,6 +2576,9 @@ define(['jquery'], function () { /** * Shows a visual group + * @param visualGroups + * @param mode + * @param instances */ showVisualGroups: function (visualGroups, mode, instances) { for (var i = 0; i < instances.length; i++) { @@ -2504,6 +2601,11 @@ define(['jquery'], function () { } }, + /** + * + * @param variables + * @returns {boolean} + */ isVisible: function (variables) { var visible = false; for (var i = 0; i < variables.length; i++) { @@ -2515,6 +2617,11 @@ define(['jquery'], function () { return visible; }, + /** + * + * @param variables + * @returns {boolean} + */ isSelected: function (variables) { var selected = false; for (var i = 0; i < variables.length; i++) { @@ -2535,12 +2642,19 @@ define(['jquery'], function () { this.resetCamera(); }, + /** + * + */ flipCameraZ: function () { this.camera.direction = new THREE.Vector3(0, 0, -1); this.setupControls(); this.resetCamera(); }, + /** + * + * @param toggle + */ movieMode: function (toggle) { this.configureRenderer(toggle); }, diff --git a/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js b/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js index 803ac8981..7cc2e1be5 100644 --- a/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js +++ b/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js @@ -239,7 +239,7 @@ define(function (require) { watchedVariables += ""; - GEPPETTO.FE.infoDialog("Recorded variables ", watchedVariables); + GEPPETTO.ModalFactory.infoDialog("Recorded variables ", watchedVariables); } }, @@ -255,7 +255,7 @@ define(function (require) { modifiedParameters += ""; - GEPPETTO.FE.infoDialog("Set Parameters ", modifiedParameters); + GEPPETTO.ModalFactory.infoDialog("Set Parameters ", modifiedParameters); }, render: function () { @@ -342,7 +342,7 @@ define(function (require) { if(error!= null || undefined){ e.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); - GEPPETTO.FE.infoDialog("Experiment Failed ", error.exception); + GEPPETTO.ModalFactory.infoDialog("Experiment Failed ", error.exception); } } }, @@ -395,7 +395,7 @@ define(function (require) { if(login){ GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.LOADING_EXPERIMENT); }else{ - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.ERROR, + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.ERROR, GEPPETTO.Resources.OPERATION_NOT_SUPPORTED + GEPPETTO.Resources.USER_NOT_LOGIN); } }, @@ -403,7 +403,7 @@ define(function (require) { deleteExperiment : function(e){ var experiment = this.props.experiment; var index = window.Project.getExperiments().indexOf(experiment); - GEPPETTO.FE.inputDialog( + GEPPETTO.ModalFactory.inputDialog( "Are you sure?", "Delete " + experiment.name + "?", "Yes", @@ -546,7 +546,7 @@ define(function (require) { GEPPETTO.on(GEPPETTO.Events.Experiment_deleted, function (experiment) { self.deleteExperiment(experiment); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.EXPERIMENT_DELETED, "Experiment " + experiment.name + " with id " + experiment.id + " was deleted successfully"); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.EXPERIMENT_DELETED, "Experiment " + experiment.name + " with id " + experiment.id + " was deleted successfully"); }); diff --git a/src/main/webapp/js/geppettoModel/ModelFactory.js b/src/main/webapp/js/geppettoModel/ModelFactory.js index 09228ae5a..094600847 100644 --- a/src/main/webapp/js/geppettoModel/ModelFactory.js +++ b/src/main/webapp/js/geppettoModel/ModelFactory.js @@ -2686,9 +2686,6 @@ define(function (require) { delete parent[instance.getId()]; } - // remove from scene - GEPPETTO.SceneController.removeFromScene(instance); - // unresolve type for (var j = 0; j < instance.getTypes().length; j++) { this.unresolveType(instance.getTypes()[j]); diff --git a/src/main/webapp/js/geppettoModel/model/VisualGroupElement.js b/src/main/webapp/js/geppettoModel/model/VisualGroupElement.js index e8dff24b0..7735ff760 100644 --- a/src/main/webapp/js/geppettoModel/model/VisualGroupElement.js +++ b/src/main/webapp/js/geppettoModel/model/VisualGroupElement.js @@ -76,33 +76,8 @@ define(function (require) { } for (var i = 0; i < instances.length; i++) { - var instance = instances[i]; - var instancePath = instance.getInstancePath(); - - // retrieve the merged mesh - var mergedMesh = GEPPETTO.getVARS().meshes[instancePath]; - - // get map of all meshes that merged mesh was merging - var map = mergedMesh.mergedMeshesPaths; - - var elements = {} - for (var v in map) { - if (v != undefined) { - var m = GEPPETTO.getVARS().visualModelMap[map[v]]; - eval(map[v].substring(0, map[v].lastIndexOf("."))); - var object = instance.getVisualType()[map[v].replace(instancePath + ".", "")]; - // get group elements list for object - var groupElementsReference = object.getInitialValue().value.groupElements; - for (var i = 0; i < groupElementsReference.length; i++) { - var objectGroup = GEPPETTO.ModelFactory.resolve(groupElementsReference[i].$ref).getId(); - if (objectGroup == this.getId()) { - elements[object.getId()] = {'color':this.getColor()} - } - } - } - } - - GEPPETTO.SceneController.showVisualGroupsRaw(elements, instance, GEPPETTO.getVARS().splitMeshes); + GEPPETTO.SceneController.showVisualGroup(instances[i]); + } }; diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index 0b82dfd90..c19543b14 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -267,7 +267,7 @@ define(function (require) { } } else { - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.CANT_PLAY_EXPERIMENT, "Experiment " + experiment.name + " with id " + experiment.id + " isn't completed, and can't be played."); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.CANT_PLAY_EXPERIMENT, "Experiment " + experiment.name + " with id " + experiment.id + " isn't completed, and can't be played."); } }, @@ -360,7 +360,7 @@ define(function (require) { this.worker = new Worker("geppetto/js/geppettoProject/ExperimentWorker.js"); // tells worker to update each half a second - this.worker.postMessage([GEPPETTO.Events.Experiment_play, GEPPETTO.getVARS().playTimerStep, this.playOptions.step]); + this.worker.postMessage([GEPPETTO.Events.Experiment_play, GEPPETTO.ExperimentsController.playTimerStep, this.playOptions.step]); var that = this; // receives message from web worker diff --git a/src/main/webapp/js/geppettoProject/model/ProjectNode.js b/src/main/webapp/js/geppettoProject/model/ProjectNode.js index 8a1b421a1..6eef8ae48 100644 --- a/src/main/webapp/js/geppettoProject/model/ProjectNode.js +++ b/src/main/webapp/js/geppettoProject/model/ProjectNode.js @@ -341,7 +341,7 @@ define(['backbone'], function (require) { message = GEPPETTO.Resources.OPERATION_NOT_SUPPORTED + GEPPETTO.Resources.DOWNLOAD_PRIVILEGES_NOT_SUPPORTED; } - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.ERROR, message); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.ERROR, message); return message; } diff --git a/src/main/webapp/js/pages/geppetto/G.js b/src/main/webapp/js/pages/geppetto/G.js index 5498b5436..bf92bef95 100644 --- a/src/main/webapp/js/pages/geppetto/G.js +++ b/src/main/webapp/js/pages/geppetto/G.js @@ -1,5 +1,3 @@ - - /** * * Global objects. Handles global operations; clearing js console history commands, @@ -19,14 +17,13 @@ define(function (require) { * @exports geppetto-objects/G */ GEPPETTO.G = { - listeners: {}, + selectionOptions: { show_inputs: true, show_outputs: true, draw_connection_lines: true, unselected_transparent: true }, - litUpInstances: [], //TODO Design something better to hold abritrary status timeWidget: {}, @@ -34,8 +31,8 @@ define(function (require) { recordedVariablesWidget: {}, recordedVariablesPlot: false, enableColorPlottingActive: false, - brightnessFunctionSet: false, consoleFocused: true, + debugMode: false, isConsoleFocused: function () { return this.consoleFocused; @@ -45,10 +42,6 @@ define(function (require) { this.consoleFocused = mode; }, - isBrightnessFunctionSet: function() { - return this.brightnessFunctionSet; - }, - /** * Adds widgets to Geppetto * @@ -139,19 +132,28 @@ define(function (require) { } }, + /** * Toggles debug statement on/off * - * @command G.debug(toggle) - * @param toggle - toggles debug statements - * + * @param mode + * @return {string} */ debug: function (mode) { this.debugMode = mode; - if(!GEPPETTO.Console.getConsole().showImplicitCommands){ - GEPPETTO.Console.toggleImplicitCommands(); + if (mode != GEPPETTO.Console.getConsole().showImplicitCommands) { + GEPPETTO.Console.toggleImplicitCommands(); } - return mode?GEPPETTO.Resources.DEBUG_ON:GEPPETTO.Resources.DEBUG_OFF; + return mode ? GEPPETTO.Resources.DEBUG_ON : GEPPETTO.Resources.DEBUG_OFF; + }, + + /** + * State of debug statements, whether they are turned on or off. + * + * @returns {boolean} Returns true or false depending if debug statements are turned on or off. + */ + isDebugOn: function () { + return this.debugMode; }, /** @@ -249,7 +251,6 @@ define(function (require) { }, - /** * Show or hide help window using command * @@ -278,20 +279,20 @@ define(function (require) { } return returnMessage; }, - - toggleTutorial : function() { - var returnMessage; - var modalVisible = $('#tutorial_dialog').is(':visible'); - - if (modalVisible) { - GEPPETTO.trigger(GEPPETTO.Events.Hide_Tutorial); - returnMessage = GEPPETTO.Resources.HIDE_TUTORIAL; - } - else { - GEPPETTO.trigger(GEPPETTO.Events.Show_Tutorial); - returnMessage = GEPPETTO.Resources.SHOW_TUTORIAL; - } - return returnMessage; + + toggleTutorial: function () { + var returnMessage; + var modalVisible = $('#tutorial_dialog').is(':visible'); + + if (modalVisible) { + GEPPETTO.trigger(GEPPETTO.Events.Hide_Tutorial); + returnMessage = GEPPETTO.Resources.HIDE_TUTORIAL; + } + else { + GEPPETTO.trigger(GEPPETTO.Events.Show_Tutorial); + returnMessage = GEPPETTO.Resources.SHOW_TUTORIAL; + } + return returnMessage; }, /** @@ -310,6 +311,11 @@ define(function (require) { return GEPPETTO.Resources.WAITING; }, + /** + * + * @param key + * @returns {string} + */ linkDropBox: function (key) { if (key != null || key != undefined) { var parameters = {}; @@ -326,217 +332,8 @@ define(function (require) { } }, - unLinkDropBox: function () { - - }, - - /** - * State of debug statements, whether they are turned on or off. - * - * @returns {boolean} Returns true or false depending if debug statements are turned on or off. - */ - isDebugOn: function () { - return debugMode; - }, - - /** - * Callback to be called whenever a watched node changes - * - * @param {Instance} node - node to couple callback to - * @param {Function} callback - Callback function to be called whenever _variable_ changes - */ - addOnNodeUpdatedCallback: function (node, callback) { - if (node != null || undefined) { - if (!this.listeners[node.getInstancePath()]) { - this.listeners[node.getInstancePath()] = []; - } - this.listeners[node.getInstancePath()].push(callback); - } - }, - - /** - * Clears callbacks coupled to changes in a node - * - * @param {Instance} node - node to which callbacks are coupled - */ - clearOnNodeUpdateCallback: function (node) { - this.listeners[node.getInstancePath()] = null; - }, - - /** - * Applies visual transformations to a given entity given instance path of the transformations. - * - * @param {AspectNode} visualAspect - Aspect for the entity the visual transformation is to be applied to - * @param {SkeletonAnimationNode} visualTransformInstancePath - node that stores the visual transformations - */ - addVisualTransformListener: function (visualAspect, visualTransformInstancePath) { - this.addOnNodeUpdatedCallback(visualTransformInstancePath, function (varnode, step) { - GEPPETTO.SceneController.applyVisualTransformation(visualAspect, varnode.skeletonTransformations[step]); - }); - }, - - /** - * Modulates the brightness of an aspect visualization, given a watched node - * and a color function. The color function should receive - * the value of the watched node and output [r,g,b]. - * - * @param {Instance} instance - The instance to be lit - * @param {Instance} modulation - Variable which modulates the brightness - * @param {Function} colorfn - Converts time-series value to [r,g,b] - */ - addBrightnessFunction: function (instance, stateVariableInstances, colorfn) { - // Check if instance is instance + visualObjects or instance (hhcell.hhpop[0].soma or hhcell.hhpop[0]) - var newInstance = ""; - var visualObjects = []; - if (instance.getInstancePath() in GEPPETTO.getVARS().meshes) { - newInstance = instance; - } - else { - newInstance = instance.getParent(); - visualObjects = [instance.getId()]; - } - - this.addBrightnessFunctionBulk(newInstance, visualObjects, [stateVariableInstances], colorfn); - }, - - /** - * Modulates the brightness of an aspect visualization, given a watched node - * and a color function. The color function should receive - * the value of the watched node and output [r,g,b]. - * - * @param {Instance} instance - The instance to be lit - * @param {Instance} modulation - Variable which modulates the brightness - * @param {Function} colorfn - Converts time-series value to [r,g,b] - */ - addBrightnessFunctionBulkSimplified: function (instances, colorfn) { - // Check if instance is instance + visualObjects or instance (hhcell.hhpop[0].soma or hhcell.hhpop[0]) - for (var i = 0; i < instances.length; ++i) { - this.litUpInstances.push(instances[i]); - } - var compositeToLit = {}; - var visualObjectsToLit = {}; - var variables = {}; - var currentCompositePath = undefined; - - for (var i = 0; i < instances.length; i++) { - var composite = undefined; - var multicompartment = false; - - composite = instances[i].getParent(); - - while (composite.getMetaType() != GEPPETTO.Resources.ARRAY_ELEMENT_INSTANCE_NODE) { - if (composite.getParent() == null) { - throw "Unsupported model to use this function"; - } else { - composite = composite.getParent(); - multicompartment = true; - } - } - - var currentCompositePath = composite.getInstancePath(); - if (!compositeToLit.hasOwnProperty(currentCompositePath)) { - compositeToLit[currentCompositePath] = composite; - visualObjectsToLit[currentCompositePath] = []; - variables[currentCompositePath] = []; - - } - - if (multicompartment) { - for (var j = 0; j < composite.getChildren().length; ++j) { - visualObjectsToLit[currentCompositePath].push(composite.getChildren()[j].getId()); - } - } - variables[currentCompositePath].push(instances[i]); - - } - - for (var i in Object.keys(compositeToLit)) { - var path = Object.keys(compositeToLit)[i]; - this.addBrightnessFunctionBulk(compositeToLit[path], visualObjectsToLit[path], variables[path], colorfn); - } - - }, - - /** - * Removes brightness functions - * - * @param {Instance} instance - The instance to be lit - */ - removeBrightnessFunctionBulkSimplified: function (instances) { - while (instances.length > 0) { - this.clearBrightnessFunctions(instances[0]); - } - - // update flag - if (this.litUpInstances.length == 0) { - this.brightnessFunctionSet = false; - } - }, - - /** - * Modulates the brightness of an aspect visualization, given a watched node - * and a color function. The color function should receive - * the value of the watched node and output [r,g,b]. - * - * @param {Instance} instance - The instance to be lit - * @param {Instance} modulation - Variable which modulates the brightness - * @param {Function} colorfn - Converts time-series value to [r,g,b] - */ - addBrightnessFunctionBulk: function (instance, visualObjects, stateVariableInstances, colorfn) { - var modulations = []; - if (visualObjects != null) { - if (visualObjects.length > 0) { - var elements = {}; - for (var voIndex in visualObjects) { - elements[visualObjects[voIndex]] = ""; - modulations.push(instance.getInstancePath() + "." + visualObjects[voIndex]); - - } - GEPPETTO.SceneController.splitGroups(instance, elements); - } - else { - modulations.push(instance.getInstancePath()); - } - } - - var matchedMap = []; - //statevariableinstances come out of order, needs to sort into map to avoid nulls - for (var index in modulations) { - for (var i in stateVariableInstances) { - if (stateVariableInstances[i].getParent().getInstancePath() == modulations[index]) { - matchedMap[modulations[index]] = stateVariableInstances[i]; - } - } - } - - //add brightness listener for map of variables - for (var index in matchedMap) { - this.addBrightnessListener(index, matchedMap[index], colorfn); - } - // update flag - this.brightnessFunctionSet = true; - }, - addBrightnessListener: function (instance, modulation, colorfn) { - GEPPETTO.trigger(GEPPETTO.Events.Lit_entities_changed); - this.addOnNodeUpdatedCallback(modulation, function (stateVariableInstance, step) { - if ((stateVariableInstance.getTimeSeries() != undefined) && - (step < stateVariableInstance.getTimeSeries().length)) { - GEPPETTO.SceneController.lightUpEntity(instance, colorfn, stateVariableInstance.getTimeSeries()[step]); - } - }); - }, - - clearBrightnessFunctions: function (varnode) { - var i = this.litUpInstances.indexOf(varnode); - this.litUpInstances.splice(i, 1); - GEPPETTO.trigger(GEPPETTO.Events.Lit_entities_changed); - if (this.litUpInstances.length == 0) { - this.brightnessFunctionSet = false; - } - this.clearOnNodeUpdateCallback(varnode); - }, /** * Sets options that happened during selection of an entity. For instance, @@ -569,52 +366,6 @@ define(function (require) { return this.selectionOptions; }, - /** - * Deselects all selected entities - * - * @command G.unSelectAll() - */ - unSelectAll: function () { - var selection = this.getSelection(); - if (selection.length > 0) { - for (var key in selection) { - var entity = selection[key]; - entity.deselect(); - } - } - - if (G.getSelectionOptions().unselected_transparent) { - GEPPETTO.SceneController.setGhostEffect(false); - } - return GEPPETTO.Resources.DESELECT_ALL; - }, - - - /** - * - * Outputs list of commands with descriptions associated with the Simulation object. - * - * @command G.getSelection() - * @returns {Array} Returns list of all entities selected - */ - getSelection: function () { - var selection = this.traverseSelection(window.Instances); - - return selection; - }, - - /** - * Unhighlight all highlighted connections - * - * @command G.unHighlightAll() - */ - unHighlightAll: function () { - for (var hc in this.highlightedConnections) { - this.highlightedConnections[hc].highlight(false); - } - - return GEPPETTO.Resources.HIGHLIGHT_ALL; - }, /** * Sets the timer for updates during play/replay. @@ -622,7 +373,7 @@ define(function (require) { * @command G.setPlayTimerStep(interval) */ setPlayTimerStep: function (interval) { - GEPPETTO.getVARS().playTimerStep = interval; + GEPPETTO.ExperimentsController.playTimerStep = interval; }, /** @@ -631,7 +382,7 @@ define(function (require) { * @command G.setPlayLoop(loop) */ setPlayLoop: function (loop) { - GEPPETTO.getVARS().playLoop = loop; + GEPPETTO.ExperimentsController.playLoop = loop; }, /** diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js index 140ca78de..d0620939c 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js @@ -1,4 +1,3 @@ - /** * * Events @@ -14,50 +13,48 @@ define(function (require) { */ GEPPETTO.Events = { Select: "experiment:selection_changed", - Visibility_changed : "experiment:visibility_changed", - Focus_changed: "experiment:focus_changed", - Experiment_over: "experiment:over", - Project_loading: "project:loading", - Project_loaded: "project:loaded", - Project_downloaded : "project:downloaded", - Model_loaded: "model:loaded", - Experiment_loaded: "experiment:loaded", - ModelTree_populated: "experiment:modeltreepopulated", - SimulationTree_populated: "experiment:simulationtreepopulated", - Do_experiment_play: "experiment:doPlay", - Experiment_play: "experiment:play", - Experiment_status_check: "experiment:status_check", - Experiment_pause: "experiment:pause", - Experiment_resume: "experiment:resume", - Experiment_running: "experiment:running", - Experiment_stop: "experiment:stop", - Experiment_completed: "experiment:completed", - Experiment_failed: "experiment:failed", - Experiment_update: "experiment:update", - Experiment_updated: "experiment:updated", - Experiment_renamed: "experiment:renamed", - Experiment_deleted: "experiment_deleted", - Experiment_active: "experiment_active", - Experiment_created:"experiment:created", - Project_persisted: "project:persisted", - Spotlight_closed: "spotlight:closed", - Spotlight_loaded: "spotlight:loaded", - Instance_deleted: "instance:deleted", - Instances_created: "instances:created", - Show_Tutorial : "show_tutorial", - Hide_Tutorial : "hide_tutorial", - Show_spinner: "spinner:show", - Hide_spinner: "spinner:hide", - Color_set: "color:set", - Canvas_initialised: "canvas:initialised", - Project_made_public : "project_made_public", - Control_panel_open: "control_panel:open", - Control_panel_close: "control_panel:close", - Lit_entities_changed: "lit_entities_changed", + Visibility_changed: "experiment:visibility_changed", + Focus_changed: "experiment:focus_changed", + Experiment_over: "experiment:over", + Project_loading: "project:loading", + Project_loaded: "project:loaded", + Project_downloaded: "project:downloaded", + Model_loaded: "model:loaded", + Experiment_loaded: "experiment:loaded", + ModelTree_populated: "experiment:modeltreepopulated", + SimulationTree_populated: "experiment:simulationtreepopulated", + Do_experiment_play: "experiment:doPlay", + Experiment_play: "experiment:play", + Experiment_status_check: "experiment:status_check", + Experiment_pause: "experiment:pause", + Experiment_resume: "experiment:resume", + Experiment_running: "experiment:running", + Experiment_stop: "experiment:stop", + Experiment_completed: "experiment:completed", + Experiment_failed: "experiment:failed", + Experiment_update: "experiment:update", + Experiment_updated: "experiment:updated", + Experiment_renamed: "experiment:renamed", + Experiment_deleted: "experiment_deleted", + Experiment_active: "experiment_active", + Experiment_created: "experiment:created", + Project_persisted: "project:persisted", + Spotlight_closed: "spotlight:closed", + Spotlight_loaded: "spotlight:loaded", + Instance_deleted: "instance:deleted", + Instances_created: "instances:created", + Show_Tutorial: "show_tutorial", + Hide_Tutorial: "hide_tutorial", + Show_spinner: "spinner:show", + Hide_spinner: "spinner:hide", + Color_set: "color:set", + Canvas_initialised: "canvas:initialised", + Project_made_public: "project_made_public", + Control_panel_open: "control_panel:open", + Control_panel_close: "control_panel:close", + Lit_entities_changed: "lit_entities_changed", Component_destroyed: "component_destroyed", - listening: false, - listen: function () { GEPPETTO.on(this.Select, function () { //notify widgets that selection has changed in scene @@ -70,15 +67,18 @@ define(function (require) { GEPPETTO.on(this.Experiment_active, function () { GEPPETTO.WidgetsListener.update(GEPPETTO.WidgetsListener.WIDGET_EVENT_TYPE.DELETE); }); + GEPPETTO.on(this.Experiment_loaded, function () { if (GEPPETTO.UserController.isLoggedIn()) { GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); } }); + GEPPETTO.on(this.Project_loaded, function () { var projectID = window.Project.getId(); GEPPETTO.Main.startStatusWorker(); }); + GEPPETTO.on(this.Experiment_over, function (e) { var name = e.name; var id = e.id; @@ -87,16 +87,18 @@ define(function (require) { GEPPETTO.WidgetsListener.update(GEPPETTO.Events.Experiment_over); // check if we are in looping mode - if (GEPPETTO.getVARS().playLoop === true) { - Project.getActiveExperiment().play({ step: 1 }); + if (GEPPETTO.ExperimentsController.playLoop === true) { + Project.getActiveExperiment().play({step: 1}); } else { GEPPETTO.Console.log("Experiment " + name + " with " + id + " is over "); } }); + GEPPETTO.on(this.Experiment_play, function (parameters) { GEPPETTO.WidgetsListener.update(GEPPETTO.Events.Experiment_play, parameters); }); + GEPPETTO.on(this.Experiment_update, function (parameters) { if (parameters.playAll != null || parameters.step != undefined) { //update scene brightness @@ -111,15 +113,19 @@ define(function (require) { //notify widgets a restart of data is needed GEPPETTO.WidgetsListener.update(GEPPETTO.Events.Experiment_update, parameters); }); + GEPPETTO.on(this.Experiment_stop, function (parameters) { }); + GEPPETTO.on(this.Lit_entities_changed, function (parameters) { GEPPETTO.WidgetsListener.update(GEPPETTO.Events.Lit_entities_changed, parameters); }); - GEPPETTO.on(this.Do_experiment_play, function () { + + GEPPETTO.on(this.Do_experiment_play, function () { Project.getActiveExperiment().playAll(); }); - GEPPETTO.on(GEPPETTO.Events.Component_destroyed, function(){ + + GEPPETTO.on(GEPPETTO.Events.Component_destroyed, function () { GEPPETTO.ViewController.anyComponentsDestroyed = true; }); } diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.FE.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.FE.js deleted file mode 100644 index b9c208c5b..000000000 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.FE.js +++ /dev/null @@ -1,207 +0,0 @@ - -/** - * Front end, user interface, methods for handling updates to the UI - * - */ -define(function(require) -{ - - return function(GEPPETTO) - { - - var React = require('react'); - var $ = require('jquery'); - var InfoModal = require('../../components/controls/modals/InfoModal'); - var ErrorModal = require('../../components/controls/modals/ErrorModal'); - var InputModal = require('../../components/controls/modals/InputModal'); - var ReactDOM = require('react-dom'); - - /** - * Create the container for holding the canvas - * - * @class GEPPETTO.FE - */ - GEPPETTO.FE = - { - - // variable to keep track of experiments rendered, used for giving - // alternate routes - // different css backgrounds - nth : 1, - - /* - * Handles events that are executed as soon as page is finished - * loading - */ - initialEvents : function() - { - - GEPPETTO.Console.createConsole(); - - /* - * Dude to bootstrap bug, multiple modals can't be open at same - * time. This line allows multiple modals to be open - * simultaneously without going in an infinite loop. - */ - $.fn.modal.Constructor.prototype.enforceFocus = function() - { - }; - }, - /** - * Enables controls after connection is established - */ - postSocketConnection : function() - { - - }, - createContainer : function() - { - $("#sim canvas").remove(); - return $("#sim").get(0); - }, - - /** - * Show error message if webgl failed to start - */ - update : function(webGLStarted) - { - if (!webGLStarted) - { - GEPPETTO.Console.debugLog(GEPPETTO.Resources.WEBGL_FAILED); - GEPPETTO.FE.disableSimulationControls(); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.WEBGL_FAILED, GEPPETTO.Resources.WEBGL_MESSAGE); - } - }, - /** - * Basic Dialog box with message to display. - * - * @method - * - * @param title - - * Title of message - * @param msg - - * Message to display - */ - infoDialog : function(title, msg) - { - var infoFactory = React.createFactory(InfoModal); - - ReactDOM.render( - infoFactory( - { - show : true, - keyboard : false, - title : title, - text : msg, - }), - - document.getElementById('modal-region') - ); - }, - - /** - * Basic Dialog box with message to display. - * - * @method - * - * @param title - - * Title of message - * @param msg - - * Message to display - */ - inputDialog : function(title, msg, aLabel, aClick, bLabel, bClick) - { - var infoFactory = React.createFactory(InputModal); - - ReactDOM.render( - infoFactory( - { - show : true, - keyboard : false, - title : title, - text : msg, - aLabel:aLabel, - aClick:aClick, - bLabel:bLabel, - bClick:bClick - }), - - document.getElementById('modal-region') - ); - }, - - /** - * Dialog box to display error messages. - * - * @method - * - * @param title - - * Notifying error - * @param msg - - * Message to display for error - * @param code - - * Error code of message - * @param source - - * Source error to display - * @param exception - - * Exception to display - */ - errorDialog : function(title, message, code, exception) - { - var errorModalFactory = React.createFactory(ErrorModal); - - ReactDOM.render( - errorModalFactory( - { - show : true, - keyboard : false, - title : title, - message : message, - code : code, - exception : exception - }), - document.getElementById('modal-region') - ); - }, - /** - * If simulation is being controlled by another user, hide the - * control and load buttons. Show "Observe" button only. - */ - disableSimulationControls : function() - { - GEPPETTO.trigger('simulation:disable_all'); - - // disable console buttons - $('#consoleButton').attr('disabled', 'disabled'); - $('#commandInputArea').attr('disabled', 'disabled'); - - }, - - - /** - * Show error message if webgl failed to start - */ - notifyInitErrors : function(webGLStarted, workersSupported) - { - if (!webGLStarted) - { - GEPPETTO.Console.debugLog(GEPPETTO.Resources.WEBGL_FAILED) - GEPPETTO.Main.disconnected = true;; - GEPPETTO.FE.disableSimulationControls(); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.WEBGL_FAILED, GEPPETTO.Resources.WEBGL_MESSAGE); - } - - if (!workersSupported) - { - GEPPETTO.Console.debugLog(GEPPETTO.Resources.WORKERS_NOT_SUPPORTED); - GEPPETTO.FE.infoDialog(GEPPETTO.Resources.WORKERS_NOT_SUPPORTED, GEPPETTO.Resources.WORKERS_NOT_SUPPORTED_MESSAGE); - } - - if(!webGLStarted || !workersSupported){ - GEPPETTO.FE.disableSimulationControls(); - } - } - }; - - }; -}); diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Init.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Init.js deleted file mode 100644 index 33ff35bdf..000000000 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Init.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @class GEPPETTO.Init - */ -define(function (require) { - return function (GEPPETTO) { - - var createChannel = function () { - // Change link from blank to self for embedded environments - if (window.EMBEDDED && window.EMBEDDEDURL !== "/" && typeof handleRequest == 'undefined') { - handleRequest = function (e) { - if (window.EMBEDDEDURL.indexOf(e.origin) != -1) { - if (e.data.command == 'loadSimulation') { - if (e.data.projectId) { - GEPPETTO.Console.executeCommand('Project.loadFromID(' + e.data.projectId + ')'); - } - else if (e.data.url) { - GEPPETTO.Console.executeCommand('Project.loadFromURL("' + e.data.url + '")'); - } - } - else if (e.data.command == 'removeWidgets') { - GEPPETTO.Console.executeCommand('G.removeWidget()'); - } - else { - eval(e.data.command); - } - } - }; - // we have to listen for 'message' - window.addEventListener('message', handleRequest, false); - if ($.isArray(window.EMBEDDEDURL)) { - window.parent.postMessage({ "command": "ready" }, window.EMBEDDEDURL[0]); - } - else { - window.parent.postMessage({ "command": "ready" }, window.EMBEDDEDURL); - } - } - }; - - - GEPPETTO.Init = { - - /** - * - */ - initEventListeners: function () { - // setup listeners for geppetto events that can be triggered - if (!GEPPETTO.Events.listening) { - GEPPETTO.Events.listen(); - GEPPETTO.Events.listening = true; - } - }, - - /** - * - * @param containerp - * @returns {*|Object} - */ - initialize: function (containerp) { - createChannel(); - } - - - }; - }; -}); diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js index 0cac8cc48..0575b753a 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.Main.js @@ -1,6 +1,5 @@ /** - * Main class for handling user interface evens associated with: Simulation Controls, - * alert & info messages, and server side communication + * Geppetto entry point * * @author matteo@openworm.org (Matteo Cantarelli) * @author giovanni@openworm.org (Giovanni Idili) @@ -9,11 +8,11 @@ define(function (require) { return function (GEPPETTO) { - var $ = require('jquery'), - React = require('react'), - InfoModal = require('../../components/controls/modals/InfoModal'), - ProjectNode = require('../../geppettoProject/model/ProjectNode'), - ReactDOM = require('react-dom'); + var $ = require('jquery'); + var React = require('react'); + var InfoModal = require('../../components/controls/modals/InfoModal'); + var ProjectNode = require('../../geppettoProject/model/ProjectNode'); + var ReactDOM = require('react-dom'); /** * @class GEPPETTO.Main @@ -22,14 +21,55 @@ define(function (require) { idleTime: 0, disconnected: false, - status: 0, - statusWorker : null, - localStorageEnabled : false, + statusWorker: null, + localStorageEnabled: false, + /** + * + */ + createChannel: function () { + // Change link from blank to self for embedded environments + if (window.EMBEDDED && window.EMBEDDEDURL !== "/" && typeof handleRequest == 'undefined') { + handleRequest = function (e) { + if (window.EMBEDDEDURL.indexOf(e.origin) != -1) { + if (e.data.command == 'loadSimulation') { + if (e.data.projectId) { + GEPPETTO.Console.executeCommand('Project.loadFromID(' + e.data.projectId + ')'); + } + else if (e.data.url) { + GEPPETTO.Console.executeCommand('Project.loadFromURL("' + e.data.url + '")'); + } + } + else if (e.data.command == 'removeWidgets') { + GEPPETTO.Console.executeCommand('G.removeWidget()'); + } + else { + eval(e.data.command); + } + } + }; + // we have to listen for 'message' + window.addEventListener('message', handleRequest, false); + if ($.isArray(window.EMBEDDEDURL)) { + window.parent.postMessage({"command": "ready"}, window.EMBEDDEDURL[0]); + } + else { + window.parent.postMessage({"command": "ready"}, window.EMBEDDEDURL); + } + } + }, + + /** + * + * @returns {null} + */ getStatusWorker: function () { return this.statusWorker; }, + /** + * + */ startStatusWorker: function () { //create web worker for checking status if (this.statusWorker != undefined) { @@ -64,6 +104,8 @@ define(function (require) { */ init: function () { GEPPETTO.MessageSocket.connect(GEPPETTO.MessageSocket.protocol + window.location.host + '/' + window.BUNDLE_CONTEXT_PATH + '/GeppettoServlet'); + GEPPETTO.Events.listen(); + this.createChannel(); GEPPETTO.Console.debugLog(GEPPETTO.Resources.GEPPETTO_INITIALIZED); }, @@ -78,8 +120,9 @@ define(function (require) { //first time check, asks if user is still there if (GEPPETTO.Main.idleTime > allowedTime) { // 5 minutes + //TODO Matteo: Make a function to create a custom Info modal inside ModalFactory and use it from here. var infoFactory = React.createFactory(InfoModal); - ReactDOM.render(infoFactory({ show: true, keyboard: false }), document.getElementById('modal-region')); + ReactDOM.render(infoFactory({show: true, keyboard: false}), document.getElementById('modal-region')); $('#infomodal-title').html("Zzz"); $('#infomodal-text').html(GEPPETTO.Resources.IDLE_MESSAGE); @@ -97,6 +140,7 @@ define(function (require) { //second check, user isn't there or didn't click yes, disconnect if (GEPPETTO.Main.idleTime > timeOut) { + //TODO Matteo: Make a function to create a custom Info modal inside ModalFactory and use it from here. var infoFactory = React.createFactory(InfoModal); ReactDOM.render(infoFactory({ show: true, @@ -110,16 +154,9 @@ define(function (require) { GEPPETTO.Main.idleTime = 0; GEPPETTO.Main.disconnected = true; - GEPPETTO.FE.disableSimulationControls(); GEPPETTO.MessageSocket.close(); - var webGLStarted = GEPPETTO.init(GEPPETTO.FE.createContainer()); - var webWorkersSupported = (typeof (Worker) !== "undefined") ? true : false; - if (!webGLStarted || !webWorkersSupported) { - GEPPETTO.FE.notifyInitErrors(webGLStarted, webWorkersSupported); - GEPPETTO.MessageSocket.close(); - } } } } @@ -127,37 +164,36 @@ define(function (require) { }; - // ============================================================================ - // Application logic. - // ============================================================================ - $(document).ready(function () { + GEPPETTO.Console.createConsole(); var webWorkersSupported = (typeof (Worker) !== "undefined") ? true : false; //make sure webgl started correctly if (!webWorkersSupported) { - GEPPETTO.FE.notifyInitErrors(webGLStarted, webWorkersSupported); + GEPPETTO.Console.debugLog(GEPPETTO.Resources.WORKERS_NOT_SUPPORTED); + GEPPETTO.ModalFactory.infoDialog(GEPPETTO.Resources.WORKERS_NOT_SUPPORTED, GEPPETTO.Resources.WORKERS_NOT_SUPPORTED_MESSAGE); } else { - GEPPETTO.FE.initialEvents(); //Increment the idle time counter every minute. setInterval(GEPPETTO.Main.idleCheck, 240000); // 1 minute var here = $(this); + //Zero the idle timer on mouse movement. here.mousemove(function (e) { if (GEPPETTO.Main.idleTime > -1) { GEPPETTO.Main.idleTime = 0; } }); + here.keypress(function (e) { if (GEPPETTO.Main.idleTime > -1) { GEPPETTO.Main.idleTime = 0; } }); - //Initialize websocket functionality GEPPETTO.Main.init(); + //TODO Matteo: All the code below needs to be removed creating a component for the tabbed UI var visibleExperiments = false; $('#experimentsButton').click(function (e) { if (!visibleExperiments) { diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.js index 5f54f2d12..b970b9bc5 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.js @@ -1,6 +1,6 @@ /** * - * @author matteo@openworm.org (Matteo Cantarelli) + * @author Matteo Cantarelli * @authot Jesus R Martinez (jesus@metacell.us) */ define(function (require) { @@ -42,10 +42,7 @@ define(function (require) { var curr_min = d.getMinutes(); var curr_sec = d.getSeconds(); var curr_msec = d.getMilliseconds(); - - console.log(curr_hour + ":" + curr_min + ":" + curr_sec + ":" - + curr_msec + ' - ' + msg, ""); - + console.log(curr_hour + ":" + curr_min + ":" + curr_sec + ":" + curr_msec + ' - ' + msg, ""); } }, @@ -73,14 +70,15 @@ define(function (require) { require('../../common/GEPPETTO.Resources')(GEPPETTO); require('../../common/GEPPETTO.ViewController')(GEPPETTO); require('./GEPPETTO.Events')(GEPPETTO); - require('./GEPPETTO.Init')(GEPPETTO); - require('../../components/interface/3dCanvas/GEPPETTO.SceneController')(GEPPETTO); - require('./GEPPETTO.FE')(GEPPETTO); require('../../common/GEPPETTO.UserController')(GEPPETTO); require('./GEPPETTO.Flows')(GEPPETTO); require('../../common/GEPPETTO.ScriptRunner')(GEPPETTO); require('../../components/interface/jsEditor/GEPPETTO.JSEditor')(GEPPETTO); require('../../components/interface/jsConsole/GEPPETTO.Console')(GEPPETTO); + + GEPPETTO.ModalFactory = new(require('../../components/controls/modals/ModalFactory'))(); + GEPPETTO.SceneController = new(require('../../components/interface/3dCanvas/SceneController'))(); + require('../../common/GEPPETTO.Utility')(GEPPETTO); require('../../components/widgets/MenuManager')(GEPPETTO); require('../../communication/MessageSocket')(GEPPETTO); diff --git a/src/main/webapp/js/pages/geppetto/main.js b/src/main/webapp/js/pages/geppetto/main.js index 5230c3b0c..0820c249a 100644 --- a/src/main/webapp/js/pages/geppetto/main.js +++ b/src/main/webapp/js/pages/geppetto/main.js @@ -1,35 +1,3 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2011, 2013 OpenWorm. - * http://openworm.org - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the MIT License - * which accompanies this distribution, and is available at - * http://opensource.org/licenses/MIT - * - * Contributors: - * OpenWorm - http://openworm.org/people.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - *******************************************************************************/ /** * Loads all scripts needed for Geppetto * diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index a49f08b04..0a02ea79b 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -141,7 +141,7 @@ module.exports = { module: { loaders: [ { - test: /\.(js)$/, exclude: [/node_modules\/(?!(ami.js)\/).*/, /build/, /\.bundle/, ], loader: ['babel-loader'], + test: /\.(js)$/, exclude: [/node_modules\/(?!(ami.js)\/).*/, /build/, /\.bundle/], loader: ['babel-loader'], query: { presets: ['react', 'es2015'] } @@ -150,6 +150,10 @@ module.exports = { test: /\.json$/, loader: "json-loader" }, + { + test: /Dockerfile/, + loader: 'ignore-loader' + }, { test: /\.(py|png|jpeg|svg|gif|css|jpg|md|hbs|dcm|gz|xmi|dzi)$/, loader: 'ignore-loader' From 5f73be07744acbd77660fcf7a2f7ea307f437fc3 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 15:31:08 +0100 Subject: [PATCH 163/339] Fixes for default extension --- .../geppetto-default/ComponentsInitialization.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index 86a527738..e6d73a57d 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -57,21 +57,12 @@ define(function (require) { //Home button initialization GEPPETTO.ComponentFactory.addComponent('HOME', {}, document.getElementById("HomeButton")); - //Home button initialization - GEPPETTO.ComponentFactory.addComponent('HOME', {}, undefined); - - //Home button initialization - GEPPETTO.ComponentFactory.addComponent('HOME', {}, undefined); - //Save initialization GEPPETTO.ComponentFactory.addComponent('SAVECONTROL', {}, document.getElementById("SaveButton")); //Simulation controls initialization GEPPETTO.ComponentFactory.addComponent('SIMULATIONCONTROLS', {}, document.getElementById("sim-toolbar")); - //Camera controls initialization - GEPPETTO.ComponentFactory.addComponent('CAMERACONTROLS', {}, document.getElementById("camera-controls")); - //Share controls initialization GEPPETTO.ComponentFactory.addComponent('SHARE', {}, document.getElementById("share-button")); From 2d2aa6dcfd0e4ddd5af050190ce51941ec20ec11 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 16:28:40 +0100 Subject: [PATCH 164/339] Fixes for getColor and zoom --- .../components/interface/3dCanvas/Canvas.js | 11 ++ .../interface/3dCanvas/SceneController.js | 28 +++ .../interface/3dCanvas/ThreeDEngine.js | 187 ++++++++++++++---- .../interface/controlPanel/controlpanel.js | 50 ++--- .../capabilities/AVisualCapability.js | 41 +--- 5 files changed, 202 insertions(+), 115 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 8def24dc2..265515c0c 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -126,6 +126,17 @@ define(function (require) { return this; }, + /** + * Retrieves the color of a given instance + * + * @param {String} + * instance - Instance we want the color of + */ + getColor: function (instance) { + this.engine.getColor(instance); + return this; + }, + /** * Change the default opacity for a given instance * diff --git a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js index 44097ce47..29063c385 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js @@ -239,6 +239,34 @@ define(['jquery'], function () { } }, + /** + * Retrieves the color for a given instance. + * If multiple canvas are present and they have different colors associated to the given instance an array is returned instead. + * + * @param {String} + * instancePath - Instance path of the instance to change color + */ + getColor: function (instance) { + var colors=[]; + for (var i = 0; i < this.canvasComponents.length; i++) { + var c = this.canvasComponents[i].getColor(instance); + if(!$.inArray(c,colors)){ + colors.push(c); + } + } + if(colors.length==1){ + return colors[0]; + } + else if(colors.length==0){ + return undefined; + } + else{ + return colors; + } + }, + + + /** * Change the default opacity for a given instance in all existing canvas. * diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index e376d2c68..1268b87b1 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -1454,18 +1454,23 @@ define(['jquery'], function () { if (child.hasOwnProperty("material")) { that.setThreeColor(child.material.color, GEPPETTO.Resources.COLORS.SELECTED); child.material.opacity = Math.max(0.5, child.material.defaultOpacity); - child.geometry.computeBoundingBox(); - that.controls.target.copy(child.position); - that.controls.target.add(child.geometry.boundingBox.getCenter()); + + if (GEPPETTO.isKeyPressed('c')) { + child.geometry.computeBoundingBox(); + that.controls.target.copy(child.position); + that.controls.target.add(child.geometry.boundingBox.getCenter()); + } } }); } else { this.setThreeColor(mesh.material.color, GEPPETTO.Resources.COLORS.SELECTED); mesh.material.opacity = Math.max(0.5, mesh.material.defaultOpacity); - mesh.geometry.computeBoundingBox(); - //let's set the center of rotation to the selected mesh - this.controls.target.copy(mesh.position); - this.controls.target.add(mesh.geometry.boundingBox.getCenter()); + if (GEPPETTO.isKeyPressed('c')) { + mesh.geometry.computeBoundingBox(); + //let's set the center of rotation to the selected mesh + this.controls.target.copy(mesh.position); + this.controls.target.add(mesh.geometry.boundingBox.getCenter()); + } } mesh.selected = true; mesh.ghosted = false; @@ -1753,10 +1758,10 @@ define(['jquery'], function () { }, /** - * Change the color of a given aspect + * Change the color of a given instance * - * @param {String} - * instancePath - Instance path of aspect to change color + * @param instancePath + * @param color */ setColor: function (instancePath, color) { if (!this.hasInstance(instancePath)) { @@ -1779,6 +1784,52 @@ define(['jquery'], function () { } }, + /** + * Retrieves the color of a given instance in this canvas + * @param instance + * @return {string} + */ + getColor: function (instance) { + var color = ""; + if (typeof instance.getChildren === "function") { + //this is a an array, it will contain children + var children = instance.getChildren(); + + var color = ""; + for (var i = 0; i < children.length; i++) { + if (typeof children[i].getColor === "function") { + var newColor = children[i].getColor(); + if (color == "") { + color = newColor; + } + else if (color != newColor) { + return ""; + } + } + } + } + + var meshes = this.getRealMeshesForInstancePath(instance.getInstancePath()); + if (meshes.length > 0) { + for (var i = 0; i < meshes.length; i++) { + var mesh = meshes[i]; + if (mesh) { + mesh.traverse(function (object) { + if (object.hasOwnProperty("material")) { + if (color == "") { + color = object.material.defaultColor; + } + else if (color != object.material.defaultColor) { + return ""; + } + } + }); + } + } + } + + return color; + }, /** * Assign random color to instance if leaf - if not leaf assign random colr to all leaf children recursively * @param instance @@ -1813,7 +1864,9 @@ define(['jquery'], function () { } - }, + } + + , /** * Change the default opacity for a given aspect. The opacity set with this command API will be persisted across different workflows, e.g. selection. @@ -1849,7 +1902,8 @@ define(['jquery'], function () { return true; } return false; - }, + } + , /** * Set the threshold (number of 3D primitives on the scene) above which we switch the visualization to lines @@ -1858,7 +1912,8 @@ define(['jquery'], function () { */ setLinesThreshold: function (threshold) { this.linesThreshold = threshold; - }, + } + , /** * Change the type of geometry used to visualize the instance @@ -1888,7 +1943,8 @@ define(['jquery'], function () { this.traverseInstances([instance], lines, thickness); return true; - }, + } + , /** * Set the type of geometry used to visualize all the instances in the scene @@ -1906,7 +1962,8 @@ define(['jquery'], function () { } } } - }, + } + , /** @@ -1929,8 +1986,45 @@ define(['jquery'], function () { this.zoomToParameters(zoomParameters); + } + , + + /** + * + * @param instances + */ + zoomTo: function (instances) { + this.controls.reset(); + this.zoomToParameters(this.zoomIterator(instances, {})); + }, + + /** + * + * @param instances + * @param zoomParameters + * @returns {*} + */ + zoomIterator: function (instances, zoomParameters) { + var that = this; + for (var i = 0; i < instances.length; i++) { + var instancePath = instances[i].getInstancePath(); + var mesh = this.meshes[instancePath]; + if (mesh) { + mesh.traverse(function (object) { + if (object.hasOwnProperty("geometry")) { + that.addMeshToZoomParameters(object, zoomParameters); + } + }); + } + else { + zoomParameters = this.zoomIterator(instances[i].getChildren(), zoomParameters); + } + + } + return zoomParameters; }, + /** * Change color for meshes that are connected to other meshes. Color depends on whether that instance is an output, input or both * @@ -1984,7 +2078,8 @@ define(['jquery'], function () { outputs[otherEndPath] = connection.getInstancePath(); } } - }, + } + , /** * @@ -1994,7 +2089,8 @@ define(['jquery'], function () { hasInstance: function (instance) { var instancePath = typeof instance == "string" ? instance : instance.getInstancePath(); return this.meshes[instancePath] != undefined; - }, + } + , /** * Restore the original colour of the connected instances @@ -2037,7 +2133,8 @@ define(['jquery'], function () { mesh.material.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; } } - }, + } + , /** @@ -2069,7 +2166,8 @@ define(['jquery'], function () { } } } - }, + } + , /** * @@ -2189,7 +2287,8 @@ define(['jquery'], function () { this.scene.add(line); this.connectionLines[connection.getInstancePath()] = line; } - }, + } + , /** * Removes connection lines, all if nothing is passed in or just the ones passed in. @@ -2219,7 +2318,8 @@ define(['jquery'], function () { } this.connectionLines = []; } - }, + } + , /** * @@ -2272,7 +2372,8 @@ define(['jquery'], function () { this.createGroupMeshes(a, geometryGroups, newGroups); } return groups; - }, + } + , /** * Highlight part of a mesh @@ -2301,7 +2402,8 @@ define(['jquery'], function () { this.setThreeColor(groupMesh.material.color, splitHighlightedGroups[groupName].color.getHex()); } } - }, + } + , /** * Split merged mesh into individual meshes @@ -2393,7 +2495,8 @@ define(['jquery'], function () { groupElements[instancePath] = {}; groupElements[instancePath].color = GEPPETTO.Resources.COLORS.SPLIT; this.createGroupMeshes(instancePath, geometryGroups, groupElements); - }, + } + , /** * Add mesh to geometry groups @@ -2424,7 +2527,8 @@ define(['jquery'], function () { geometry.merge(m.geometry, m.matrix); } return true; - }, + } + , /** * Create group meshes for given groups, retrieves from map if already present @@ -2478,7 +2582,8 @@ define(['jquery'], function () { groupMesh.visible = true; this.scene.add(groupMesh); } - }, + } + , /** * Merge mesh that was split before @@ -2511,7 +2616,8 @@ define(['jquery'], function () { this.scene.add(mergedMesh); } } - }, + } + , /** * @@ -2547,7 +2653,8 @@ define(['jquery'], function () { } this.showVisualGroupsRaw(elements, instance, this.splitMeshes); - }, + } + , /** * @@ -2572,7 +2679,8 @@ define(['jquery'], function () { groupMesh.visible = true; this.setThreeColor(groupMesh.material.color, visualGroup.color); } - }, + } + , /** * Shows a visual group @@ -2599,7 +2707,8 @@ define(['jquery'], function () { } } - }, + } + , /** * @@ -2615,7 +2724,8 @@ define(['jquery'], function () { } } return visible; - }, + } + , /** * @@ -2631,7 +2741,8 @@ define(['jquery'], function () { } } return selected; - }, + } + , /** * Reinitializes the camera with the Y axis flipped @@ -2640,7 +2751,8 @@ define(['jquery'], function () { this.camera.up = new THREE.Vector3(0, -1, 0); this.setupControls(); this.resetCamera(); - }, + } + , /** * @@ -2649,7 +2761,8 @@ define(['jquery'], function () { this.camera.direction = new THREE.Vector3(0, 0, -1); this.setupControls(); this.resetCamera(); - }, + } + , /** * @@ -2657,7 +2770,8 @@ define(['jquery'], function () { */ movieMode: function (toggle) { this.configureRenderer(toggle); - }, + } + , /** * Resets the scene controller @@ -2667,7 +2781,8 @@ define(['jquery'], function () { this.aboveLinesThreshold = false; } - }; + } + ; return ThreeDEngine; }); diff --git a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js index d4d10976d..e51f6d160 100644 --- a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js +++ b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js @@ -1,35 +1,3 @@ -/******************************************************************************* - * - * Copyright (c) 2011, 2016 OpenWorm. - * http://openworm.org - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the MIT License - * which accompanies this distribution, and is available at - * http://opensource.org/licenses/MIT - * - * Contributors: - * OpenWorm - http://openworm.org/people.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - *******************************************************************************/ - define(function (require) { function loadCss(url) { @@ -354,14 +322,15 @@ define(function (require) { if (entity.hasCapability(GEPPETTO.Resources.VISUAL_CAPABILITY)) { defColor = entity.getColor(); } - - // init dat color picker var coloPickerElement = $('#' + this.colorPickerBtnId); coloPickerElement.colorpicker({format: 'hex', customClass: 'controlpanel-colorpicker'}); - coloPickerElement.colorpicker('setValue', defColor.replace(/0X/i, "#")); + + if(defColor!=undefined){ + // init dat color picker + coloPickerElement.colorpicker('setValue', defColor.replace(/0X/i, "#")); + } // closure on local scope at this point - hook on change event - coloPickerElement.on('changeColor', function (e) { that.colorPickerActionFn(e.color.toHex().replace("#", "0x")); $(this).css("color", e.color.toHex()); @@ -490,9 +459,12 @@ define(function (require) { if (entity != undefined && controlConfig.id == "color") { that.colorPickerBtnId = idVal; that.colorPickerActionFn = actionFn; - // set style val to color tint icon - var colorVal = String(entity.getColor().replace(/0X/i, "#") + "0000").slice(0, 7); - styleVal = {color: colorVal.startsWith('#') ? colorVal : ('#' + colorVal) }; + var color = entity.getColor(); + if(color!=undefined) { + // set style val to color tint icon + var colorVal = String(color.replace(/0X/i, "#") + "0000").slice(0, 7); + styleVal = {color: colorVal.startsWith('#') ? colorVal : ('#' + colorVal)}; + } classVal += " color-picker-button"; } diff --git a/src/main/webapp/js/geppettoModel/capabilities/AVisualCapability.js b/src/main/webapp/js/geppettoModel/capabilities/AVisualCapability.js index 40e12ee37..23a7a4967 100644 --- a/src/main/webapp/js/geppettoModel/capabilities/AVisualCapability.js +++ b/src/main/webapp/js/geppettoModel/capabilities/AVisualCapability.js @@ -161,46 +161,7 @@ define(function (require) { * @returns {*} */ getColor: function () { - - var color = ""; - if (typeof this.getChildren === "function") { - //this is a an array, it will contain children - var children = this.getChildren(); - - var color = ""; - for (var i = 0; i < children.length; i++) { - if (typeof children[i].getColor === "function") { - var newColor = children[i].getColor(); - if (color == "") { - color = newColor; - } - else if (color != newColor) { - return ""; - } - } - } - } - - var meshes = GEPPETTO.SceneController.getRealMeshesForInstancePath(this.getInstancePath()); - if (meshes.length > 0) { - for (var i = 0; i < meshes.length; i++) { - var mesh = meshes[i]; - if (mesh) { - mesh.traverse(function (object) { - if (object.hasOwnProperty("material")) { - if (color == "") { - color = object.material.defaultColor; - } - else if (color != object.material.defaultColor) { - return ""; - } - } - }); - } - } - } - - return color; + return GEPPETTO.SceneController.getColor(this); }, /** From 45e7aaf9cbdc9c70615f99574eacde5e472d5190 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 16:38:21 +0100 Subject: [PATCH 165/339] getColor fix --- src/main/webapp/js/components/interface/3dCanvas/Canvas.js | 3 +-- .../webapp/js/components/interface/3dCanvas/SceneController.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 265515c0c..73f29b524 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -133,8 +133,7 @@ define(function (require) { * instance - Instance we want the color of */ getColor: function (instance) { - this.engine.getColor(instance); - return this; + return this.engine.getColor(instance); }, /** diff --git a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js index 29063c385..371273d3c 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js @@ -250,7 +250,7 @@ define(['jquery'], function () { var colors=[]; for (var i = 0; i < this.canvasComponents.length; i++) { var c = this.canvasComponents[i].getColor(instance); - if(!$.inArray(c,colors)){ + if($.inArray(c,colors)==-1){ colors.push(c); } } From 6fdcd3131d46187a079e85b7e19ca5fd6e4659f3 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 16:52:36 +0100 Subject: [PATCH 166/339] Assign random color fix --- .../interface/3dCanvas/ThreeDEngine.js | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index 1268b87b1..124fbf836 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -1835,9 +1835,9 @@ define(['jquery'], function () { * @param instance */ assignRandomColor: function (instance) { - if (!this.hasInstance(instance)) { - return; - } + + var that = this; + var getRandomColor = function () { var letters = '0123456789ABCDEF'; var color = '0x'; @@ -1847,18 +1847,32 @@ define(['jquery'], function () { return color; }; - var meshes = this.getRealMeshesForInstancePath(instance.getInstancePath()); - if (meshes.length > 0) { - for (var i = 0; i < meshes.length; i++) { - var mesh = meshes[i]; - if (mesh) { - var randomColor = getRandomColor(); - mesh.traverse(function (object) { - if (object.hasOwnProperty("material")) { - this.setThreeColor(object.material.color, randomColor); - object.material.defaultColor = randomColor; + if (instance.hasCapability('VisualCapability')) { + var children = instance.getChildren(); + + if (children.length == 0 || instance.getMetaType() == GEPPETTO.Resources.ARRAY_ELEMENT_INSTANCE_NODE) { + if (!this.hasInstance(instance)) { + return; + } + var meshes = this.getRealMeshesForInstancePath(instance.getInstancePath()); + if (meshes.length > 0) { + for (var i = 0; i < meshes.length; i++) { + var mesh = meshes[i]; + if (mesh) { + var randomColor = getRandomColor(); + + mesh.traverse(function (object) { + if (object.hasOwnProperty("material")) { + that.setThreeColor(object.material.color, randomColor); + object.material.defaultColor = randomColor; + } + }); } - }); + } + } + } else { + for (var i = 0; i < children.length; i++) { + this.assignRandomColor(children[i]); } } } From 14f370a82602ad8bde7b3411eaec2fa6e414703b Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 17:19:41 +0100 Subject: [PATCH 167/339] Deal with potentially different colors in different canvases --- .../interface/controlPanel/controlpanel.js | 487 ++++++++++-------- 1 file changed, 267 insertions(+), 220 deletions(-) diff --git a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js index e51f6d160..0acf721da 100644 --- a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js +++ b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js @@ -22,9 +22,9 @@ define(function (require) { $.widget.bridge('uitooltip', $.ui.tooltip); GEPPETTO.ImageComponent = React.createClass({ - attachTooltip: function(){ + attachTooltip: function () { $('img[rel="tooltip"]').uitooltip({ - position: { my: "left+15 center", at: "right center" }, + position: {my: "left+15 center", at: "right center"}, tooltipClass: "tooltip-container", content: function () { return this.getAttribute("title"); @@ -32,17 +32,17 @@ define(function (require) { }); }, - componentDidMount: function(){ + componentDidMount: function () { this.attachTooltip(); }, render: function () { - var imgId = this.props.rowData.path.replace(/\./g,'_') + "_thumbnail"; + var imgId = this.props.rowData.path.replace(/\./g, '_') + "_thumbnail"; var titleValue = ""; var imgElement = ""; - if(this.props.data.indexOf("http") > -1){ - imgElement = + if (this.props.data.indexOf("http") > -1) { + imgElement = } return ( @@ -99,12 +99,12 @@ define(function (require) { // retrieve markup to inject as string var markupToInject = domObj.prop('outerHTML'); - var getMarkup = function() { + var getMarkup = function () { return {__html: markupToInject}; }; return ( - + ); }) } @@ -141,18 +141,18 @@ define(function (require) { }); GEPPETTO.ParameterInputComponent = React.createClass({ - refresh: function() { + refresh: function () { this.forceUpdate(); }, - replaceTokensWithProjectExperimentIds: function(inputStr, projectId, experimentId){ + replaceTokensWithProjectExperimentIds: function (inputStr, projectId, experimentId) { return inputStr.replace(/\$projectId\$/gi, projectId).replace(/\$experimentId\$/gi, experimentId); }, componentDidMount: function () { - - var that = this; - + + var that = this; + // listen to experiment status change and trigger a re-render to refresh input / read-only status GEPPETTO.on(GEPPETTO.Events.Experiment_completed, function () { that.refresh(); @@ -164,8 +164,8 @@ define(function (require) { that.refresh(); }); }, - - componentWillUnmount: function() { + + componentWillUnmount: function () { GEPPETTO.off(GEPPETTO.Events.Experiment_failed, this.refresh, this); GEPPETTO.off(GEPPETTO.Events.Experiment_running, this.refresh, this); GEPPETTO.off(GEPPETTO.Events.Experiment_completed, this.refresh, this); @@ -176,26 +176,26 @@ define(function (require) { var path = this.props.rowData.path; var projectId = this.props.rowData.projectId; var experimentId = this.props.rowData.experimentId; - var entity=undefined; + var entity = undefined; var unit = undefined; var defaultValue = undefined; var initialValue = undefined; - if(this.props.rowData.fetched_value != undefined){ + if (this.props.rowData.fetched_value != undefined) { // we have a value in the record, we are dealing with an external item, it's not an actual entity that can be evaluated unit = this.props.rowData.unit; - defaultValue = this.props.rowData.fetched_value -1; //we don't have the default value and will never show it, this only allows for the value to show as edited + defaultValue = this.props.rowData.fetched_value - 1; //we don't have the default value and will never show it, this only allows for the value to show as edited initialValue = this.props.rowData.fetched_value; } else { - try{ - entity=eval(path); + try { + entity = eval(path); // fetch unit unit = entity.getUnit(); // fetch current or default value defaultValue = entity.getInitialValue(); initialValue = entity.getValue(); } - catch(e){ + catch (e) { // something went horribly wrong - this should never happen throw "ParameterInputComponent - could not eval path: " + path; } @@ -203,7 +203,7 @@ define(function (require) { // figure out if input is readonly, this is always true if not dealing with entities (entity == undefined) var readOnly = true; - if(entity != undefined) { + if (entity != undefined) { try { var deTokenizedCondition = this.replaceTokensWithProjectExperimentIds(this.props.metadata.readOnlyCondition, projectId, experimentId); // eval condition + make sure we have a real entity @@ -212,12 +212,12 @@ define(function (require) { // nothing to do here readOnly defaults to true if evaluation failed } } - + var that = this; // get and ready action string var actionStr = this.props.metadata.actions; - var onInputChangeHandler = function(event){ - if(!readOnly) { + var onInputChangeHandler = function (event) { + if (!readOnly) { var newVal = event.target.value; // only set if it's different than its current value, could be initial or set value @@ -233,8 +233,8 @@ define(function (require) { } }; - var onKeyPressHandler = function(event){ - if(event.key == 'Enter'){ + var onKeyPressHandler = function (event) { + if (event.key == 'Enter') { onInputChangeHandler(event); } }; @@ -259,32 +259,32 @@ define(function (require) { colorPickerBtnId: '', colorPickerActionFn: '', - refresh: function() { + refresh: function () { this.forceUpdate(); }, - - replaceTokensWithPath: function(inputStr, path){ + + replaceTokensWithPath: function (inputStr, path) { return inputStr.replace(/\$instance\$/gi, path).replace(/\$instances\$/gi, '[' + path + ']'); }, - replaceTokensWithProjectExperimentIds: function(inputStr, projectId, experimentId){ + replaceTokensWithProjectExperimentIds: function (inputStr, projectId, experimentId) { return inputStr.replace(/\$projectId\$/gi, projectId).replace(/\$experimentId\$/gi, experimentId); }, - replaceAllTokensKnownToMan: function(inputStr, path, projectId, experimentId){ + replaceAllTokensKnownToMan: function (inputStr, path, projectId, experimentId) { return this.replaceTokensWithProjectExperimentIds(this.replaceTokensWithPath(inputStr, path), projectId, experimentId); }, getActionString: function (control, path, projectId, experimentId) { var actionStr = ''; - if(control.actions!=null || undefined){ - if (control.actions.length > 0) { - for (var i = 0; i < control.actions.length; i++) { + if (control.actions != null || undefined) { + if (control.actions.length > 0) { + for (var i = 0; i < control.actions.length; i++) { var deTokenizedStr = this.replaceAllTokensKnownToMan(control.actions[i], path, projectId, experimentId); - actionStr += ((i != 0) ? ";" : "") + deTokenizedStr; - } - } + actionStr += ((i != 0) ? ";" : "") + deTokenizedStr; + } + } } return actionStr; }, @@ -325,9 +325,16 @@ define(function (require) { var coloPickerElement = $('#' + this.colorPickerBtnId); coloPickerElement.colorpicker({format: 'hex', customClass: 'controlpanel-colorpicker'}); - if(defColor!=undefined){ + if (defColor != undefined) { + var setColor = "" + if ($.isArray(defColor)) { + setColor = "0xfc6320"; + } + else { + setColor = defColor.replace(/0X/i, "#"); + } // init dat color picker - coloPickerElement.colorpicker('setValue', defColor.replace(/0X/i, "#")); + coloPickerElement.colorpicker('setValue', setColor); } // closure on local scope at this point - hook on change event @@ -348,27 +355,27 @@ define(function (require) { that.refresh(); }); }, - - componentWillUnmount: function() { + + componentWillUnmount: function () { GEPPETTO.off(GEPPETTO.Events.Experiment_failed, this.refresh, this); GEPPETTO.off(GEPPETTO.Events.Experiment_running, this.refresh, this); GEPPETTO.off(GEPPETTO.Events.Experiment_completed, this.refresh, this); }, - + // Utility method to iterate over a config property and populate a list of control buttons to be created - addControlButtons: function(controlsConfig, showControlsConfig, configPropertyName, buttonsList, targetPath, projectId, experimentId){ + addControlButtons: function (controlsConfig, showControlsConfig, configPropertyName, buttonsList, targetPath, projectId, experimentId) { for (var control in controlsConfig[configPropertyName]) { if ($.inArray(control.toString(), showControlsConfig[configPropertyName]) != -1) { var add = true; // check show condition - if(controlsConfig[configPropertyName][control].showCondition != undefined){ + if (controlsConfig[configPropertyName][control].showCondition != undefined) { var condition = this.replaceAllTokensKnownToMan(controlsConfig[configPropertyName][control].showCondition, targetPath, projectId, experimentId); add = eval(condition); } - if(add) { + if (add) { buttonsList.push(controlsConfig[configPropertyName][control]); } } @@ -413,9 +420,9 @@ define(function (require) {
                {ctrlButtons.map(function (control, id) { var menuButton = false; - if(control.menu!=undefined && control.menu!=null){ - menuButton = control.menu; - } + if (control.menu != undefined && control.menu != null) { + menuButton = control.menu; + } // grab attributes to init button attributes var controlConfig = that.resolveCondition(control, path, false, projectId, experimentId); var idVal = path.replace(/\./g, '_').replace(/\[/g, '_').replace(/\]/g, '_') + "_" + controlConfig.id + "_ctrlPanel_btn"; @@ -439,7 +446,7 @@ define(function (require) { if (actionStr != '' && actionStr != undefined) { GEPPETTO.Console.executeImplicitCommand(actionStr); // check custom action to run after configured command - if(that.props.metadata.actions != '' && that.props.metadata.actions != undefined) { + if (that.props.metadata.actions != '' && that.props.metadata.actions != undefined) { // straight up eval as we don't want this to show on the geppetto console var evalString = that.replaceAllTokensKnownToMan(that.props.metadata.actions, path, projectId, experimentId); eval(evalString); @@ -460,57 +467,65 @@ define(function (require) { that.colorPickerBtnId = idVal; that.colorPickerActionFn = actionFn; var color = entity.getColor(); - if(color!=undefined) { - // set style val to color tint icon - var colorVal = String(color.replace(/0X/i, "#") + "0000").slice(0, 7); + if (color != undefined) { + + var colorVal = "" + if ($.isArray(color)) { + colorVal = "#fc6320"; + } + else { + colorVal = String(color.replace(/0X/i, "#") + "0000").slice(0, 7); + } + styleVal = {color: colorVal.startsWith('#') ? colorVal : ('#' + colorVal)}; } classVal += " color-picker-button"; } - var controlPanelMenuButtonConfig= {}; - if(control.menu){ - var menuButtonItems = []; - if(control.menuItems!=null || control.menuItems != undefined){ - for(var i =0; i {menuButton ? - - : - + } ) @@ -598,38 +613,38 @@ define(function (require) { this.forceUpdate(); }, - componentDidMount: function(){ + componentDidMount: function () { var that = this; - GEPPETTO.on(GEPPETTO.Events.Control_panel_open, function(){ + GEPPETTO.on(GEPPETTO.Events.Control_panel_open, function () { // when control panel is open and we are using the filter component // if no other main component is toggled show visual instances - if(!that.state.stateVarsFilterToggled && !that.state.paramsFilterToggled){ + if (!that.state.stateVarsFilterToggled && !that.state.paramsFilterToggled) { // same logic as if viz instances filter was clicked that.computeResult('visualInstancesFilterBtn'); } }); - GEPPETTO.on("control_panel_refresh", function(){ + GEPPETTO.on("control_panel_refresh", function () { // when control panel is open and we are using the filter component // if no other main component is toggled show visual instances - if(that.state.stateVarsFilterToggled){ + if (that.state.stateVarsFilterToggled) { // same logic as if viz instances filter was clicked that.computeResult('stateVariablesFilterBtn'); } - else if(that.state.paramsFilterToggled){ + else if (that.state.paramsFilterToggled) { // same logic as if viz instances filter was clicked that.computeResult('parametersFilterBtn'); - }else if(that.state.visualFilterToggled){ + } else if (that.state.visualFilterToggled) { // same logic as if viz instances filter was clicked that.computeResult('visualInstancesFilterBtn'); } }); }, - computeResult: function(controlId){ + computeResult: function (controlId) { // logic for disable/enable stuff here - switch(controlId) { + switch (controlId) { case 'visualInstancesFilterBtn': - if(!this.state.visualFilterToggled){ + if (!this.state.visualFilterToggled) { this.setTogglesState( // visual instance being toggled on, untoggle everything else true, false, false, false, false, false, false, @@ -641,9 +656,9 @@ define(function (require) { } break; case 'stateVariablesFilterBtn': - if(!this.state.stateVarsFilterToggled){ + if (!this.state.stateVarsFilterToggled) { var activeExperimentToggleStatus = this.state.activeExperimentFilterToggled; - if(!(this.state.activeExperimentFilterToggled || this.state.anyExperimentFilterToggled || this.state.anyProjectFilterToggled)){ + if (!(this.state.activeExperimentFilterToggled || this.state.anyExperimentFilterToggled || this.state.anyProjectFilterToggled)) { // if state var is selected and none of the relevant sub-toggles are selected, select active experiment activeExperimentToggleStatus = true; } @@ -659,9 +674,9 @@ define(function (require) { } break; case 'parametersFilterBtn': - if(!this.state.paramsFilterToggled){ + if (!this.state.paramsFilterToggled) { var activeExperimentToggleStatus = this.state.activeExperimentFilterToggled; - if(!(this.state.activeExperimentFilterToggled || this.state.anyExperimentFilterToggled || this.state.anyProjectFilterToggled)){ + if (!(this.state.activeExperimentFilterToggled || this.state.anyExperimentFilterToggled || this.state.anyProjectFilterToggled)) { // if params is selected and none of the relevant sub-toggles are selected, select active experiment activeExperimentToggleStatus = true; } @@ -677,7 +692,7 @@ define(function (require) { } break; case 'activeExperimentFilterBtn': - if(!this.state.activeExperimentFilterToggled){ + if (!this.state.activeExperimentFilterToggled) { // recorded filter is only visible for state vars in the active experiment // NOTE: this variable assignment is verbose but more readable var recordedVisibility = this.state.stateVarsFilterToggled ? true : false; @@ -693,7 +708,7 @@ define(function (require) { } break; case 'anyExperimentFilterBtn': - if(!this.state.anyExperimentFilterToggled){ + if (!this.state.anyExperimentFilterToggled) { // auto-toggle recording if state vars filter is toggled (can only look at recorded state vars for external experiments) otherwise leave as is var recordingToggleStatus = this.state.visualFilterToggled ? true : this.state.recordedFilterToggled; @@ -708,7 +723,7 @@ define(function (require) { } break; case 'anyProjectFilterBtn': - if(!this.state.anyProjectFilterToggled){ + if (!this.state.anyProjectFilterToggled) { // auto-toggle recording if state vars filter is toggled (can only look at recorded state vars for external projects/experiments) otherwise leave as is var recordingToggleStatus = this.state.visualFilterToggled ? true : this.state.recordedFilterToggled; @@ -731,24 +746,24 @@ define(function (require) { // this will cause the control panel to refresh data based on injected filter handler var filterHandler = this.props.filterHandler; - if(this.state.visualFilterToggled){ + if (this.state.visualFilterToggled) { filterHandler('VISUAL_INSTANCES'); - } else if(this.state.stateVarsFilterToggled){ - if(this.state.activeExperimentFilterToggled && this.state.recordedFilterToggled){ + } else if (this.state.stateVarsFilterToggled) { + if (this.state.activeExperimentFilterToggled && this.state.recordedFilterToggled) { filterHandler('ACTIVE_RECORDED_STATE_VARIABLES'); - } else if(this.state.activeExperimentFilterToggled && !this.state.recordedFilterToggled){ + } else if (this.state.activeExperimentFilterToggled && !this.state.recordedFilterToggled) { filterHandler('ACTIVE_STATE_VARIABLES'); - } else if(this.state.anyExperimentFilterToggled){ + } else if (this.state.anyExperimentFilterToggled) { filterHandler('ANY_EXPERIMENT_RECORDED_STATE_VARIABLES'); - } else if(this.state.anyProjectFilterToggled){ + } else if (this.state.anyProjectFilterToggled) { filterHandler('ANY_PROJECT_GLOBAL_STATE_VARIABLES'); } - } else if (this.state.paramsFilterToggled){ - if(this.state.activeExperimentFilterToggled){ + } else if (this.state.paramsFilterToggled) { + if (this.state.activeExperimentFilterToggled) { filterHandler('ACTIVE_PARAMETERS'); - } else if(this.state.anyExperimentFilterToggled){ + } else if (this.state.anyExperimentFilterToggled) { filterHandler('ANY_EXPERIMENT_PARAMETERS'); - } else if(this.state.anyProjectFilterToggled){ + } else if (this.state.anyProjectFilterToggled) { filterHandler('ANY_PROJECT_PARAMETERS'); } } @@ -758,7 +773,9 @@ define(function (require) { var that = this; var vizConfig = { id: 'visualInstancesFilterBtn', - condition: function(){return that.state.visualFilterToggled;}, + condition: function () { + return that.state.visualFilterToggled; + }, true: { icon: 'gpt-3dshape', action: '', @@ -766,7 +783,7 @@ define(function (require) { tooltip: 'Visual objects' }, false: { - icon: 'gpt-3dshape', + icon: 'gpt-3dshape', action: '', label: '', tooltip: 'Visual objects' @@ -775,7 +792,9 @@ define(function (require) { }; var stateVarConfig = { id: 'stateVariablesFilterBtn', - condition: function(){return that.state.stateVarsFilterToggled;}, + condition: function () { + return that.state.stateVarsFilterToggled; + }, true: { icon: 'fa fa-superscript', action: '', @@ -792,7 +811,9 @@ define(function (require) { }; var paramConfig = { id: 'parametersFilterBtn', - condition: function(){return that.state.paramsFilterToggled;}, + condition: function () { + return that.state.paramsFilterToggled; + }, true: { icon: 'fa fa-sign-in', action: '', @@ -800,7 +821,7 @@ define(function (require) { tooltip: 'Parameters' }, false: { - icon: 'fa fa-sign-in', + icon: 'fa fa-sign-in', action: '', label: '', tooltip: 'Parameters' @@ -809,7 +830,9 @@ define(function (require) { }; var activeConfig = { id: 'activeExperimentFilterBtn', - condition: function(){return that.state.activeExperimentFilterToggled;}, + condition: function () { + return that.state.activeExperimentFilterToggled; + }, true: { icon: 'gpt-activeExp', action: '', @@ -817,7 +840,7 @@ define(function (require) { label: '' }, false: { - icon: 'gpt-activeExp', + icon: 'gpt-activeExp', action: '', tooltip: 'Active Experiment', label: '' @@ -826,16 +849,18 @@ define(function (require) { }; var anyExpConfig = { id: 'anyExperimentFilterBtn', - condition: function(){return that.state.anyExperimentFilterToggled;}, - tooltipPosition: { my: "center bottom",at: "center-50 top-10"}, + condition: function () { + return that.state.anyExperimentFilterToggled; + }, + tooltipPosition: {my: "center bottom", at: "center-50 top-10"}, true: { - icon: 'fa fa-flask', + icon: 'fa fa-flask', action: '', tooltip: 'Look in any Experiment for the current project', label: '' }, false: { - icon: 'fa fa-flask', + icon: 'fa fa-flask', action: '', tooltip: 'Look in any Experiment for the current project', label: '' @@ -844,8 +869,10 @@ define(function (require) { }; var anyProjConfig = { id: 'anyProjectFilterBtn', - condition: function(){return that.state.anyProjectFilterToggled;}, - tooltipPosition: { my: "center bottom",at: "center-50 top-10"}, + condition: function () { + return that.state.anyProjectFilterToggled; + }, + tooltipPosition: {my: "center bottom", at: "center-50 top-10"}, true: { icon: 'fa fa-globe', action: '', @@ -853,7 +880,7 @@ define(function (require) { label: '' }, false: { - icon: 'fa fa-globe', + icon: 'fa fa-globe', action: '', tooltip: 'Look in any of your projects', label: '' @@ -862,7 +889,9 @@ define(function (require) { }; var recordedConfig = { id: 'recordedFilterBtn', - condition: function(){return that.state.recordedFilterToggled;}, + condition: function () { + return that.state.recordedFilterToggled; + }, true: { icon: 'fa fa-dot-circle-o', action: '', @@ -870,7 +899,7 @@ define(function (require) { label: '' }, false: { - icon: 'fa fa-dot-circle-o', + icon: 'fa fa-dot-circle-o', action: '', tooltip: 'Recorded variables', label: '' @@ -883,27 +912,27 @@ define(function (require) {
                ) @@ -966,7 +995,7 @@ define(function (require) { "label": "Selected", "tooltip": "Deselect" }, - },"visibility": { + }, "visibility": { "condition": "GEPPETTO.SceneController.isVisible($instances$)", "false": { "id": "visibility", @@ -1017,29 +1046,31 @@ define(function (require) { }, "Common": {} }; - var defaultDataFilter = function(entities){ + var defaultDataFilter = function (entities) { return GEPPETTO.ModelFactory.getAllInstancesWithCapability(GEPPETTO.Resources.VISUAL_CAPABILITY, entities); }; //Control panel initialization - var createPlotButtonMenuItems = function(projectId, experimentId, instance){ + var createPlotButtonMenuItems = function (projectId, experimentId, instance) { var menuButtonItems = []; var controller = GEPPETTO.WidgetFactory.getController(GEPPETTO.Widgets.PLOT); var plots = controller.getWidgets() - .filter(function(plot) { return !controller.isColorbar(plot); }); - if(plots.length > 0){ - for(var i =0 ; i 0) { + for (var i = 0; i < plots.length; i++) { menuButtonItems.push({ - label: "Add to " +plots[i].getId(), - action:"GEPPETTO.ControlPanel.plotController.plotStateVariable(" + projectId + "," + experimentId + ",'" + instance + "'," + plots[i].getId() + ")", + label: "Add to " + plots[i].getId(), + action: "GEPPETTO.ControlPanel.plotController.plotStateVariable(" + projectId + "," + experimentId + ",'" + instance + "'," + plots[i].getId() + ")", value: "plot_variable" }); } - }else{ + } else { //add default item menuButtonItems.push({ label: "Add new plot ", - action:"GEPPETTO.ControlPanel.plotController.plotStateVariable(" + projectId + "," + experimentId + ",'" + instance + "')", + action: "GEPPETTO.ControlPanel.plotController.plotStateVariable(" + projectId + "," + experimentId + ",'" + instance + "')", value: "plot_variable" }); } @@ -1200,9 +1231,9 @@ define(function (require) { }, //dynamic menu button, no initial list of items, and adds menu items on the go as plots are created "plot2": { - "menu" :true, - "menuMaker" : createPlotButtonMenuItems, - "actions" :["GEPPETTO.ControlPanel.refresh();"], + "menu": true, + "menuMaker": createPlotButtonMenuItems, + "actions": ["GEPPETTO.ControlPanel.refresh();"], "showCondition": "GEPPETTO.ExperimentsController.isLocalWatchedInstanceOrExternal($projectId$, $experimentId$, '$instance$');", "id": "plot2", "icon": "gpt-addplot", @@ -1215,7 +1246,7 @@ define(function (require) { var instancesControls = { "Common": [], "VisualCapability": ['color', 'randomcolor', 'visibility', 'zoom'], - "StateVariableCapability": ['watch', 'plot','plot2'] + "StateVariableCapability": ['watch', 'plot', 'plot2'] }; // state variables config (treated as potential instances) @@ -1318,9 +1349,9 @@ define(function (require) { }, //dynamic menu button, no initial list of items, and adds menu items on the go as plots are created "plot2": { - "menu" :true, - "menuMaker" : createPlotButtonMenuItems, - "actions" :["GEPPETTO.ControlPanel.refresh();"], + "menu": true, + "menuMaker": createPlotButtonMenuItems, + "actions": ["GEPPETTO.ControlPanel.refresh();"], "showCondition": "GEPPETTO.ExperimentsController.isLocalWatchedInstanceOrExternal($projectId$, $experimentId$, '$instance$');", "id": "plot2", "icon": "gpt-addplot", @@ -1329,7 +1360,7 @@ define(function (require) { } } }; - var stateVariablesControls = { "Common": ['watch', 'plot', 'plot2'] }; + var stateVariablesControls = {"Common": ['watch', 'plot', 'plot2']}; // parameters config (treated as potential instances) var parametersColMeta = [ @@ -1410,22 +1441,22 @@ define(function (require) { "visible": false } ]; - var paramsCols = ['name', 'value']; + var paramsCols = ['name', 'value']; var paramsColsWithExperiment = ['name', 'value', 'experimentName']; var paramsColsWithProjectAndExperiment = ['name', 'value', 'projectName', 'experimentName']; var parametersControlsConfig = {}; - var parametersControls = { "Common": [] }; + var parametersControls = {"Common": []}; var ControlPanel = React.createClass({ displayName: 'ControlPanel', - refresh: function() { - var self = this; - var callback = function(){ + refresh: function () { + var self = this; + var callback = function () { self.forceUpdate(); GEPPETTO.trigger("control_panel_refresh"); - }; - GEPPETTO.ProjectsController.refreshUserProjects(callback); + }; + GEPPETTO.ProjectsController.refreshUserProjects(callback); }, getInitialState: function () { @@ -1454,35 +1485,35 @@ define(function (require) { this.setState({columnMeta: colMeta}); }, - addData: function(instances){ - if(instances!= undefined && instances.length>0){ - - var columnMeta = this.state.columnMeta; - - // filter new records with data filter - var records = this.state.dataFilter(instances); - - // grab existing input - var gridInput = this.state.data; - - for (var i = 0; i < records.length; i++) { - var gridRecord = {}; + addData: function (instances) { + if (instances != undefined && instances.length > 0) { + + var columnMeta = this.state.columnMeta; + + // filter new records with data filter + var records = this.state.dataFilter(instances); + + // grab existing input + var gridInput = this.state.data; + + for (var i = 0; i < records.length; i++) { + var gridRecord = {}; var entityPath = records[i].getPath(); // check for duplicates var isDuplicate = false; - for(var k=0; k0){ + deleteData: function (instancePaths) { + if (instancePaths != undefined && instancePaths.length > 0) { // grab existing input var gridInput = this.state.data; var newGridInput = []; // remove unwanted instances from grid input - for(var i=0; i + ); } var filterMarkup = ''; if (this.props.useBuiltInFilters === true) { filterMarkup = ( - + ); } // figure out if we are to use infinite scrolling for results and store in state var infiniteScroll = true; - if(this.props.enablePagination != undefined) { + if (this.props.enablePagination != undefined) { infiniteScroll = !this.props.enablePagination; } @@ -1928,8 +1975,8 @@ define(function (require) { {menuButtonMarkup} {filterMarkup} + showFilter={true} showSettings={false} enableInfiniteScroll={infiniteScroll} resultsPerPage={this.props.resultsPerPage} + bodyHeight={400} useGriddleStyles={false} columnMetadata={this.state.columnMeta}/>
                ); } From 1adfe5bf1af1d4e0a95d540e2a427a1fd0d4cf5a Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 19:38:59 +0100 Subject: [PATCH 168/339] More fixes. --- .../webapp/js/components/interface/3dCanvas/ThreeDEngine.js | 4 ++-- .../webapp/js/components/interface/spotlight/spotlight.js | 2 +- .../js/components/widgets/plot/controllers/PlotsController.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index 124fbf836..46e9a765e 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -29,7 +29,7 @@ define(['jquery'], function () { function ThreeDEngine(container, viewerId) { this.container = container; - this.colorController = require('./ColorController'); + this.colorController = new(require('./ColorController'))(); this.viewerId = viewerId; //Engine components @@ -2585,7 +2585,7 @@ define(['jquery'], function () { // Update visualization feature for a mesh if (mergedMesh.ghosted) { - this.ghostEffect([groupMesh], true); + this.unselectedTransparent([groupMesh], true); } if (mergedMesh.selected) { this.selectInstance(groupName); diff --git a/src/main/webapp/js/components/interface/spotlight/spotlight.js b/src/main/webapp/js/components/interface/spotlight/spotlight.js index 636970f0d..5af1fe255 100644 --- a/src/main/webapp/js/components/interface/spotlight/spotlight.js +++ b/src/main/webapp/js/components/interface/spotlight/spotlight.js @@ -513,7 +513,7 @@ define(function (require) { } if (useSelection) { - var selection = GEPPETTO.G.getSelection(); + var selection = GEPPETTO.SceneController.getSelection(); if (selection.length > 0) { this.openToInstance(selection[selection.length - 1]); return; diff --git a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js index 95e75bca2..790a67f2f 100644 --- a/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js +++ b/src/main/webapp/js/components/widgets/plot/controllers/PlotsController.js @@ -110,7 +110,7 @@ define(function(require) { else if (event == GEPPETTO.Events.Lit_entities_changed) { for (var i = 0; i < this.widgets.length; i++) { var plot = this.widgets[i]; - if (GEPPETTO.G.litUpInstances.length == 0 && this.isColorbar(plot)) { + if (this.isColorbar(plot)) { plot.destroy(); } } From fa52573446cc8fedd6a2bb7231eb72924a770b0b Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 20:27:48 +0100 Subject: [PATCH 169/339] Fix for resize. Add method to retrieve colorFunctionInstances --- .../components/interface/3dCanvas/Canvas.js | 23 +++++++++++++++++-- .../interface/3dCanvas/ColorController.js | 8 +++++++ .../interface/3dCanvas/SceneController.js | 21 +++++++++++++---- .../interface/3dCanvas/ThreeDEngine.js | 3 --- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 73f29b524..0f2c9b6af 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -35,9 +35,11 @@ define(function (require) { var that = this; GEPPETTO.on(GEPPETTO.Events.Instances_created, function (instances) { that.engine.updateSceneWithNewInstances(instances); + that.resetCamera(); }); GEPPETTO.on(GEPPETTO.Events.Instance_deleted, function (instance) { that.engine.removeFromScene(instance); + that.resetCamera(); }); return this; }, @@ -205,6 +207,15 @@ define(function (require) { return this; }, + /** + * Returns all the instances that are being listened to + * + * @return {Array} + */ + getColorFunctionInstances: function () { + return this.engine.colorController.getColorFunctionInstances(); + }, + /** * Shows the visual groups associated to the passed instance * @param instance @@ -308,15 +319,23 @@ define(function (require) { Detector.addGetWebGLMessage(); } else { this.container = $("#" + this.props.id + "_component").get(0); - this.setContainerDimensions(); + var [width, height] = this.setContainerDimensions(); this.engine = new ThreeDEngine(this.container, this.props.id); + this.engine.setSize(width, height); + GEPPETTO.SceneController.add3DCanvas(this); + var that = this; $("#" + this.props.id).on("dialogresizestop resizeEnd", function (event, ui) { - var [width, height] = that.setContainerDimensions() + var [width, height] = that.setContainerDimensions(); that.engine.setSize(width, height); }); + window.addEventListener('resize', function () { + var [width, height] = that.setContainerDimensions(); + that.engine.setSize(width, height); + }, false); + } }, diff --git a/src/main/webapp/js/components/interface/3dCanvas/ColorController.js b/src/main/webapp/js/components/interface/3dCanvas/ColorController.js index aa2dab622..bb45c2b5b 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ColorController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ColorController.js @@ -119,6 +119,14 @@ define(['jquery'], function () { } }, + /** + * + * @returns {Array} + */ + getColorFunctionInstances: function () { + return this.litUpInstances; + }, + /** * Modulates the color of an aspect visualization, given a watched node * and a color function. The color function should receive diff --git a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js index 371273d3c..57dd63836 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js @@ -332,11 +332,12 @@ define(['jquery'], function () { }, /** - * Associate a color function to a group of instances for all the existing canvas + * Associate a color function to a group of instances for all the existing canvas. + * The visual instance that will be colored is the one associated with the composite type + * which contains the state variables passed as parameter. * - * @param instances - The instances we want to change the color of + * @param instances - The state variable instances we are listening to * @param colorfn - The function to be used to modulate the color - * @return {canvasComponent} */ addColorFunction: function (instances, colorfn) { for (var i = 0; i < this.canvasComponents.length; i++) { @@ -348,7 +349,6 @@ define(['jquery'], function () { * Remove a previously associated color function for all existing canvas * * @param instances - * @return {canvasComponent} */ removeColorFunction: function (instances) { for (var i = 0; i < this.canvasComponents.length; i++) { @@ -357,6 +357,19 @@ define(['jquery'], function () { }, + /** + * Returns all the instances that are being listened to + * + * @return {Array} + */ + getColorFunctionInstances: function () { + var instances=[]; + for (var i = 0; i < this.canvasComponents.length; i++) { + instances.concat(this.canvasComponents[i].getColorFunctionInstances()); + } + return instances; + }, + }; return SceneController; diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index 46e9a765e..0f42e0928 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -249,8 +249,6 @@ define(['jquery'], function () { var color = new THREE.Color(this.backgroundColor); //this.renderer.setClearColor(color, 1); - var width = $(this.container).width(); - var height = $(this.container).height(); this.renderer.setPixelRatio(window.devicePixelRatio); this.renderer.autoClear = false; @@ -259,7 +257,6 @@ define(['jquery'], function () { var renderModel = new THREE.RenderPass(this.scene, this.camera); this.composer = new THREE.EffectComposer(this.renderer); - this.setSize(width, height); if (shaders) { var effectBloom = new THREE.BloomPass(0.75); From 0529d8c12a193db14bab2d90f9e041cdb2fcf6ec Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Thu, 4 May 2017 22:24:19 +0100 Subject: [PATCH 170/339] changing regexp for windows --- src/main/webapp/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index 0a02ea79b..f3d07be1a 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -141,7 +141,7 @@ module.exports = { module: { loaders: [ { - test: /\.(js)$/, exclude: [/node_modules\/(?!(ami.js)\/).*/, /build/, /\.bundle/], loader: ['babel-loader'], + test: /\.(js)$/, exclude: [/node_modules(?!(\/|\\)ami.js)/, /build/, /\.bundle/], loader: ['babel-loader'], query: { presets: ['react', 'es2015'] } From 4323fd316f4dbbf89e1cc935e36b0d2b3e067384 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 23:15:59 +0100 Subject: [PATCH 171/339] Lighting up fixes --- .../webapp/js/communication/MessageHandler.js | 33 +++++++++------ .../interface/3dCanvas/ColorController.js | 41 ++++++++++++++++++- .../interface/3dCanvas/ThreeDEngine.js | 2 +- .../js/pages/geppetto/GEPPETTO.Events.js | 17 ++------ 4 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index cf017c57d..64048301d 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -536,26 +536,35 @@ define(function(require) { for (var i = 0; i < instancePath.length; i++) { try { - instances[i] = eval(InstanceVarName + instancePath[i]); - if (instances[i] == undefined) { + var potentialVar = eval(InstanceVarName + instancePath[i]); + if(potentialVar!=undefined){ + if (override) { + GEPPETTO.ModelFactory.deleteInstance(instances[i]); + Instances.addInstances(instancePath[i]); + instances.push(eval(InstanceVarName + instancePath[i])); + } + else{ + instances.push(potentialVar); + } + } + else { if (create) { Instances.addInstances(instancePath[i]); - instances[i] = eval(InstanceVarName + instancePath[i]); + instances.push(eval(InstanceVarName + instancePath[i])); } - } else if (override) { - GEPPETTO.ModelFactory.deleteInstance(instances[i]); - Instances.addInstances(instancePath[i]); - instances[i] = eval(InstanceVarName + instancePath[i]); } } catch (e) { if (create) { - Instances.addInstances(instancePath[i]); - instances[i] = eval(InstanceVarName + instancePath[i]); + try{ + + Instances.addInstances(instancePath[i]); + instances[i] = eval(InstanceVarName + instancePath[i]); + } + catch(e){ + throw ("The instance " + instancePath[i] + " does not exist in the current model"); + } } } - if (instances[i] == undefined && create) { - throw ("The instance " + instancePath[i] + " does not exist in the current model"); - } } if (instances.length == 1 && !arrayParameter) { diff --git a/src/main/webapp/js/components/interface/3dCanvas/ColorController.js b/src/main/webapp/js/components/interface/3dCanvas/ColorController.js index bb45c2b5b..656f77c1c 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ColorController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ColorController.js @@ -10,6 +10,21 @@ define(['jquery'], function () { this.litUpInstances = []; this.colorFunctionSet = false; + var that = this; + + GEPPETTO.on(GEPPETTO.Events.Experiment_update, function (parameters) { + if (parameters.playAll != null || parameters.step != undefined) { + //update scene brightness + for (var key in that.listeners) { + if (that.listeners[key] != null || undefined) { + for (var i = 0; i < that.listeners[key].length; i++) { + that.listeners[key][i](Instances.getInstance(key), parameters.step); + } + } + } + } + }); + }; @@ -146,7 +161,7 @@ define(['jquery'], function () { modulations.push(instance.getInstancePath() + "." + visualObjects[voIndex]); } - GEPPETTO.SceneController.splitGroups(instance, elements); + this.engine.splitGroups(instance, elements); } else { modulations.push(instance.getInstancePath()); @@ -179,15 +194,37 @@ define(['jquery'], function () { * @param colorfn */ addColorListener: function (instance, modulation, colorfn) { + var that = this; GEPPETTO.trigger(GEPPETTO.Events.Lit_entities_changed); this.addOnNodeUpdatedCallback(modulation, function (stateVariableInstance, step) { if ((stateVariableInstance.getTimeSeries() != undefined) && (step < stateVariableInstance.getTimeSeries().length)) { - GEPPETTO.SceneController.lightUpEntity(instance, colorfn, stateVariableInstance.getTimeSeries()[step]); + that.colorInstance(instance, colorfn, stateVariableInstance.getTimeSeries()[step]); } }); }, + /** + * Light up the entity + * + * @param {Instance} + * instance - the instance to be lit + * @param {Float} + * intensity - the lighting intensity from 0 (no illumination) to 1 (full illumination) + */ + colorInstance: function (instance, colorfn, intensity) { + var threeObject; + if (instance in this.engine.meshes) { + threeObject = this.engine.meshes[instance]; + } + else { + threeObject = this.engine.splitMeshes[instance]; + } + + var [r,g,b] = colorfn(intensity); + threeObject.material.color.setRGB(r,g,b); + }, + /** * * @param varnode diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index 0f42e0928..9912dbe13 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -29,7 +29,7 @@ define(['jquery'], function () { function ThreeDEngine(container, viewerId) { this.container = container; - this.colorController = new(require('./ColorController'))(); + this.colorController = new(require('./ColorController'))(this); this.viewerId = viewerId; //Engine components diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js index d0620939c..3f84f7ec4 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js @@ -99,22 +99,11 @@ define(function (require) { GEPPETTO.WidgetsListener.update(GEPPETTO.Events.Experiment_play, parameters); }); - GEPPETTO.on(this.Experiment_update, function (parameters) { - if (parameters.playAll != null || parameters.step != undefined) { - //update scene brightness - for (var key in GEPPETTO.G.listeners) { - if (GEPPETTO.G.listeners[key] != null || undefined) { - for (var i = 0; i < GEPPETTO.G.listeners[key].length; i++) { - GEPPETTO.G.listeners[key][i](Instances.getInstance(key), parameters.step); - } - } - } - } - //notify widgets a restart of data is needed - GEPPETTO.WidgetsListener.update(GEPPETTO.Events.Experiment_update, parameters); + GEPPETTO.on(this.Experiment_stop, function (parameters) { }); - GEPPETTO.on(this.Experiment_stop, function (parameters) { + GEPPETTO.on(this.Experiment_update, function (parameters) { + GEPPETTO.WidgetsListener.update(GEPPETTO.Events.Experiment_update, parameters); }); GEPPETTO.on(this.Lit_entities_changed, function (parameters) { From cff330d3888c9fa3eda1835f9b60dc88766fa860 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 4 May 2017 17:04:33 -0700 Subject: [PATCH 172/339] removes lines for flickering interface when interacting with control panel buttons --- .../webapp/js/components/interface/controlPanel/controlpanel.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js index 0acf721da..155b93c2d 100644 --- a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js +++ b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js @@ -1454,7 +1454,6 @@ define(function (require) { var self = this; var callback = function () { self.forceUpdate(); - GEPPETTO.trigger("control_panel_refresh"); }; GEPPETTO.ProjectsController.refreshUserProjects(callback); }, From 41b5fe264b81af691699e148f732acf8e96d406d Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Thu, 4 May 2017 23:21:20 +0100 Subject: [PATCH 173/339] Canvas3D -> Canvas --- .../extensions/geppetto-default/ComponentsInitialization.js | 2 +- src/main/webapp/js/components/ComponentFactory.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js index e6d73a57d..399e5c5c4 100644 --- a/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js +++ b/src/main/webapp/extensions/geppetto-default/ComponentsInitialization.js @@ -19,7 +19,7 @@ define(function (require) { }; //Canvas initialisation - GEPPETTO.ComponentFactory.addComponent('CANVAS3D', {}, document.getElementById("sim"), function () { + GEPPETTO.ComponentFactory.addComponent('CANVAS', {}, document.getElementById("sim"), function () { this.displayAllInstances(); }); diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index b47e6bce2..e71070c1a 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -41,7 +41,7 @@ define(function (require) { 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer', 'CAROUSEL': 'interface/carousel/Carousel', - 'CANVAS3D': 'interface/3dCanvas/Canvas' + 'CANVAS': 'interface/3dCanvas/Canvas' // 'PLOT': 'interface/plot/Plot', // 'POPUP': 'interface/popup/Popup' }, From c2f281c29cfbe83230e149eb0cf8b51ec7d62fa8 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Thu, 4 May 2017 23:52:39 +0100 Subject: [PATCH 174/339] exclude node_modules from war --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index e9621404b..b666a267e 100644 --- a/pom.xml +++ b/pom.xml @@ -352,6 +352,7 @@ maven-war-plugin 2.3 + node_modules/** WEB-INF/classes/lib/*.jar, WEB-INF/lib/*.jar, node_modules/**, node/** org.geppetto.frontend From b343c2ea205f051aaaf7e9776f99f5ca4e773b23 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Fri, 5 May 2017 00:08:55 +0100 Subject: [PATCH 175/339] JS doc fix --- pom.xml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index e9621404b..45ca36cab 100644 --- a/pom.xml +++ b/pom.xml @@ -392,19 +392,8 @@ true - ${basedir}/src/main/webapp/js/geppetto-objects - ${basedir}/src/main/webapp/js/components/widgets - ${basedir}/src/main/webapp/js/model - ${basedir}/src/main/webapp/js/3d_visualization + ${basedir}/src/main/webapp/js/ - - src/main/webapp/js/GEPPETTO.Console.js - src/main/webapp/js/GEPPETTO.FE.js - src/main/webapp/js/GEPPETTO.JSEditor.js - src/main/webapp/js/GEPPETTO.Main.js - src/main/webapp/js/GEPPETTO.MenuManager.js - src/main/webapp/js/GEPPETTO.ScriptRunner.js - ${basedir}/src/main/webapp/jsdocs From 5c1a428937b914e1bbc9061a58bf6a51a7c53259 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 4 May 2017 20:52:02 -0700 Subject: [PATCH 176/339] bug fixing and qunit testing --- .../webapp/js/communication/MessageHandler.js | 1 - .../geppettoProject/ExperimentsController.js | 1 - .../js/pages/tests/casperjs/CoreTests.js | 39 +++++++++---------- .../tests/qunit/ExternalSimulatorTests.js | 2 +- .../js/pages/tests/qunit/NeuronalTests.js | 2 +- .../js/pages/tests/qunit/PersistenceTests.js | 2 +- 6 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 64048301d..52004109c 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -619,7 +619,6 @@ define(function(require) { newExperiment.setParent(window.Project); newExperiment.setActive(); - G.unSelectAll(); GEPPETTO.ExperimentsController.closeCurrentExperiment(); window.Project.setActiveExperiment(newExperiment); GEPPETTO.Console.log(GEPPETTO.Resources.EXPERIMENT_CREATED); diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index c19543b14..6680a90c1 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -134,7 +134,6 @@ define(function (require) { }, setActive:function(experiment){ - G.unSelectAll(); GEPPETTO.ExperimentsController.closeCurrentExperiment(); var parameters = {}; parameters["experimentId"] = experiment.id; diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index e464244c9..4c50666fd 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -55,10 +55,10 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertTitle("geppetto", "geppetto title is ok"); test.assertUrlMatch(/load_project_from_id=1/, "project load attempted"); test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); hhcellTest(test); - },null,20000); + },null,30000); }); /**Tests C302 project**/ @@ -68,7 +68,7 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertTitle("geppetto", "geppetto title is ok"); test.assertUrlMatch(/load_project_from_id=6/, "project load attempted"); test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); c302Test(test); },null,450000); @@ -81,10 +81,10 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertTitle("geppetto", "geppetto title is ok"); test.assertUrlMatch(/load_project_from_id=5/, "project load attempted"); test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); acnetTest(test); - },null,30000); + },null,40000); }); /**Tests CA1 project**/ @@ -94,10 +94,10 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertTitle("geppetto", "geppetto title is ok"); test.assertUrlMatch(/load_project_from_id=3/, "project load attempted"); test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); ca1Test(test); - },null,30000); + },null,40000); }); /**Tests EyeWire project**/ @@ -107,9 +107,9 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertTitle("geppetto", "geppetto title is ok"); test.assertUrlMatch(/load_project_from_id=9/, "project load attempted"); test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); - }, null, 30000); + }, null, 50000); }); /**Tests Pharyngeal project**/ @@ -119,10 +119,10 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertTitle("geppetto", "geppetto title is ok"); test.assertUrlMatch(/load_project_from_id=58/, "project load attempted"); test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); pharyngealTest(test); - },null,30000); + },null,50000); }); /**Tests NWB project**/ @@ -132,10 +132,10 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertTitle("geppetto", "geppetto title is ok"); test.assertUrlMatch(/load_project_from_id=18/, "project load attempted"); test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); nwbSampleTest(test); - },null,30000); + },null,50000); }); // casper.thenOpen(urlBase+baseFollowUp+cElegansConnectome,function() { @@ -438,7 +438,7 @@ function ca1Test(test){ test.assertTitle("geppetto", "geppetto title is ok"); test.assertUrlMatch(/load_project_from_id=3/, "project load attempted"); test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="camera-controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial camera controls"); casper.then(function () { @@ -482,13 +482,12 @@ function ca1Test(test){ //press enter this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); - casper.waitUntilVisible('div#spotlight', function () { + casper.wait(1000, function () { casper.then(function () { - this.echo("Waiting to see if the Plot variables button becomes visible"); - casper.waitUntilVisible('button#plot', function () { - test.assertVisible('button#plot', "Plot variables icon correctly visible"); - this.echo("Plot variables button became visible correctly"); - }, null, 8000); + this.echo("Waiting to see if the Plot and watch variable buttons becomes visible"); + test.assertDoesntExist('button#plot', "Plot variables icon correctly invisible"); + test.assertDoesntExist('button#watch', "Watch button correctly hidden"); + this.echo("Variables button are hidden correctly"); }); }); }); diff --git a/src/main/webapp/js/pages/tests/qunit/ExternalSimulatorTests.js b/src/main/webapp/js/pages/tests/qunit/ExternalSimulatorTests.js index c01bea0c8..0bff593dd 100644 --- a/src/main/webapp/js/pages/tests/qunit/ExternalSimulatorTests.js +++ b/src/main/webapp/js/pages/tests/qunit/ExternalSimulatorTests.js @@ -1,7 +1,7 @@ define(function (require) { var QUnit = require("qunitjs"); - + require('../../../components/ComponentFactory')(GEPPETTO); /** * Calls "start()" from QUnit to start qunit tests, closes socket and clears * handlers. Method is called from each test. diff --git a/src/main/webapp/js/pages/tests/qunit/NeuronalTests.js b/src/main/webapp/js/pages/tests/qunit/NeuronalTests.js index 6117beae3..090cb51d0 100644 --- a/src/main/webapp/js/pages/tests/qunit/NeuronalTests.js +++ b/src/main/webapp/js/pages/tests/qunit/NeuronalTests.js @@ -1,7 +1,7 @@ define(function (require) { var QUnit = require("qunitjs"); - + require('../../../components/ComponentFactory')(GEPPETTO); /** * Closes socket and clears handlers. Method is called from each test. */ diff --git a/src/main/webapp/js/pages/tests/qunit/PersistenceTests.js b/src/main/webapp/js/pages/tests/qunit/PersistenceTests.js index 28fcc105e..fbf18e589 100644 --- a/src/main/webapp/js/pages/tests/qunit/PersistenceTests.js +++ b/src/main/webapp/js/pages/tests/qunit/PersistenceTests.js @@ -1,7 +1,7 @@ define(function (require) { var QUnit = require("qunitjs"); - + require('../../../components/ComponentFactory')(GEPPETTO); /** * Calls "start()" from QUnit to start qunit tests, closes socket and clears * handlers. Method is called from each test. From 9d7ece41c109253a809513b160329a357648f869 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Fri, 5 May 2017 11:36:51 +0100 Subject: [PATCH 177/339] Fixes for tutorial component --- .../interface/tutorial/TutorialModule.js | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index 6f1739fd1..9cfedb096 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -132,11 +132,11 @@ define(function (require) { }, close : function(){ - $(this.__container).parent().hide(); + this.dialog.parent().hide(); }, open : function(started){ - var p=$(this.__container).parent(); + var p=this.dialog.parent(); p.show(); var self = this; @@ -145,11 +145,11 @@ define(function (require) { var height =self.getActiveTutorial()["height"]; if(height!=undefined){ p.height(height+"px"); - $(self.__container).css("height",height+"px"); + this.dialog.css("height",height+"px"); } if(width!=undefined){ p.width(width+"px"); - $(self.__container).css("width",width+"px"); + this.dialog.css("width",width+"px"); } }; @@ -237,13 +237,13 @@ define(function (require) { event.stopPropagation(); }); - var dialog = $(this.__container).parent(); + var dialog = this.dialog.parent(); var closeButton=dialog.find("button.ui-dialog-titlebar-close"); closeButton.off("click"); closeButton.click(this.close); dialog.find("div.ui-dialog-titlebar").prepend(button); $(button).addClass("widget-title-bar-button"); - $(this.__container).css("overflow","scroll"); + this.dialog.css("overflow","scroll"); } @@ -251,17 +251,17 @@ define(function (require) { var screenWidth = $( window ).width(); var screenHeight = $( window ).height(); - var left = (screenWidth/2) - ($(this.__container).parent().width()/2); - var top = (screenHeight/2) - ($(this.__container).parent().height()/2); - - $(this.__container).parent().css("top",top+"px"); - $(this.__container).parent().css("left",left+"px"); + var left = (screenWidth/2) - (this.dialog.parent().width()/2); + var top = (screenHeight/2) - (this.dialog.parent().height()/2); + + this.dialog.parent().css("top",top+"px"); + this.dialog.parent().css("left",left+"px"); }, componentDidMount:function(){ this.close(); var self = this; - + //launches specific tutorial is experiment is loaded GEPPETTO.on(GEPPETTO.Events.Model_loaded,function(){ if(!self.dontShowTutorial){ @@ -334,7 +334,7 @@ define(function (require) { var step = activeTutorial.steps[this.state.currentStep]; - var dialog = $(this.__container).parent(); + var dialog = this.dialog.parent(); dialog.find(".ui-dialog-title").html(step.title); var iconClass = ""; if(step.icon!=null && step.icon!=undefined && step.icon!=""){ @@ -351,11 +351,11 @@ define(function (require) { if(height!=undefined){ dialog.height(height+"px"); - $(this.__container).css("height",height+"px"); + this.dialog.css("height",height+"px"); } if(width!=undefined){ dialog.width(width+"px"); - $(this.__container).css("width",width+"px"); + this.dialog.css("width",width+"px"); } return
                From 30c0a060c34b83611587d8a2c3ad4d13d3d4f6d8 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Fri, 5 May 2017 17:36:06 +0100 Subject: [PATCH 178/339] start using react form from npm --- src/main/webapp/js/components/interface/form/Form.js | 2 +- .../components/interface/form/react-jsonschema-form.js | 4 ---- src/main/webapp/package.json | 9 ++++++--- 3 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 src/main/webapp/js/components/interface/form/react-jsonschema-form.js diff --git a/src/main/webapp/js/components/interface/form/Form.js b/src/main/webapp/js/components/interface/form/Form.js index 13a8f5b86..316f32008 100644 --- a/src/main/webapp/js/components/interface/form/Form.js +++ b/src/main/webapp/js/components/interface/form/Form.js @@ -7,7 +7,7 @@ define(function (require) { document.getElementsByTagName("head")[0].appendChild(link); var React = require('react'); - var reactJsonSchemaForm = require('./react-jsonschema-form'); + var reactJsonSchemaForm = require('react-jsonschema-form'); var Form = reactJsonSchemaForm.default; diff --git a/src/main/webapp/js/components/interface/form/react-jsonschema-form.js b/src/main/webapp/js/components/interface/form/react-jsonschema-form.js deleted file mode 100644 index 674875d2c..000000000 --- a/src/main/webapp/js/components/interface/form/react-jsonschema-form.js +++ /dev/null @@ -1,4 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define(["react"],t):"object"==typeof exports?exports.JSONSchemaForm=t(require("react")):e.JSONSchemaForm=t(e.React)}(this,function(e){return function(e){function t(n){if(r[n])return r[n].exports;var a=r[n]={exports:{},id:n,loaded:!1};return e[n].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var r={};return t.m=e,t.c=r,t.p="/dist/",t(0)}([function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=r(1),i=n(a);t["default"]=i["default"]},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},s=Object.assign||function(e){for(var t=1;t0?((0,S.setState)(r,{errors:t,errorSchema:n},function(){r.props.onError?r.props.onError(t):console.error("Form validation failed",t)}),{v:void 0}):void 0}();if("object"===("undefined"==typeof t?"undefined":u(t)))return t.v}r.props.onSubmit&&r.props.onSubmit(r.state),r.setState({status:"initial",errors:[],errorSchema:{}})},r.state=r.getStateFromProps(e),r}return o(t,e),l(t,[{key:"componentWillReceiveProps",value:function(e){this.setState(this.getStateFromProps(e))}},{key:"getStateFromProps",value:function(e){var t=this.state||{},r="schema"in e?e.schema:this.props.schema,n="uiSchema"in e?e.uiSchema:this.props.uiSchema,a="undefined"!=typeof e.formData,i=e.liveValidate||this.props.liveValidate,o=a&&!e.noValidate&&i,u=r.definitions,s=(0,S.getDefaultFormState)(r,e.formData,u),l=o?this.validate(s,r):{errors:t.errors||[],errorSchema:t.errorSchema||{}},c=l.errors,f=l.errorSchema,d=(0,S.toIdSchema)(r,n["ui:rootFieldId"],u);return{status:"initial",schema:r,uiSchema:n,idSchema:d,formData:s,edit:a,errors:c,errorSchema:f}}},{key:"shouldComponentUpdate",value:function(e,t){return(0,S.shouldRender)(this,e,t)}},{key:"validate",value:function r(e,t){var r=this.props.validate;return(0,w["default"])(e,t||this.props.schema,r)}},{key:"renderErrors",value:function(){var e=this.state,t=e.status,r=e.errors,n=this.props.showErrorList;return"editing"!==t&&r.length&&0!=n?f["default"].createElement(g["default"],{errors:r}):null}},{key:"getRegistry",value:function(){var e=this.props.SchemaField||m["default"],t=this.props.TitleField||p["default"],r=this.props.DescriptionField||v["default"],n=Object.assign({SchemaField:e,TitleField:t,DescriptionField:r},this.props.fields);return{fields:n,widgets:this.props.widgets||{},definitions:this.props.schema.definitions||{}}}},{key:"render",value:function(){var e=this.props,t=e.children,r=e.safeRenderCompletion,n=e.id,a=e.className,i=e.name,o=e.method,u=e.target,s=e.action,l=e.autocomplete,c=e.enctype,d=e.acceptcharset,m=this.state,h=m.schema,p=m.uiSchema,y=m.formData,v=m.errorSchema,b=m.idSchema,g=this.getRegistry(),S=g.fields.SchemaField;return f["default"].createElement("form",{className:a?a:"rjsf",id:n,name:i,method:o,target:u,action:s,autoComplete:l,encType:c,acceptCharset:d,onSubmit:this.onSubmit},this.renderErrors(),f["default"].createElement(S,{schema:h,uiSchema:p,errorSchema:v,idSchema:b,formData:y,onChange:this.onChange,registry:g,safeRenderCompletion:r}),t?t:f["default"].createElement("p",null,f["default"].createElement("button",{type:"submit",className:"btn btn-info"},"Submit")))}}]),t}(c.Component);E.defaultProps={uiSchema:{},noValidate:!1,liveValidate:!1,safeRenderCompletion:!1},t["default"]=E,t["default"]=E},function(t,r){t.exports=e},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}function a(e,t,r){var n=t["ui:field"];return"function"==typeof n?n:"string"==typeof n&&n in r?r[n]:C[e.type]||_["default"]}function i(e,t,r){return e?d["default"].createElement("label",{className:"control-label",htmlFor:r},t?e+A:e):null}function o(e){return e?"string"==typeof e?d["default"].createElement("p",{className:"help-block"},e):d["default"].createElement("div",{className:"help-block"},e):null}function u(e){var t=e.errors;return d["default"].createElement("div",null,d["default"].createElement("p",null),d["default"].createElement("ul",{className:"error-detail bs-callout bs-callout-info"},(t||[]).map(function(e,t){return d["default"].createElement("li",{className:"text-danger",key:t},e)})))}function s(e){var t=e.type,r=e.classNames,n=e.errorSchema,a=e.label,s=e.description,l=e.hidden,c=e.help,f=e.required,m=e.displayLabel,h=e.id,p=e.children;if(l)return p;var y=n.__errors,v=y&&y.length>0,b=["form-group","field","field-"+t,v?"field-error has-error":"",r].join(" ").trim();return d["default"].createElement("div",{className:b},m&&a?i(a,f,h):null,m&&s?d["default"].createElement(P["default"],{id:h+"__description",description:s}):null,p,v?d["default"].createElement(u,{errors:y}):d["default"].createElement("div",null),o(c))}function l(e){var t=e.uiSchema,r=e.errorSchema,n=e.idSchema,i=e.name,o=e.required,u=e.registry,l=u.definitions,f=u.fields,h=(0,m.retrieveSchema)(e.schema,l),p=a(h,t,f),y=Boolean(e.disabled||t["ui:disabled"]),v=Boolean(e.readonly||t["ui:readonly"]);if(0===Object.keys(h).length)return d["default"].createElement("div",null);var b=!0;return"array"===h.type&&(b=(0,m.isMultiSelect)(h)||(0,m.isFilesArray)(h,t)),"object"===h.type&&(b=!1),"boolean"!==h.type||t["ui:widget"]||(b=!1),t["ui:field"]&&(b=!1),d["default"].createElement(s,{label:e.schema.title||h.title||i,description:e.schema.description||h.description,errorSchema:r,hidden:"hidden"===t["ui:widget"],help:t["ui:help"],required:o,type:h.type,displayLabel:b,id:n.$id,classNames:t.classNames},d["default"].createElement(p,c({},e,{schema:h,disabled:y,readonly:v})))}Object.defineProperty(t,"__esModule",{value:!0});var c=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}function i(){return{fields:{SchemaField:r(3)["default"],TitleField:M["default"],DescriptionField:q["default"]},widgets:{},definitions:{}}}function o(e,t){return"undefined"==typeof e?t["default"]:e}function u(e,t){function r(e){return e.defaultProps=I({},e.defaultProps,{options:a}),e}var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2],a=arguments.length<=3||void 0===arguments[3]?{}:arguments[3],i=e.type,o=e.format;if("function"==typeof t)return r(t);if(c(t)){var s=t.component,l=t.options,f=I({},l,a);return u(e,s,n,f)}if("string"!=typeof t)throw new Error("Unsupported widget definition: "+("undefined"==typeof t?"undefined":k(t)));if(n.hasOwnProperty(t)){var d=n[t];return u(e,d,n,a)}if(!ye.hasOwnProperty(i))throw new Error("No alternative widget for type "+i);if(ye[i].hasOwnProperty(t)){var m=ye[i][t];return u(e,m,n,a)}if("string"===i&&ve.hasOwnProperty(o)){var h=ve[o];return u(e,h,n,a)}var p="string"===i&&o?"/"+o:"";throw new Error('No alternative widget "'+t+'" for type '+i+p)}function s(e,t){var r=arguments.length<=2||void 0===arguments[2]?{}:arguments[2],n=t;if(c(n)&&c(e["default"]))n=f(n,e["default"]);else if("default"in e)n=e["default"];else if("enum"in e&&Array.isArray(e["enum"]))n=e["enum"][0];else{if("$ref"in e){var a=g(e.$ref,r);return s(a,n,r)}y(e)&&(n=e.items.map(function(e){return s(e,void 0,r)}))}return"undefined"==typeof n&&(n=e["default"]),"object"===e.type?Object.keys(e.properties).reduce(function(t,a){return t[a]=s(e.properties[a],(n||{})[a],r),t},{}):n}function l(e,t){var r=arguments.length<=2||void 0===arguments[2]?{}:arguments[2];if(!c(e))throw new Error("Invalid schema: "+e);var n=S(e,r),a=s(n,e["default"],r);return"undefined"==typeof t?a:c(t)?f(a,t):t||a}function c(e){return"object"===("undefined"==typeof e?"undefined":k(e))&&null!==e&&!Array.isArray(e)}function f(e,t){var r=arguments.length<=2||void 0===arguments[2]?!1:arguments[2],n=Object.assign({},e);return Object.keys(t).reduce(function(n,a){var i=e[a],o=t[a];return e.hasOwnProperty(a)&&c(o)?n[a]=f(i,o,r):r&&Array.isArray(i)&&Array.isArray(o)?n[a]=i.concat(o):n[a]=o,n},n)}function d(e){if(/\.$/.test(e))return e;if(/\.0$/.test(e))return e;if(/.[^1-9]+/.test(e))return e;var t=Number(e),r="number"==typeof t&&!Number.isNaN(t);return r?t:e}function m(e,t){if(!Array.isArray(t))return e;if(t.length!==e.length)throw new Error("uiSchema order list length should match object properties length");var r=function(e){return[].slice.call(e).sort().toString()};if(r(t)!==r(e))throw new Error("uiSchema order list does not match object properties list");return t}function h(e){return Array.isArray(e.items["enum"])&&e.uniqueItems}function p(e,t){return"string"===e.items.type&&"data-url"===e.items.format||"files"===t["ui:widget"]}function y(e){return Array.isArray(e.items)&&e.items.length>0&&e.items.every(function(e){return c(e)})}function v(e){return e.additionalItems===!0&&console.warn("additionalItems=true is currently not supported"),c(e.additionalItems)}function b(e){return e["enum"].map(function(t,r){var n=e.enumNames&&e.enumNames[r]||String(t);return{label:n,value:t}})}function g(e){var t=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],r=/#\/definitions\/(.*)$/.exec(e);if(r&&r[1]&&t.hasOwnProperty(r[1]))return t[r[1]];throw new Error("Could not find a definition for "+e+".")}function S(e){var t=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];if(!e.hasOwnProperty("$ref"))return e;var r=g(e.$ref,t),n=(e.$ref,a(e,["$ref"]));return I({},r,n)}function O(e){return"[object Arguments]"===Object.prototype.toString.call(e)}function w(e,t){var r=arguments.length<=2||void 0===arguments[2]?[]:arguments[2],n=arguments.length<=3||void 0===arguments[3]?[]:arguments[3];if(e===t)return!0;if("function"==typeof e||"function"==typeof t)return!0;if("object"!==("undefined"==typeof e?"undefined":k(e))||"object"!==("undefined"==typeof t?"undefined":k(t)))return!1;if(null===e||null===t)return!1;if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(e instanceof RegExp&&t instanceof RegExp)return e.source===t.source&&e.global===t.global&&e.multiline===t.multiline&&e.lastIndex===t.lastIndex&&e.ignoreCase===t.ignoreCase;if(O(e)||O(t)){if(!O(e)||!O(t))return!1;var a=Array.prototype.slice;return w(a.call(e),a.call(t),r,n)}if(e.constructor!==t.constructor)return!1;var i=Object.keys(e),o=Object.keys(t);if(0===i.length&&0===o.length)return!0;if(i.length!==o.length)return!1;for(var u=r.length;u--;)if(r[u]===e)return n[u]===t;r.push(e),n.push(t),i.sort(),o.sort();for(var s=i.length-1;s>=0;s--)if(i[s]!==o[s])return!1;for(var l=void 0,c=i.length-1;c>=0;c--)if(l=i[c],!w(e[l],t[l],r,n))return!1;return r.pop(),n.pop(),!0}function E(e,t,r){var n=e.props,a=e.state;return!w(n,t)||!w(a,r)}function j(e,t,r){var n={$id:t||"root"};if("$ref"in e){var a=S(e,r);return j(a,t,r)}if("items"in e)return j(e.items,t,r);if("object"!==e.type)return n;for(var i in e.properties||{}){var o=e.properties[i],u=n.$id+"_"+i;n[i]=j(o,u,r)}return n}function _(e){var t=arguments.length<=1||void 0===arguments[1]?!0:arguments[1];if(!e)return{year:-1,month:-1,day:-1,hour:t?-1:0,minute:t?-1:0,second:t?-1:0};var r=new Date(e);if(Number.isNaN(r.getTime()))throw new Error("Unable to parse date "+e);return{year:r.getUTCFullYear(),month:r.getUTCMonth()+1,day:r.getUTCDate(),hour:t?r.getUTCHours():0,minute:t?r.getUTCMinutes():0,second:t?r.getUTCSeconds():0}}function x(e){var t=e.year,r=e.month,n=e.day,a=e.hour,i=void 0===a?0:a,o=e.minute,u=void 0===o?0:o,s=e.second,l=void 0===s?0:s,c=arguments.length<=1||void 0===arguments[1]?!0:arguments[1],f=Date.UTC(t,r-1,n,i,u,l),d=new Date(f).toJSON();return c?d:d.slice(0,10)}function P(e,t){for(var r=String(e);r.length=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},t.setImmediate="function"==typeof e?e:function(e){var r=l++,n=arguments.length<2?!1:u.call(arguments,1);return s[r]=!0,i(function(){s[r]&&(n?e.apply(null,n):e.call(null),t.clearImmediate(r))}),r},t.clearImmediate="function"==typeof n?n:function(e){delete s[e]}}).call(t,r(5).setImmediate,r(5).clearImmediate)},function(e,t){"use strict";function r(){l&&o&&(l=!1,o.length?s=o.concat(s):c=-1,s.length&&n())}function n(){if(!l){var e=setTimeout(r);l=!0;for(var t=s.length;t;){for(o=s,s=[];++c1)for(var r=1;r1)for(var r=1;r=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}function i(e){var t=e.value,r=e.readonly,n=e.onChange,i=(e.options,e.schema,a(e,["value","readonly","onChange","options","schema"]));return s["default"].createElement("input",o({},i,{className:"form-control",readOnly:r,value:"undefined"==typeof t?"":t,onChange:function(e){return n(e.target.value)}}))}Object.defineProperty(t,"__esModule",{value:!0});var o=Object.assign||function(e){for(var t=1;t=a;a++)n.push({value:a,label:(0,p.pad)(a,2)});return n}function l(e){return Object.keys(e).every(function(t){return-1!==e[t]})}function c(e){var t=e.type,r=e.range,n=e.value,a=e.select,i=e.rootId,o=e.disabled,u=e.readonly,l=i+"_"+t;return h["default"].createElement(v["default"],{schema:{type:"integer"},id:l,className:"form-control",options:{enumOptions:s(t,r[0],r[1])},value:n,disabled:o,readonly:u,onChange:function(e){return a(t,e)}})}Object.defineProperty(t,"__esModule",{value:!0});var f=Object.assign||function(e){for(var t=1;tr.indexOf(t)})}function i(e,t){return t.filter(function(t){return t!==e})}function o(e){var t=e.id,r=e.disabled,n=e.options,o=e.value,u=e.onChange,l=n.enumOptions;return s["default"].createElement("div",{className:"checkboxes",id:t},l.map(function(e,n){var c=-1!==o.indexOf(e.value);return s["default"].createElement("div",{key:n,className:"checkbox"},s["default"].createElement("label",null,s["default"].createElement("input",{type:"checkbox",id:t+"_"+n,checked:c,disabled:r,onChange:function(t){var r=l.map(function(e){var t=e.value;return t});u(t.target.checked?a(e.value,o,r):i(e.value,o))}}),s["default"].createElement("strong",null,e.label)))}))}Object.defineProperty(t,"__esModule",{value:!0});var u=r(2),s=n(u);t["default"]=o},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function u(e){var t=e.TitleField,r=e.idSchema,n=e.title,a=e.required;if(!n)return null;var i=r.$id+"__title";return d["default"].createElement(t,{id:i,title:n,required:a})}function s(e){var t=e.DescriptionField,r=e.idSchema,n=e.description;if(!n)return null;var a=r.$id+"__description";return d["default"].createElement(t,{id:a,description:n})}function l(e){var t=e.onClick,r=e.disabled;return d["default"].createElement("div",{className:"row"},d["default"].createElement("p",{className:"col-xs-2 col-xs-offset-10 array-item-add text-right"},d["default"].createElement("button",{type:"button",className:"btn btn-info col-xs-12",tabIndex:"-1",onClick:t,disabled:r,style:{fontWeight:"bold"}},"âž•")))}Object.defineProperty(t,"__esModule",{value:!0});var c=function(){function e(e,t){for(var r=0;r0}},{key:"asyncSetState",value:function(e){var t=this,r=arguments.length<=1||void 0===arguments[1]?{validate:!1}:arguments[1];(0,m.setState)(this,e,function(){t.props.onChange(t.state.items,r)})}},{key:"render",value:function(){var e=this.props,t=e.schema,r=e.uiSchema;return(0,m.isFilesArray)(t,r)?this.renderFiles():(0,m.isFixedItems)(t)?this.renderFixedArray():(0,m.isMultiSelect)(t)?this.renderMultiSelect():this.renderNormalArray()}},{key:"renderNormalArray",value:function(){var e=this,t=this.props,r=t.schema,n=t.uiSchema,a=t.errorSchema,i=t.idSchema,o=t.name,c=t.required,f=t.disabled,h=t.readonly,p=r.title||o,y=this.state.items,v=this.props.registry,b=v.definitions,g=v.fields,S=g.TitleField,O=g.DescriptionField,w=(0,m.retrieveSchema)(r.items,b);return d["default"].createElement("fieldset",{className:"field field-array field-array-of-"+w.type},d["default"].createElement(u,{TitleField:S,idSchema:i,title:p,required:c}),r.description?d["default"].createElement(s,{DescriptionField:O,idSchema:i,description:r.description}):null,d["default"].createElement("div",{className:"row array-item-list"},y.map(function(t,r){var o=a?a[r]:void 0,u=i.$id+"_"+r,s=(0,m.toIdSchema)(w,u,b);return e.renderArrayFieldItem({index:r,canMoveUp:r>0,canMoveDown:r=S.length,u=o?O:S[r],s=i.$id+"_"+r,l=(0,m.toIdSchema)(u,s,v),c=o?n.additionalItems||{}:Array.isArray(n.items)?n.items[r]:n.items||{},f=a?a[r]:void 0;return e.renderArrayFieldItem({index:r,removable:o,canMoveUp:r>=S.length+1,canMoveDown:o&&r already exists with different definition");return this.schemas[i]}this.schemas[i]=t;var s=i.replace(/^([^#]*)#$/,"$1");this.schemas[s]=t}return this.addSubSchemaArray(u,t.items instanceof Array?t.items:[t.items]),this.addSubSchemaArray(u,t["extends"]instanceof Array?t["extends"]:[t["extends"]]),this.addSubSchema(u,t.additionalItems),this.addSubSchemaObject(u,t.properties),this.addSubSchema(u,t.additionalProperties),this.addSubSchemaObject(u,t.definitions),this.addSubSchemaObject(u,t.patternProperties),this.addSubSchemaObject(u,t.dependencies),this.addSubSchemaArray(u,t.disallow),this.addSubSchemaArray(u,t.allOf),this.addSubSchemaArray(u,t.anyOf),this.addSubSchemaArray(u,t.oneOf),this.addSubSchema(u,t.not),this.schemas[i]}},c.prototype.addSubSchemaArray=function(e,t){if(t instanceof Array)for(var r=0;r",e);var l=o.objectGetPath(r.schemas[u],i.substr(1));if(void 0===l)throw new s("no such schema "+i+" located in <"+u+">",e);return{subschema:l,switchSchema:t}},c.prototype.testType=function(e,t,r,a,i){if("function"==typeof this.types[i])return this.types[i].call(this,e);if(i&&"object"==("undefined"==typeof i?"undefined":n(i))){var o=this.validateSchema(e,i,r,a);return void 0===o||!(o&&o.errors.length)}return!0};var f=c.prototype.types={};f.string=function(e){return"string"==typeof e},f.number=function(e){return!isNaN(e)&&isFinite(e)},f.integer=function(e){return!isNaN(e)&&e%1===0},f["boolean"]=function(e){return"boolean"==typeof e},f.array=function(e){return e instanceof Array},f["null"]=function(e){return null===e},f.date=function(e){return e instanceof Date},f.any=function(e){return!0},f.object=function(e){return e&&"object"===("undefined"==typeof e?"undefined":n(e))&&!(e instanceof Array)&&!(e instanceof Date)},e.exports=c},function(e,t,r){"use strict";function n(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function a(e,t,r){if(e&&l(e)&&e instanceof n)return e;var a=new n;return a.parse(e,t,r),a}function i(e){return s(e)&&(e=a(e)),e instanceof n?e.format():n.prototype.format.call(e)}function o(e,t){return a(e,!1,!0).resolve(t)}function u(e,t){return e?a(e,!1,!0).resolveObject(t):t}function s(e){return"string"==typeof e}function l(e){return"object"===("undefined"==typeof e?"undefined":d(e))&&null!==e}function c(e){return null===e}function f(e){return null==e}var d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},m=r(41);t.parse=a,t.resolve=o,t.resolveObject=u,t.format=i,t.Url=n;var h=/^([a-z0-9.+-]+:)/i,p=/:[0-9]*$/,y=["<",">",'"',"`"," ","\r","\n"," "],v=["{","}","|","\\","^","`"].concat(y),b=["'"].concat(v),g=["%","/","?",";","#"].concat(b),S=["/","?","#"],O=255,w=/^[a-z0-9A-Z_-]{0,63}$/,E=/^([a-z0-9A-Z_-]{0,63})(.*)$/,j={javascript:!0,"javascript:":!0},_={javascript:!0,"javascript:":!0},x={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},P=r(44);n.prototype.parse=function(e,t,r){if(!s(e))throw new TypeError("Parameter 'url' must be a string, not "+("undefined"==typeof e?"undefined":d(e)));var n=e;n=n.trim();var a=h.exec(n);if(a){a=a[0];var i=a.toLowerCase();this.protocol=i,n=n.substr(a.length)}if(r||a||n.match(/^\/\/[^@\/]+@[^@\/]+/)){var o="//"===n.substr(0,2);!o||a&&_[a]||(n=n.substr(2),this.slashes=!0)}if(!_[a]&&(o||a&&!x[a])){for(var u=-1,l=0;lc)&&(u=c)}var f,p;p=-1===u?n.lastIndexOf("@"):n.lastIndexOf("@",u),-1!==p&&(f=n.slice(0,p),n=n.slice(p+1),this.auth=decodeURIComponent(f)),u=-1;for(var l=0;lc)&&(u=c)}-1===u&&(u=n.length),this.host=n.slice(0,u),n=n.slice(u),this.parseHost(),this.hostname=this.hostname||"";var y="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!y)for(var v=this.hostname.split(/\./),l=0,A=v.length;A>l;l++){var C=v[l];if(C&&!C.match(w)){for(var k="",I=0,F=C.length;F>I;I++)k+=C.charCodeAt(I)>127?"x":C[I];if(!k.match(w)){var M=v.slice(0,l),N=v.slice(l+1),q=C.match(E);q&&(M.push(q[1]),N.unshift(q[2])),N.length&&(n="/"+N.join(".")+n),this.hostname=M.join(".");break}}}if(this.hostname.length>O?this.hostname="":this.hostname=this.hostname.toLowerCase(),!y){for(var R=this.hostname.split("."),T=[],l=0;ll;l++){var L=b[l],z=encodeURIComponent(L);z===L&&(z=escape(L)),n=n.split(L).join(z)}var Z=n.indexOf("#");-1!==Z&&(this.hash=n.substr(Z),n=n.slice(0,Z));var V=n.indexOf("?");if(-1!==V?(this.search=n.substr(V),this.query=n.substr(V+1),t&&(this.query=P.parse(this.query)),n=n.slice(0,V)):t&&(this.search="",this.query={}),n&&(this.pathname=n),x[i]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var $=this.pathname||"",D=this.search||"";this.path=$+D}return this.href=this.format(),this},n.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",r=this.pathname||"",n=this.hash||"",a=!1,i="";this.host?a=e+this.host:this.hostname&&(a=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(a+=":"+this.port)),this.query&&l(this.query)&&Object.keys(this.query).length&&(i=P.stringify(this.query));var o=this.search||i&&"?"+i||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||x[t])&&a!==!1?(a="//"+(a||""),r&&"/"!==r.charAt(0)&&(r="/"+r)):a||(a=""),n&&"#"!==n.charAt(0)&&(n="#"+n),o&&"?"!==o.charAt(0)&&(o="?"+o),r=r.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),o=o.replace("#","%23"),t+a+r+o+n},n.prototype.resolve=function(e){return this.resolveObject(a(e,!1,!0)).format()},n.prototype.resolveObject=function(e){ -if(s(e)){var t=new n;t.parse(e,!1,!0),e=t}var r=new n;if(Object.keys(this).forEach(function(e){r[e]=this[e]},this),r.hash=e.hash,""===e.href)return r.href=r.format(),r;if(e.slashes&&!e.protocol)return Object.keys(e).forEach(function(t){"protocol"!==t&&(r[t]=e[t])}),x[r.protocol]&&r.hostname&&!r.pathname&&(r.path=r.pathname="/"),r.href=r.format(),r;if(e.protocol&&e.protocol!==r.protocol){if(!x[e.protocol])return Object.keys(e).forEach(function(t){r[t]=e[t]}),r.href=r.format(),r;if(r.protocol=e.protocol,e.host||_[e.protocol])r.pathname=e.pathname;else{for(var a=(e.pathname||"").split("/");a.length&&!(e.host=a.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==a[0]&&a.unshift(""),a.length<2&&a.unshift(""),r.pathname=a.join("/")}if(r.search=e.search,r.query=e.query,r.host=e.host||"",r.auth=e.auth,r.hostname=e.hostname||e.host,r.port=e.port,r.pathname||r.search){var i=r.pathname||"",o=r.search||"";r.path=i+o}return r.slashes=r.slashes||e.slashes,r.href=r.format(),r}var u=r.pathname&&"/"===r.pathname.charAt(0),l=e.host||e.pathname&&"/"===e.pathname.charAt(0),d=l||u||r.host&&e.pathname,m=d,h=r.pathname&&r.pathname.split("/")||[],a=e.pathname&&e.pathname.split("/")||[],p=r.protocol&&!x[r.protocol];if(p&&(r.hostname="",r.port=null,r.host&&(""===h[0]?h[0]=r.host:h.unshift(r.host)),r.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===a[0]?a[0]=e.host:a.unshift(e.host)),e.host=null),d=d&&(""===a[0]||""===h[0])),l)r.host=e.host||""===e.host?e.host:r.host,r.hostname=e.hostname||""===e.hostname?e.hostname:r.hostname,r.search=e.search,r.query=e.query,h=a;else if(a.length)h||(h=[]),h.pop(),h=h.concat(a),r.search=e.search,r.query=e.query;else if(!f(e.search)){if(p){r.hostname=r.host=h.shift();var y=r.host&&r.host.indexOf("@")>0?r.host.split("@"):!1;y&&(r.auth=y.shift(),r.host=r.hostname=y.shift())}return r.search=e.search,r.query=e.query,c(r.pathname)&&c(r.search)||(r.path=(r.pathname?r.pathname:"")+(r.search?r.search:"")),r.href=r.format(),r}if(!h.length)return r.pathname=null,r.search?r.path="/"+r.search:r.path=null,r.href=r.format(),r;for(var v=h.slice(-1)[0],b=(r.host||e.host)&&("."===v||".."===v)||""===v,g=0,S=h.length;S>=0;S--)v=h[S],"."==v?h.splice(S,1):".."===v?(h.splice(S,1),g++):g&&(h.splice(S,1),g--);if(!d&&!m)for(;g--;g)h.unshift("..");!d||""===h[0]||h[0]&&"/"===h[0].charAt(0)||h.unshift(""),b&&"/"!==h.join("/").substr(-1)&&h.push("");var O=""===h[0]||h[0]&&"/"===h[0].charAt(0);if(p){r.hostname=r.host=O?"":h.length?h.shift():"";var y=r.host&&r.host.indexOf("@")>0?r.host.split("@"):!1;y&&(r.auth=y.shift(),r.host=r.hostname=y.shift())}return d=d||r.host&&h.length,d&&!O&&h.unshift(""),h.length?r.pathname=h.join("/"):(r.pathname=null,r.path=null),c(r.pathname)&&c(r.search)||(r.path=(r.pathname?r.pathname:"")+(r.search?r.search:"")),r.auth=e.auth||r.auth,r.slashes=r.slashes||e.slashes,r.href=r.format(),r},n.prototype.parseHost=function(){var e=this.host,t=p.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},function(e,t,r){var n;(function(e,a){"use strict";var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};!function(o){function u(e){throw RangeError(R[e])}function s(e,t){for(var r=e.length,n=[];r--;)n[r]=t(e[r]);return n}function l(e,t){var r=e.split("@"),n="";r.length>1&&(n=r[0]+"@",e=r[1]),e=e.replace(q,".");var a=e.split("."),i=s(a,t).join(".");return n+i}function c(e){for(var t,r,n=[],a=0,i=e.length;i>a;)t=e.charCodeAt(a++),t>=55296&&56319>=t&&i>a?(r=e.charCodeAt(a++),56320==(64512&r)?n.push(((1023&t)<<10)+(1023&r)+65536):(n.push(t),a--)):n.push(t);return n}function f(e){return s(e,function(e){var t="";return e>65535&&(e-=65536,t+=$(e>>>10&1023|55296),e=56320|1023&e),t+=$(e)}).join("")}function d(e){return 10>e-48?e-22:26>e-65?e-65:26>e-97?e-97:_}function m(e,t){return e+22+75*(26>e)-((0!=t)<<5)}function h(e,t,r){var n=0;for(e=r?D(e/C):e>>1,e+=D(e/t);e>T*P>>1;n+=_)e=D(e/T);return D(n+(T+1)*e/(e+A))}function p(e){var t,r,n,a,i,o,s,l,c,m,p=[],y=e.length,v=0,b=I,g=k;for(r=e.lastIndexOf(F),0>r&&(r=0),n=0;r>n;++n)e.charCodeAt(n)>=128&&u("not-basic"),p.push(e.charCodeAt(n));for(a=r>0?r+1:0;y>a;){for(i=v,o=1,s=_;a>=y&&u("invalid-input"),l=d(e.charCodeAt(a++)),(l>=_||l>D((j-v)/o))&&u("overflow"),v+=l*o,c=g>=s?x:s>=g+P?P:s-g,!(c>l);s+=_)m=_-c,o>D(j/m)&&u("overflow"),o*=m;t=p.length+1,g=h(v-i,t,0==i),D(v/t)>j-b&&u("overflow"),b+=D(v/t),v%=t,p.splice(v++,0,b)}return f(p)}function y(e){var t,r,n,a,i,o,s,l,f,d,p,y,v,b,g,S=[];for(e=c(e),y=e.length,t=I,r=0,i=k,o=0;y>o;++o)p=e[o],128>p&&S.push($(p));for(n=a=S.length,a&&S.push(F);y>n;){for(s=j,o=0;y>o;++o)p=e[o],p>=t&&s>p&&(s=p);for(v=n+1,s-t>D((j-r)/v)&&u("overflow"),r+=(s-t)*v,t=s,o=0;y>o;++o)if(p=e[o],t>p&&++r>j&&u("overflow"),p==t){for(l=r,f=_;d=i>=f?x:f>=i+P?P:f-i,!(d>l);f+=_)g=l-d,b=_-d,S.push($(m(d+g%b,0))),l=D(g/b);S.push($(m(l,0))),i=h(r,v,n==a),r=0,++n}++r,++t}return S.join("")}function v(e){return l(e,function(e){return M.test(e)?p(e.slice(4).toLowerCase()):e})}function b(e){return l(e,function(e){return N.test(e)?"xn--"+y(e):e})}var g="object"==i(t)&&t&&!t.nodeType&&t,S="object"==i(e)&&e&&!e.nodeType&&e,O="object"==("undefined"==typeof a?"undefined":i(a))&&a;O.global!==O&&O.window!==O&&O.self!==O||(o=O);var w,E,j=2147483647,_=36,x=1,P=26,A=38,C=700,k=72,I=128,F="-",M=/^xn--/,N=/[^\x20-\x7E]/,q=/[\x2E\u3002\uFF0E\uFF61]/g,R={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},T=_-x,D=Math.floor,$=String.fromCharCode;if(w={version:"1.3.2",ucs2:{decode:c,encode:f},decode:p,encode:y,toASCII:b,toUnicode:v},"object"==i(r(43))&&r(43))n=function(){return w}.call(t,r,t,e),!(void 0!==n&&(e.exports=n));else if(g&&S)if(e.exports==g)S.exports=w;else for(E in w)w.hasOwnProperty(E)&&(g[E]=w[E]);else o.punycode=w}(void 0)}).call(t,r(42)(e),function(){return this}())},function(e,t){"use strict";e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children=[],e.webpackPolyfill=1),e}},function(e,t){(function(t){e.exports=t}).call(t,{})},function(e,t,r){"use strict";t.decode=t.parse=r(45),t.encode=t.stringify=r(46)},function(e,t){"use strict";function r(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,a){t=t||"&",n=n||"=";var i={};if("string"!=typeof e||0===e.length)return i;var o=/\+/g;e=e.split(t);var u=1e3;a&&"number"==typeof a.maxKeys&&(u=a.maxKeys);var s=e.length;u>0&&s>u&&(s=u);for(var l=0;s>l;++l){var c,f,d,m,h=e[l].replace(o,"%20"),p=h.indexOf(n);p>=0?(c=h.substr(0,p),f=h.substr(p+1)):(c=h,f=""),d=decodeURIComponent(c),m=decodeURIComponent(f),r(i,d)?Array.isArray(i[d])?i[d].push(m):i[d]=[i[d],m]:i[d]=m}return i}},function(e,t){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},n=function(e){switch("undefined"==typeof e?"undefined":r(e)){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,a,i){return t=t||"&",a=a||"=",null===e&&(e=void 0),"object"===("undefined"==typeof e?"undefined":r(e))?Object.keys(e).map(function(r){var i=encodeURIComponent(n(r))+a;return Array.isArray(e[r])?e[r].map(function(e){return i+encodeURIComponent(n(e))}).join(t):i+encodeURIComponent(n(e[r]))}).join(t):i?encodeURIComponent(n(i))+a+encodeURIComponent(n(e)):""}},function(e,t,r){"use strict";function n(e,t,r,n){return this.validateSchema(e,n,t,r).valid}function a(e,t,r,n,a,i){if(!t.properties||void 0===t.properties[a])if(t.additionalProperties===!1)i.addError({name:"additionalProperties",argument:a,message:"additionalProperty "+JSON.stringify(a)+" exists in instance when not allowed"});else{var o=t.additionalProperties||{},u=this.validateSchema(e[a],o,r,n.makeChild(o,a));u.instance!==i.instance[a]&&(i.instance[a]=u.instance),i.importErrors(u)}}function i(e,t,r){var n,a=r.length;for(n=t+1,a;a>n;n++)if(u.deepCompareStrict(e,r[n]))return!1;return!0}var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},u=r(48),s=u.ValidatorResult,l=u.SchemaError,c={};c.ignoreProperties={id:!0,"default":!0,description:!0,title:!0,exclusiveMinimum:!0,exclusiveMaximum:!0,additionalItems:!0,$schema:!0,$ref:!0,"extends":!0};var f=c.validators={};f.type=function(e,t,r,n){if(void 0===e)return null;var a=new s(e,t,r,n),i=t.type instanceof Array?t.type:[t.type];if(!i.some(this.testType.bind(this,e,t,r,n))){var o=i.map(function(e){return e.id&&"<"+e.id+">"||e+""});a.addError({name:"type",argument:o,message:"is not of a type(s) "+o})}return a},f.anyOf=function(e,t,r,a){if(void 0===e)return null;var i=new s(e,t,r,a);if(!(t.anyOf instanceof Array))throw new l("anyOf must be an array");if(!t.anyOf.some(n.bind(this,e,r,a))){var o=t.anyOf.map(function(e,t){return e.id&&"<"+e.id+">"||e.title&&JSON.stringify(e.title)||e.$ref&&"<"+e.$ref+">"||"[subschema "+t+"]"});i.addError({name:"anyOf",argument:o,message:"is not any of "+o.join(",")})}return i},f.allOf=function(e,t,r,n){if(void 0===e)return null;if(!(t.allOf instanceof Array))throw new l("allOf must be an array");var a=new s(e,t,r,n),i=this;return t.allOf.forEach(function(t,o){var u=i.validateSchema(e,t,r,n);if(!u.valid){var s=t.id&&"<"+t.id+">"||t.title&&JSON.stringify(t.title)||t.$ref&&"<"+t.$ref+">"||"[subschema "+o+"]";a.addError({name:"allOf",argument:{id:s,length:u.errors.length,valid:u},message:"does not match allOf schema "+s+" with "+u.errors.length+" error[s]:"}),a.importErrors(u)}}),a},f.oneOf=function(e,t,r,a){if(void 0===e)return null;if(!(t.oneOf instanceof Array))throw new l("oneOf must be an array");var i=new s(e,t,r,a),o=t.oneOf.filter(n.bind(this,e,r,a)).length,u=t.oneOf.map(function(e,t){return e.id&&"<"+e.id+">"||e.title&&JSON.stringify(e.title)||e.$ref&&"<"+e.$ref+">"||"[subschema "+t+"]"});return 1!==o&&i.addError({name:"oneOf",argument:u,message:"is not exactly one from "+u.join(",")}),i},f.properties=function(e,t,r,n){if(void 0!==e&&e instanceof Object){var a=new s(e,t,r,n),i=t.properties||{};for(var o in i){var u=(e||void 0)&&e[o],l=this.validateSchema(u,i[o],r,n.makeChild(i[o],o));l.instance!==a.instance[o]&&(a.instance[o]=l.instance),a.importErrors(l)}return a}},f.patternProperties=function(e,t,r,n){if(void 0!==e&&this.types.object(e)){var i=new s(e,t,r,n),o=t.patternProperties||{};for(var u in e){var l=!0;for(var c in o){var f=new RegExp(c);if(f.test(u)){l=!1;var d=this.validateSchema(e[u],o[c],r,n.makeChild(o[c],u));d.instance!==i.instance[u]&&(i.instance[u]=d.instance),i.importErrors(d)}}l&&a.call(this,e,t,r,n,u,i)}return i}},f.additionalProperties=function(e,t,r,n){if(void 0!==e&&this.types.object(e)){if(t.patternProperties)return null;var i=new s(e,t,r,n);for(var o in e)a.call(this,e,t,r,n,o,i);return i}},f.minProperties=function(e,t,r,n){if(!e||"object"!==("undefined"==typeof e?"undefined":o(e)))return null;var a=new s(e,t,r,n),i=Object.keys(e);return i.length>=t.minProperties||a.addError({name:"minProperties",argument:t.minProperties,message:"does not meet minimum property length of "+t.minProperties}),a},f.maxProperties=function(e,t,r,n){if(!e||"object"!==("undefined"==typeof e?"undefined":o(e)))return null;var a=new s(e,t,r,n),i=Object.keys(e);return i.length<=t.maxProperties||a.addError({name:"maxProperties",argument:t.maxProperties,message:"does not meet maximum property length of "+t.maxProperties}),a},f.items=function(e,t,r,n){if(!(e instanceof Array))return null;var a=this,i=new s(e,t,r,n);return void 0!==e&&t.items?(e.every(function(e,o){var u=t.items instanceof Array?t.items[o]||t.additionalItems:t.items;if(void 0===u)return!0;if(u===!1)return i.addError({name:"items",message:"additionalItems not permitted"}),!1;var s=a.validateSchema(e,u,r,n.makeChild(u,o));return s.instance!==i.instance[o]&&(i.instance[o]=s.instance),i.importErrors(s),!0}),i):i},f.minimum=function(e,t,r,n){if("number"!=typeof e)return null;var a=new s(e,t,r,n),i=!0;return i=t.exclusiveMinimum&&t.exclusiveMinimum===!0?e>t.minimum:e>=t.minimum,i||a.addError({name:"minimum",argument:t.minimum,message:"must have a minimum value of "+t.minimum}),a},f.maximum=function(e,t,r,n){if("number"!=typeof e)return null;var a,i=new s(e,t,r,n);return a=t.exclusiveMaximum&&t.exclusiveMaximum===!0?e=t.minLength||a.addError({name:"minLength",argument:t.minLength,message:"does not meet minimum length of "+t.minLength}),a},f.maxLength=function(e,t,r,n){if("string"!=typeof e)return null;var a=new s(e,t,r,n);return e.length<=t.maxLength||a.addError({name:"maxLength",argument:t.maxLength,message:"does not meet maximum length of "+t.maxLength}),a},f.minItems=function(e,t,r,n){if(!(e instanceof Array))return null;var a=new s(e,t,r,n);return e.length>=t.minItems||a.addError({name:"minItems",argument:t.minItems,message:"does not meet minimum length of "+t.minItems}),a},f.maxItems=function(e,t,r,n){if(!(e instanceof Array))return null;var a=new s(e,t,r,n);return e.length<=t.maxItems||a.addError({name:"maxItems",argument:t.maxItems,message:"does not meet maximum length of "+t.maxItems}),a},f.uniqueItems=function(e,t,r,n){function a(e,t,r){for(var n=t+1;n"||o;i.addError({name:"not",argument:u,message:"is of prohibited type "+u})}}),i):null},e.exports=c},function(e,t,r){"use strict";var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},a=r(40),i=t.ValidationError=function(e,t,r,n,a,i){n&&(this.property=n),e&&(this.message=e),r&&(r.id?this.schema=r.id:this.schema=r),t&&(this.instance=t),this.name=a,this.argument=i,this.stack=this.toString()};i.prototype.toString=function(){return this.property+" "+this.message};var o=t.ValidatorResult=function(e,t,r,n){this.instance=e,this.schema=t,this.propertyPath=n.propertyPath,this.errors=[],this.throwError=r&&r.throwError,this.disableFormat=r&&r.disableFormat===!0};o.prototype.addError=function(e){var t;if("string"==typeof e)t=new i(e,this.instance,this.schema,this.propertyPath);else{if(!e)throw new Error("Missing error detail");if(!e.message)throw new Error("Missing error message");if(!e.name)throw new Error("Missing validator type");t=new i(e.message,this.instance,this.schema,this.propertyPath,e.name,e.argument)}if(this.throwError)throw t;return this.errors.push(t),t},o.prototype.importErrors=function(e){if("string"==typeof e||e&&e.validatorType)this.addError(e);else if(e&&e.errors){var t=this.errors;e.errors.forEach(function(e){t.push(e)})}},o.prototype.toString=function(e){return this.errors.map(function(e,t){return t+": "+e.toString()+"\n"}).join("")},Object.defineProperty(o.prototype,"valid",{get:function(){return!this.errors.length}});var u=t.SchemaError=function f(e,t){this.message=e,this.schema=t,Error.call(this,e),Error.captureStackTrace(this,f)};u.prototype=Object.create(Error.prototype,{constructor:{value:u,enumerable:!1},name:{value:"SchemaError",enumerable:!1}});var s=t.SchemaContext=function(e,t,r,n,a){this.schema=e,this.options=t,this.propertyPath=r,this.base=n,this.schemas=a};s.prototype.resolve=function(e){return a.resolve(this.base,e)},s.prototype.makeChild=function(e,t){var r=void 0===t?this.propertyPath:this.propertyPath+c(t),n=a.resolve(this.base,e.id||""),i=new s(e,this.options,r,n,Object.create(this.schemas));return e.id&&!i.schemas[n]&&(i.schemas[n]=e),i};var l=t.FORMAT_REGEXPS={"date-time":/^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-(3[01]|0[1-9]|[12][0-9])[tT ](2[0-4]|[01][0-9]):([0-5][0-9]):(60|[0-5][0-9])(\.\d+)?([zZ]|[+-]([0-5][0-9]):(60|[0-5][0-9]))$/,date:/^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-(3[01]|0[1-9]|[12][0-9])$/,time:/^(2[0-4]|[01][0-9]):([0-5][0-9]):(60|[0-5][0-9])$/,email:/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/,"ip-address":/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,ipv6:/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,uri:/^[a-zA-Z][a-zA-Z0-9+-.]*:[^\s]*$/,color:/^(#?([0-9A-Fa-f]{3}){1,2}\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\(\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/,hostname:/^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/,"host-name":/^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/,alpha:/^[a-zA-Z]+$/,alphanumeric:/^[a-zA-Z0-9]+$/,"utc-millisec":function(e){return"string"==typeof e&&parseFloat(e)===parseInt(e,10)&&!isNaN(e)},regex:function(e){var t=!0;try{new RegExp(e)}catch(r){t=!1}return t},style:/\s*(.+?):\s*([^;]+);?/g,phone:/^\+(?:[0-9] ?){6,14}[0-9]$/};l.regexp=l.regex,l.pattern=l.regex,l.ipv4=l["ip-address"],t.isFormat=function(e,t,r){if("string"==typeof e&&void 0!==l[t]){if(l[t]instanceof RegExp)return l[t].test(e);if("function"==typeof l[t])return l[t](e)}else if(r&&r.customFormats&&"function"==typeof r.customFormats[t])return r.customFormats[t](e);return!0};var c=t.makeSuffix=function(e){return e=e.toString(),e.match(/[.\s\[\]]/)||e.match(/^[\d]/)?e.match(/^\d+$/)?"["+e+"]":"["+JSON.stringify(e)+"]":"."+e};t.deepCompareStrict=function d(e,t){if(("undefined"==typeof e?"undefined":n(e))!==("undefined"==typeof t?"undefined":n(t)))return!1;if(e instanceof Array)return t instanceof Array?e.length!==t.length?!1:e.every(function(r,n){return d(e[n],t[n])}):!1;if("object"===("undefined"==typeof e?"undefined":n(e))){if(!e||!t)return e===t;var r=Object.keys(e),a=Object.keys(t);return r.length!==a.length?!1:r.every(function(r){return d(e[r],t[r])})}return e===t},e.exports.deepMerge=function m(e,t){var r=Array.isArray(t),a=r&&[]||{};return r?(e=e||[],a=a.concat(e),t.forEach(function(t,r){"object"===("undefined"==typeof t?"undefined":n(t))?a[r]=m(e[r],t):-1===e.indexOf(t)&&a.push(t)})):(e&&"object"===("undefined"==typeof e?"undefined":n(e))&&Object.keys(e).forEach(function(t){a[t]=e[t]}),Object.keys(t).forEach(function(r){"object"===n(t[r])&&t[r]&&e[r]?a[r]=m(e[r],t[r]):a[r]=t[r]})),a},t.objectGetPath=function(e,t){for(var r,n=t.split("/").slice(1);"string"==typeof(r=n.shift());){var a=decodeURIComponent(r.replace(/~0/,"~").replace(/~1/g,"/"));if(!(a in e))return;e=e[a]}return e},t.encodePath=function(e){return e.map(function(e){return"/"+encodeURIComponent(e).replace(/~/g,"%7E")}).join("")}}])}); -//# sourceMappingURL=react-jsonschema-form.js.map \ No newline at end of file diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 7da69787c..a42290bc1 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -62,6 +62,7 @@ "raw-loader": "^0.5.1", "react": "15.4.1", "react-dom": "15.4.1", + "react-jsonschema-form": "^0.48.0", "react-simpletabs": "^0.7.0", "react-slick": "^0.14.8", "remarkable": "^1.7.1", @@ -77,10 +78,12 @@ "webpack": "1.13.2", "webpack-dev-server": "^1.16.2" }, - "buildOptions": { + "buildOptions": { "emitEntryPoint": true, "compile": { - "exclude": ["node_modules"] - } + "exclude": [ + "node_modules" + ] } + } } From ba1a8cc56ef1087a8c64e4b85c81f6598c7ce3cf Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sat, 6 May 2017 23:06:16 +0100 Subject: [PATCH 179/339] fix wrong path --- src/main/webapp/js/pages/admin/admin.ejs | 4 ++-- src/main/webapp/js/pages/tests/tests.ejs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/pages/admin/admin.ejs b/src/main/webapp/js/pages/admin/admin.ejs index 1339ede75..b63163562 100644 --- a/src/main/webapp/js/pages/admin/admin.ejs +++ b/src/main/webapp/js/pages/admin/admin.ejs @@ -9,8 +9,8 @@ - - + + - + From 8682ef052f78d28cce2950d16fa47424e940c178 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 7 May 2017 04:57:52 +0100 Subject: [PATCH 181/339] add route libs --- src/main/webapp/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index a42290bc1..0a2c5eec1 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -63,6 +63,8 @@ "react": "15.4.1", "react-dom": "15.4.1", "react-jsonschema-form": "^0.48.0", + "react-router": "^4.1.1", + "react-router-dom": "^4.1.1", "react-simpletabs": "^0.7.0", "react-slick": "^0.14.8", "remarkable": "^1.7.1", From 146ebf6fdca21b4378ab825149b817ef30365d61 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 7 May 2017 04:58:03 +0100 Subject: [PATCH 182/339] fix typo --- .../java/org/geppetto/frontend/controllers/Application.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/geppetto/frontend/controllers/Application.java b/src/main/java/org/geppetto/frontend/controllers/Application.java index 2169b00e8..2602dd7de 100644 --- a/src/main/java/org/geppetto/frontend/controllers/Application.java +++ b/src/main/java/org/geppetto/frontend/controllers/Application.java @@ -110,7 +110,7 @@ public String geppetto(HttpServletRequest req, Model model) } @RequestMapping(value = "/**", method = RequestMethod.GET) - public String geppettoExtensiont(HttpServletRequest req, Model model) + public String geppettoExtension(HttpServletRequest req, Model model) { String path = "/build/static" + req.getServletPath() + ".html"; if (Application.class.getResource(path) != null){ From e9476c6608ef376c63cecbbf0ab5ceb81ef52cd8 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 7 May 2017 04:58:20 +0100 Subject: [PATCH 183/339] comment out component initilziation per page --- src/main/webapp/extensions/extensions.js | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/webapp/extensions/extensions.js b/src/main/webapp/extensions/extensions.js index c4328e23e..d26caf7dd 100644 --- a/src/main/webapp/extensions/extensions.js +++ b/src/main/webapp/extensions/extensions.js @@ -16,18 +16,18 @@ define(['./extensionsConfiguration.json', 'geppetto','../js/components/Component } } - var paths = GEPPETTO.Utility.getPathStringParameters(); - for (var pathIndex in paths){ - for (var availableExtensionIndex in availableExtensions){ - try { - require(['../extensions/' + availableExtensions[availableExtensionIndex] + "/" + paths[pathIndex] + "/" + paths[pathIndex]], function(componentsInitialization){ - componentsInitialization(GEPPETTO); - }); - } - catch( e ) { - console.log('Components Initialization ' + paths[pathIndex] + ' can not be found in extension ' + availableExtensions[availableExtensionIndex]); - } - } - } + // var paths = GEPPETTO.Utility.getPathStringParameters(); + // for (var pathIndex in paths){ + // for (var availableExtensionIndex in availableExtensions){ + // try { + // require(['../extensions/' + availableExtensions[availableExtensionIndex] + "/" + paths[pathIndex] + "/" + paths[pathIndex]], function(componentsInitialization){ + // componentsInitialization(GEPPETTO); + // }); + // } + // catch( e ) { + // console.log('Components Initialization ' + paths[pathIndex] + ' can not be found in extension ' + availableExtensions[availableExtensionIndex]); + // } + // } + // } }); \ No newline at end of file From 42b1cdbdf47486a2a8143ae10c2a38859ba80449 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Sun, 7 May 2017 17:34:37 -0700 Subject: [PATCH 184/339] bug fixing and more casper testing --- .../webapp/js/communication/MessageHandler.js | 12 +- .../interface/controlPanel/controlpanel.js | 23 ++ .../interface/spotlight/spotlight.js | 5 +- .../js/pages/geppetto/GEPPETTO.Events.js | 3 + .../js/pages/tests/casperjs/CoreTests.js | 270 +++++++++--------- .../js/pages/tests/qunit/PersistenceTests.js | 9 +- 6 files changed, 164 insertions(+), 158 deletions(-) diff --git a/src/main/webapp/js/communication/MessageHandler.js b/src/main/webapp/js/communication/MessageHandler.js index 52004109c..fad805a79 100644 --- a/src/main/webapp/js/communication/MessageHandler.js +++ b/src/main/webapp/js/communication/MessageHandler.js @@ -210,16 +210,12 @@ define(function(require) { messageHandler[messageTypes.PROJECT_PROPS_SAVED] = function(payload) { GEPPETTO.Console.log("Project saved succesfully"); - if(GEPPETTO.ControlPanel.isOpen()){ - GEPPETTO.ControlPanel.refresh(); - } + GEPPETTO.trigger(GEPPETTO.Events.Project_properties_saved); }; messageHandler[messageTypes.SET_PARAMETERS] = function(payload) { GEPPETTO.Console.log("Set parameters succesfully"); - if(GEPPETTO.ControlPanel.isOpen()){ - GEPPETTO.ControlPanel.refresh(); - } + GEPPETTO.trigger(GEPPETTO.Events.Parameters_set); }; messageHandler[messageTypes.EXPERIMENT_PROPS_SAVED] = function(payload) { @@ -233,9 +229,7 @@ define(function(require) { experiment.setStatus(data.status); } - if(GEPPETTO.ControlPanel.isOpen()){ - GEPPETTO.ControlPanel.refresh(); - } + GEPPETTO.trigger(GEPPETTO.Events.Experiment_properties_saved); }; messageHandler[messageTypes.DROPBOX_LINKED] = function(payload) { diff --git a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js index 155b93c2d..848570745 100644 --- a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js +++ b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js @@ -1451,6 +1451,11 @@ define(function (require) { displayName: 'ControlPanel', refresh: function () { + var self = this; + self.forceUpdate(); + }, + + refreshData: function () { var self = this; var callback = function () { self.forceUpdate(); @@ -1914,6 +1919,24 @@ define(function (require) { GEPPETTO.on(GEPPETTO.Events.Project_loaded, function () { that.clearData(); }); + + GEPPETTO.on(GEPPETTO.Events.Experiment_properties_saved, function () { + if(that.isOpen()){ + that.refreshData(); + } + }); + + GEPPETTO.on(GEPPETTO.Events.Project_properties_saved, function () { + if(that.isOpen()){ + that.refreshData(); + } + }); + + GEPPETTO.on(GEPPETTO.Events.Parameters_set, function () { + if(that.isOpen()){ + that.refreshData(); + } + }); if (this.props.listenToInstanceCreationEvents) { GEPPETTO.on(GEPPETTO.Events.Instance_deleted, function (parameters) { diff --git a/src/main/webapp/js/components/interface/spotlight/spotlight.js b/src/main/webapp/js/components/interface/spotlight/spotlight.js index 5af1fe255..f9a47a8fb 100644 --- a/src/main/webapp/js/components/interface/spotlight/spotlight.js +++ b/src/main/webapp/js/components/interface/spotlight/spotlight.js @@ -925,17 +925,16 @@ define(function (require) { $.each(buttonGroups, function (groupName, groupDef) { if ((instanceToCheck.getCapabilities().indexOf(groupName) != -1) || (instanceToCheck.getType().getMetaType() == groupName)) { + var copiedObject = jQuery.extend({}, groupDef); if(modifiable){ - var copiedObject = jQuery.extend({}, groupDef); delete copiedObject["plot"]; tbar.append(that.createButtonGroup(groupName, copiedObject, instance)); }else{ //don't load default toolbar for these two types if modifiable flag set to false, if(groupName!="StateVariableCapability" && groupName!="ParameterCapability"){ + var copiedObject = jQuery.extend({}, groupDef); if(modifiable){ - var copiedObject = jQuery.extend({}, groupDef); delete copiedObject["plot"]; - delete copiedObject["plot"]; } tbar.append(that.createButtonGroup(groupName, copiedObject, instance)); }else{ diff --git a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js index 3f84f7ec4..2c3ac0f02 100644 --- a/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js +++ b/src/main/webapp/js/pages/geppetto/GEPPETTO.Events.js @@ -54,6 +54,9 @@ define(function (require) { Control_panel_close: "control_panel:close", Lit_entities_changed: "lit_entities_changed", Component_destroyed: "component_destroyed", + Experiment_properties_saved : "experiment_properties_saved", + Project_properties_saved : "project_properties_saved", + Parameters_set : "parameters_set", listen: function () { GEPPETTO.on(this.Select, function () { diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index 4c50666fd..3086a5d77 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -48,31 +48,31 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { }, null, 3000); }); - /**Tests HHCELL project**/ - casper.thenOpen(urlBase+baseFollowUp+hhcellProject,function() { - this.waitWhileVisible('div[id="loading-spinner"]', function () { - this.echo("I've waited for hhcell project to load."); - test.assertTitle("geppetto", "geppetto title is ok"); - test.assertUrlMatch(/load_project_from_id=1/, "project load attempted"); - test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); - test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); - hhcellTest(test); - },null,30000); - }); - - /**Tests C302 project**/ - casper.thenOpen(urlBase+baseFollowUp+c302Project,function() { - this.waitWhileVisible('div[id="loading-spinner"]', function () { - this.echo("I've waited for c302 project to load."); - test.assertTitle("geppetto", "geppetto title is ok"); - test.assertUrlMatch(/load_project_from_id=6/, "project load attempted"); - test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); - test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); - c302Test(test); - },null,450000); - }); +// /**Tests HHCELL project**/ +// casper.thenOpen(urlBase+baseFollowUp+hhcellProject,function() { +// this.waitWhileVisible('div[id="loading-spinner"]', function () { +// this.echo("I've waited for hhcell project to load."); +// test.assertTitle("geppetto", "geppetto title is ok"); +// test.assertUrlMatch(/load_project_from_id=1/, "project load attempted"); +// test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); +// test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); +// test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); +// hhcellTest(test); +// },null,50000); +// }); +// +// /**Tests C302 project**/ +// casper.thenOpen(urlBase+baseFollowUp+c302Project,function() { +// this.waitWhileVisible('div[id="loading-spinner"]', function () { +// this.echo("I've waited for c302 project to load."); +// test.assertTitle("geppetto", "geppetto title is ok"); +// test.assertUrlMatch(/load_project_from_id=6/, "project load attempted"); +// test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); +// test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); +// test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); +// c302Test(test); +// },null,450000); +// }); /**Tests Acnet project**/ casper.thenOpen(urlBase+baseFollowUp+acnetProject,function() { @@ -172,6 +172,9 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { function hhcellTest(test,name){ casper.then(function () { + var defaultColor = [0.00392156862745098,0.6,0.9098039215686274]; + test3DMeshColor(test,defaultColor,"hhcell.hppop[0]"); + this.echo("Opening controls panel"); this.evaluate(function() { $("#controlPanelBtn").click(); @@ -196,15 +199,10 @@ function hhcellTest(test,name){ }); this.waitUntilVisible('div[id="Plot1"]', function () { - var plots = this.evaluate(function(){ - var plots = $("#Plot1").length; - - return plots; - }); this.echo("I've waited for Plot1 to come up"); - test.assertEquals(plots, 1,"Amount of plot widgets in existence passed"); this.evaluate(function(){ + $("#Plot1").remove(); $("#anyProjectFilterBtn").click(); }); @@ -218,34 +216,8 @@ function hhcellTest(test,name){ casper.evaluate(function() { $("#controlpanel").hide(); }); - - test.assertExists('i.fa-search', "Spotlight button exists") - this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); - - this.waitUntilVisible('div#spotlight', function () { - test.assertVisible('div#spotlight', "Spotlight opened"); - - //type in the spotlight - this.sendKeys('input#typeahead', "hhcell.hhpop[0].v", {keepFocus: true}); - //press enter - this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); - - casper.waitUntilVisible('div#spotlight', function () { - casper.then(function () { - this.echo("Waiting to see if the Plot variables button becomes visible"); - casper.waitUntilVisible('button#plot', function () { - test.assertVisible('button#plot', "Plot variables icon correctly visible"); - this.echo("Plot variables button became visible correctly"); - this.evaluate(function(){ - $("#plot").click(); - }); - this.waitUntilVisible('div[id="Plot2"]', function () { - this.echo("Plot 2 came up correctly"); - }); - }, null, 8000); - }); - }); - }); + + testSpotlight(test, "hhcell.hhpop[0].v",'div[id="Plot1"]',true,true,"hhcell"); }); }); }); @@ -253,6 +225,99 @@ function hhcellTest(test,name){ }); }; +function test3DMeshColor(test,testColor,variableName){ + var color = casper.evaluate(function(variableName) { + var color = Canvas1.engine.getRealMeshesForInstancePath(variableName)[0].material.color; + return [color.r, color.g, color.b]; + },variableName); + + casper.echo("color"+color); + test.assertEquals(testColor[0], color[0], "Red default color is correct"); + test.assertEquals(testColor[1], color[1], "Green default color is correct"); + test.assertEquals(testColor[2], color[2], "Black default color is correct"); +} + +function testSelection(test,variableName){ + casper.mouseEvent('click', 'div#spotlight', "attempting to close spotlight"); + casper.echo("Clicking to close spotlight"); + casper.sendKeys('input#typeahead', casper.page.event.key.Escape, {keepFocus: true}); + casper.echo("Hitting escape to close spotlight"); + + casper.waitWhileVisible('div#spotlight', function () { + casper.echo("Spotlight closed"); + casper.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + casper.waitUntilVisible('div#spotlight', function () { + casper.sendKeys('input#typeahead', variableName, {keepFocus: true}); + casper.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); + casper.waitUntilVisible('button#buttonOne', function () { + test.assertVisible('button#buttonOne', "Select button correctly visible"); + this.evaluate(function(){ + $("#buttonOne").click(); + }); + this.wait(500, function () { + var selectColor = [1,0.8,0]; + test3DMeshColor(test,selectColor); + }); + }); + }); + }, null, 1000); +} + +function testSpotlight(test, variableName,plotName,expectButton,testSelect, selectionName){ + test.assertExists('i.fa-search', "Spotlight button exists") + casper.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + casper.waitUntilVisible('div#spotlight', function () { + test.assertVisible('div#spotlight', "Spotlight opened"); + + //type in the spotlight + this.sendKeys('input#typeahead', variableName, {keepFocus: true}); + //press enter + this.sendKeys('input#typeahead', this.page.event.key.Return, {keepFocus: true}); + + casper.waitUntilVisible('div#spotlight', function () { + casper.then(function () { + this.echo("Waiting to see if the Plot variables button becomes visible"); + if(expectButton){ + casper.waitUntilVisible('button#plot', function () { + test.assertVisible('button#plot', "Plot variables icon correctly visible"); + this.echo("Plot variables button became visible correctly"); + this.evaluate(function(){ + $("#plot").click(); + }); + this.waitUntilVisible(plotName, function () { + this.echo("Plot 2 came up correctly"); + if(testSelect){ + testSelection(test, selectionName); + } + }); + }, null, 8000); + }else{ + casper.wait(1000, function () { + casper.then(function () { + this.echo("Waiting to see if the Plot and watch variable buttons becomes visible"); + test.assertDoesntExist('button#plot', "Plot variables icon correctly invisible"); + test.assertDoesntExist('button#watch', "Watch button correctly hidden"); + this.echo("Variables button are hidden correctly"); + if(testSelect){ + testSelection(test, selectionName); + } + this.evaluate(function(){ + $(".js-plotly-plot").remove(); + }); + + this.waitWhileVisible('div[id="Plot1"]', function () { + this.echo("I've waited for Plot1 to disappear"); + },null,1000); + }); + }); + } + }); + }); + }); +} + function c302Test(test){ casper.waitForSelector('div[id="Plot1"]', function() { this.echo("I've waited for Plot1 to load."); @@ -300,33 +365,7 @@ function c302Test(test){ $("#controlpanel").hide(); }); - test.assertExists('i.fa-search', "Spotlight button exists") - this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); - - this.waitUntilVisible('div#spotlight', function () { - test.assertVisible('div#spotlight', "Spotlight opened"); - - //type in the spotlight - this.sendKeys('input#typeahead', "c302.ADAL[0].v", {keepFocus: true}); - //press enter - this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); - - casper.waitUntilVisible('div#spotlight', function () { - casper.then(function () { - this.echo("Waiting to see if the Plot variables button becomes visible"); - casper.waitUntilVisible('button#plot', function () { - test.assertVisible('button#plot', "Plot variables icon correctly visible"); - this.echo("Plot variables button became visible correctly"); - this.evaluate(function(){ - $("#plot").click(); - }); - this.waitUntilVisible('div[id="Plot3"]', function () { - this.echo("Plot 3 came up correctly"); - }); - }, null, 8000); - }); - }); - }); + testSpotlight(test, "c302.ADAL[0].v",'div[id="Plot3"]',true,false); }); }); }); @@ -337,6 +376,9 @@ function c302Test(test){ function acnetTest(test){ casper.then(function () { + var defaultColor = [0.00392156862745098,0.6,0.9098039215686274]; + test3DMeshColor(test,defaultColor,"acnet2.pyramidals_48[0]"); + this.echo("Opening controls panel"); this.evaluate(function() { $("#controlPanelBtn").click(); @@ -373,59 +415,9 @@ function acnetTest(test){ $("#controlpanel").hide(); }); - test.assertExists('i.fa-search', "Spotlight button exists") - this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); - - this.waitUntilVisible('div#spotlight', function () { - test.assertVisible('div#spotlight', "Spotlight opened"); - - //type in the spotlight - this.sendKeys('input#typeahead', "acnet2.pyramidals_48[1].soma_0.v", {keepFocus: true}); - //press enter - this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); - - casper.waitUntilVisible('div#spotlight', function () { - casper.then(function () { - this.echo("Waiting to see if the Plot variables button becomes visible"); - casper.waitUntilVisible('button#plot', function () { - test.assertVisible('button#plot', "Plot variables icon correctly visible"); - test.assertDoesntExist('button#watch', "Watch button correctly hidden"); - this.echo("Plot variables button became visible correctly"); - this.evaluate(function(){ - $("#plot").click(); - }); - this.waitUntilVisible('div[id="Plot2"]', function () { - this.echo("Plot 2 came up correctly"); - }); - }, null, 8000); - }); - }); - - this.mouseEvent('click', 'i.fa-search', "attempting to close spotlight"); - - casper.waitWhileVisible(function(){ - this.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); - - this.waitUntilVisible('div#spotlight', function () { - test.assertVisible('div#spotlight', "Spotlight opened"); - - //type in the spotlight - this.sendKeys('input#typeahead', - "acnet2.pyramidals_48[0].biophys.membraneProperties.Ca_pyr_soma_group.gDensity", {keepFocus: true}); - //press enter - this.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); - - casper.wait(1000, function () { - casper.then(function () { - this.echo("Waiting to see if the Plot and watch variable buttons becomes visible"); - test.assertDoesntExist('button#plot', "Plot variables icon correctly invisible"); - test.assertDoesntExist('button#watch', "Watch button correctly hidden"); - this.echo("Variables button are hidden correctly"); - }); - }); - }); - },null,1000); - }); + testSpotlight(test, "acnet2.pyramidals_48[1].soma_0.v",'div[id="Plot2"]',true,true,"acnet2.pyramidals_48[0]"); + this.mouseEvent('click', 'i.fa-search', "attempting to close spotlight"); + testSpotlight(test, "acnet2.pyramidals_48[0].biophys.membraneProperties.Ca_pyr_soma_group.gDensity",'div[id="Plot2"]',false,false); }); }); }, null, 1000); diff --git a/src/main/webapp/js/pages/tests/qunit/PersistenceTests.js b/src/main/webapp/js/pages/tests/qunit/PersistenceTests.js index fbf18e589..f0ad801b1 100644 --- a/src/main/webapp/js/pages/tests/qunit/PersistenceTests.js +++ b/src/main/webapp/js/pages/tests/qunit/PersistenceTests.js @@ -1069,13 +1069,8 @@ define(function (require) { assert.equal(window.Project.getExperiments().length, newLength, "Experiment deleted succesfully"); - this.runTimes++; - if(this.runTimes>5){ - done(); - resetConnection(); - }else{ - window.Project.getExperiments()[0].clone(); - } + done(); + resetConnection(); break; case GEPPETTO.GlobalHandler.MESSAGE_TYPE.INFO_MESSAGE: From 2d9e5ee88a7577be4ef62d033a79f450a54a4f95 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 8 May 2017 15:58:19 +0100 Subject: [PATCH 185/339] get/set view for tutorial component --- .../interface/tutorial/TutorialModule.js | 75 ++++++++++++++++++- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js index 9cfedb096..1d650acba 100644 --- a/src/main/webapp/js/components/interface/tutorial/TutorialModule.js +++ b/src/main/webapp/js/components/interface/tutorial/TutorialModule.js @@ -43,7 +43,6 @@ define(function (require) { document.getElementsByTagName("head")[0].appendChild(link); var React = require('react'), - ReactDOM = require('react-dom'), $ = require('jquery'), Button = require('../../controls/mixins/bootstrap/button'), GEPPETTO = require('geppetto'); @@ -51,7 +50,11 @@ define(function (require) { $.cookie=require('js-cookie'); var Tutorial = React.createClass({ - /** + + isDirty: false, + tutorials: [], + + /** * Stores cookie to avoid showing tutorial next time at startup */ dontShowAtStartup: function(val){ @@ -116,6 +119,15 @@ define(function (require) { } }, + gotToStep: function(currentStep){ + this.state.currentStep = currentStep; + if(this.state.currentStep <= this.getActiveTutorial().steps.length-1){ + this.updateTutorialWindow(); + }else{ + this.start(); + } + }, + nextStep: function(){ this.state.currentStep++; if(this.state.currentStep <= this.getActiveTutorial().steps.length-1){ @@ -170,6 +182,11 @@ define(function (require) { }, addTutorial : function(tutorialURL){ + // do not add if the same url was already successfully added + if(this.tutorials.includes(tutorialURL)){ + return; + } + var self = this; $.ajax({ @@ -177,6 +194,10 @@ define(function (require) { dataType: 'json', url: tutorialURL, success: function (responseData, textStatus, jqXHR) { + // on success add to tutorials utl list for view-state + self.tutorials.push(tutorialURL); + self.setDirty(true); + // load tutorial self.loadTutorial(responseData, false); }, error: function (responseData, textStatus, errorThrown) { @@ -226,7 +247,6 @@ define(function (require) { return false; }, - componentDidUpdate:function(){ if(this.chaptersMenu==undefined){ var that = this; @@ -310,7 +330,6 @@ define(function (require) { } }, - getCookie : function(){ var ignoreTutorial = $.cookie('ignore_tutorial'); if(ignoreTutorial == undefined){ @@ -380,6 +399,54 @@ define(function (require) { else{ return null; } + }, + + isDirty: function(){ + return this.isDirty; + }, + + setDirty: function(dirty){ + this.isDirty = dirty; + }, + + getView: function(){ + var baseView = {}; + + // add data-type and data field + any other custom fields in the component-specific attribute + baseView.dataType = "array"; + baseView.data = this.tutorials; + baseView.componentSpecific = { + activeTutorial: this.state.activeTutorial, + currentStep: this.state.currentStep, + }; + + return baseView; + }, + + setView: function(view){ + // TODO: set base properties + + // set data + if(view.data != undefined){ + if(view.dataType == 'array'){ + for(var i=0; i Date: Mon, 8 May 2017 17:19:45 +0100 Subject: [PATCH 186/339] missing semi colon --- src/main/webapp/js/components/ComponentFactory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index e71070c1a..dcd6f30f7 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -81,7 +81,7 @@ define(function (require) { var id = ""; var available; - var components = [] + var components = []; if (componentType in this.componentsMap){ components = this.componentsMap[componentType]; } From bb24c0aade3dd5a4b049c3d9c17ab2716efd551a Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Mon, 8 May 2017 18:14:18 +0100 Subject: [PATCH 187/339] Scaffolding for saving and restoring state --- .../components/interface/3dCanvas/Canvas.js | 192 +++++++++++++++++- 1 file changed, 191 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 0f2c9b6af..821a2b147 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -15,7 +15,21 @@ define(function (require) { var canvasComponent = React.createClass({ engine: null, container: null, - backgroundColor: 0x101010, + + isDirty: false, + + //State + canvasState: { + cameraPosition: {x: 0, y: 0, z: 0}, + cameraRotation: {rx: 0, ry: 0, rz: 0, radius: 0}, + colorMap: {}, + colorFunctionMap: {}, + opacityMap: {}, + geometryTypeMap: {}, + instances: [], + backgroundColor: 0x101010 + }, + /** * Displays all the passed instances in this canvas component @@ -24,20 +38,53 @@ define(function (require) { */ display: function (instances) { this.engine.buildScene(instances); + this.instances = this.canvasState.instances.concat(instances.map(function (item) { + return item.getInstancePath(); + })); + this.setDirty(true); return this; }, + /** + * Remove all the passed instances from this canvas component + * This method is only able to remove instances that were explicitly added + * e.g. if acnet2 is added acent2.baskets[3] can't be removed. + * @param instances an array of instances + * @returns {canvasComponent} + */ + remove: function (instances) { + for (var i = 0; i < instances.length; i++) { + if (this.canvasState.instances.indexOf(instances[i].getInstancePath()) != -1) { + this.canvasState.instances.splice(this.canvasState.instances.indexOf(instances[i].getInstancePath()), 1); + } + this.engine.removeFromScene(instances[i]); + } + this.setDirty(true); + this.resetCamera(); + return this; + }, + + /** * Displays all the instances available in the current model in this canvas * @returns {canvasComponent} */ displayAllInstances: function () { var that = this; + //TODO if the component is added after the events are triggered traverse all the existing instances GEPPETTO.on(GEPPETTO.Events.Instances_created, function (instances) { + that.canvasState.instances = that.canvasState.instances.concat(instances.map(function (item) { + return item.getInstancePath(); + })); + that.setDirty(true); that.engine.updateSceneWithNewInstances(instances); that.resetCamera(); }); GEPPETTO.on(GEPPETTO.Events.Instance_deleted, function (instance) { + if (that.canvasState.instances.indexOf(instance.getInstancePath()) != -1) { + that.canvasState.instances.splice(that.canvasState.instances.indexOf(instance.getInstancePath()), 1); + } + that.setDirty(true); that.engine.removeFromScene(instance); that.resetCamera(); }); @@ -115,6 +162,17 @@ define(function (require) { return this; }, + /** + * Set background color for this canvas + * + * * @param {String} color - hex or rgb color. e.g. "#ff0000" / "rgb(255,0,0)" + */ + setBackgroundColor: function (color) { + this.canvasState.backgroundColor = color; + this.setDirty(true); + this.dialog.css("background", color); + }, + /** * Change the color of a given instance * @@ -125,6 +183,8 @@ define(function (require) { */ setColor: function (instancePath, color) { this.engine.setColor(instancePath, color); + this.canvasState.colorMap[instancePath] = color; + this.setDirty(true); return this; }, @@ -148,6 +208,8 @@ define(function (require) { */ setOpacity: function (instancePath, opacity) { this.engine.setOpacity(instancePath, opacity); + this.canvasState.opacityMap[instancePath] = opacity; + this.setDirty(true); return this; }, @@ -172,6 +234,8 @@ define(function (require) { */ setGeometryType: function (instance, type, thickness) { this.engine.setGeometryType(instance, type, thickness); + this.canvasState.geometryTypeMap[instance.getInstancePath()] = {"type": type, "thickness": thickness}; + this.setDirty(true); return this; }, @@ -193,6 +257,10 @@ define(function (require) { */ addColorFunction: function (instances, colorfn) { this.engine.colorController.addColorFunction(instances, colorfn); + for (var i = 0; i < instances.length; i++) { + this.canvasState.colorFunctionMap[instances[i].getInstancePath()] = colorfn.toString(); + } + this.setDirty(true); return this; }, @@ -203,6 +271,12 @@ define(function (require) { * @return {canvasComponent} */ removeColorFunction: function (instances) { + for (var i = 0; i < instances.length; i++) { + if (this.colorFunctionMap[instances[i].getInstancePath()] != undefined) { + delete this.canvasState.colorFunctionMap[instances[i].getInstancePath()]; + } + } + this.setDirty(true); this.engine.colorController.removeColorFunction(instances); return this; }, @@ -259,6 +333,10 @@ define(function (require) { * @param z */ setCameraPosition: function (x, y, z) { + this.canvasState.cameraPosition.x = x; + this.canvasState.cameraPosition.y = y; + this.canvasState.cameraPosition.z = z; + this.setDirty(true); this.engine.setCameraPosition(x, y, z); return this; }, @@ -270,6 +348,11 @@ define(function (require) { * @param radius */ setCameraRotation: function (rx, ry, rz, radius) { + this.canvasState.cameraRotation.rx = rx; + this.canvasState.cameraRotation.ry = ry; + this.canvasState.cameraRotation.rz = rz; + this.canvasState.cameraRotation.radius = radius; + this.setDirty(true); this.engine.setCameraRotation(rx, ry, rz, radius); return this; }, @@ -306,6 +389,106 @@ define(function (require) { return [width, height]; }, + + //TODO Move to base class for components + /** + * Did something change in the state of the widget? + * + * @command isDirty() + * @returns {boolean} - ID of widget + */ + isDirty: function () { + return this.isDirty; + }, + + //TODO Move to base class for components + /** + * Explicitly sets status of view + * NOTE: we need to be able to control this from outside the component + * + * @command setDirty() + * @param {boolean} dirty + */ + setDirty: function (dirty) { + this.isDirty = dirty; + }, + + /** + * + * @param view + */ + setView: function (view) { + // set base properties + Widget.View.prototype.setView.call(this, view); + + // set data + if (view.data != undefined) { + if (view.dataType == 'instances') { + this.display(view.data); + } + } + + // set component specific stuff, only custom handlers for popup widget + if (view.componentSpecific != undefined) { + if (view.componentSpecific.cameraRotation != undefined) { + this.setCameraRotation( + view.componentSpecific.cameraRotation.rx, + view.componentSpecific.cameraRotation.ry, + view.componentSpecific.cameraRotation.rz, + view.componentSpecific.cameraRotation.radius); + } + if (view.componentSpecific.cameraPosition != undefined) { + this.setCameraPosition( + view.componentSpecific.cameraPosition.x, + view.componentSpecific.cameraPosition.y, + view.componentSpecific.cameraPosition.z); + } + if (view.componentSpecific.colorMap != undefined) { + for (var path in view.componentSpecific.colorMap) { + this.setColor(path, view.componentSpecific.colorMap[path]); + } + } + if (view.componentSpecific.opacityMap != undefined) { + for (var path in view.componentSpecific.opacityMap) { + this.setOpacity(path, view.componentSpecific.opacityMap[path]); + } + } + if (view.componentSpecific.geometryTypeMap != undefined) { + for (var path in view.componentSpecific.geometryTypeMap) { + this.setGeometryType(eval(path), + view.componentSpecific.geometryTypeMap[path].type, + view.componentSpecific.geometryTypeMap[path].thickness); + } + } + if (view.componentSpecific.colorFunctionMap != undefined) { + for (var path in view.componentSpecific.colorFunctionMap) { + this.addColorFunction([eval(path)], eval("(" + view.componentSpecific.colorFunctionMap[path] + ")")); + } + } + if (view.componentSpecific.backgroundColor != undefined) { + this.setBackgroundColor(view.componentSpecific.backgroundColor); + } + } + + // after setting view through setView, reset dirty flag + this.setDirty(false); + }, + + + /** + * + * @returns {*} + */ + getView: function () { + var baseView = Widget.View.prototype.getView.call(this); + + // add data-type and data field + any other custom fields in the component-specific attribute + baseView.dataType = "instances"; + baseView.data = this.instances; + baseView.componentSpecific = this.canvasState; + return baseView; + }, + /** * * @returns {boolean} @@ -314,6 +497,9 @@ define(function (require) { return false; }, + /** + * + */ componentDidMount: function () { if (!isWebglEnabled) { Detector.addGetWebGLMessage(); @@ -339,6 +525,10 @@ define(function (require) { } }, + /** + * + * @returns {XML} + */ render: function () { return (
                From 76b85004a9386026ece1d120cc2c85978357c60d Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 8 May 2017 21:09:01 -0700 Subject: [PATCH 188/339] expanding casper tests --- .../js/pages/tests/casperjs/CoreTests.js | 570 +++++++----------- .../pages/tests/casperjs/CoreTestsUtility.js | 264 ++++++++ .../webapp/js/pages/tests/casperjs/README.md | 2 + 3 files changed, 498 insertions(+), 338 deletions(-) create mode 100644 src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index 3086a5d77..0208088cc 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -1,18 +1,3 @@ -var urlBase = "http://127.0.0.1:8080/"; -var baseFollowUp = "org.geppetto.frontend/geppetto?"; - -var hhcellProject = "load_project_from_id=1"; -var c302Project = "load_project_from_id=6"; -var acnetProject = "load_project_from_id=5"; -var ca1Project = "load_project_from_id=3"; -var cElegansConnectome = "load_project_from_id=16"; -var cElegansMuscleModel = "load_project_from_id=4"; -var cElegansPVDR = "load_project_from_id=8"; -var eyeWire = "load_project_from_id=9"; -var nwbSample = "load_project_from_id=18"; -var Pharyngeal = "load_project_from_id=58"; - - casper.test.begin('Geppetto basic tests', 52, function suite(test) { casper.options.viewportSize = { width: 1340, @@ -48,18 +33,17 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { }, null, 3000); }); -// /**Tests HHCELL project**/ -// casper.thenOpen(urlBase+baseFollowUp+hhcellProject,function() { -// this.waitWhileVisible('div[id="loading-spinner"]', function () { -// this.echo("I've waited for hhcell project to load."); -// test.assertTitle("geppetto", "geppetto title is ok"); -// test.assertUrlMatch(/load_project_from_id=1/, "project load attempted"); -// test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); -// test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); -// test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); -// hhcellTest(test); -// },null,50000); -// }); + /**Tests HHCELL project**/ + casper.thenOpen(urlBase+baseFollowUp+hhcellProject,function() { + casper.then(function(){launchTest(test,"Hhcell");}); + casper.then(function(){hhcellTest(test);}); + }); + + /**Tests Acnet project**/ + casper.thenOpen(urlBase+baseFollowUp+acnetProject,function() { + casper.then(function(){launchTest(test,"ACNet");}); + casper.then(function(){acnetTest(test);}); + }); // // /**Tests C302 project**/ // casper.thenOpen(urlBase+baseFollowUp+c302Project,function() { @@ -74,69 +58,58 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { // },null,450000); // }); - /**Tests Acnet project**/ - casper.thenOpen(urlBase+baseFollowUp+acnetProject,function() { - this.waitWhileVisible('div[id="loading-spinner"]', function () { - this.echo("I've waited for ACNet project to load."); - test.assertTitle("geppetto", "geppetto title is ok"); - test.assertUrlMatch(/load_project_from_id=5/, "project load attempted"); - test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); - test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); - acnetTest(test); - },null,40000); - }); - /**Tests CA1 project**/ - casper.thenOpen(urlBase+baseFollowUp+ca1Project,function() { - this.waitWhileVisible('div[id="loading-spinner"]', function () { - this.echo("I've waited for CA1 project to load."); - test.assertTitle("geppetto", "geppetto title is ok"); - test.assertUrlMatch(/load_project_from_id=3/, "project load attempted"); - test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); - test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); - ca1Test(test); - },null,40000); - }); - - /**Tests EyeWire project**/ - casper.thenOpen(urlBase+baseFollowUp+eyeWire,function() { - this.waitForSelector('div[id="Popup1"]', function() { - this.echo("I've waited for the EyeWireGanglionCell project to load."); - test.assertTitle("geppetto", "geppetto title is ok"); - test.assertUrlMatch(/load_project_from_id=9/, "project load attempted"); - test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); - test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); - }, null, 50000); - }); - - /**Tests Pharyngeal project**/ - casper.thenOpen(urlBase+baseFollowUp+Pharyngeal,function() { - this.waitWhileVisible('div[id="loading-spinner"]', function () { - this.echo("I've waited for the Pharyngeal project to load."); - test.assertTitle("geppetto", "geppetto title is ok"); - test.assertUrlMatch(/load_project_from_id=58/, "project load attempted"); - test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); - test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); - pharyngealTest(test); - },null,50000); - }); - - /**Tests NWB project**/ - casper.thenOpen(urlBase+baseFollowUp+nwbSample,function() { - this.waitWhileVisible('div[id="loading-spinner"]', function () { - this.echo("I've waited for NWB project to load."); - test.assertTitle("geppetto", "geppetto title is ok"); - test.assertUrlMatch(/load_project_from_id=18/, "project load attempted"); - test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); - test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); - nwbSampleTest(test); - },null,50000); - }); +// +// /**Tests CA1 project**/ +// casper.thenOpen(urlBase+baseFollowUp+ca1Project,function() { +// this.waitWhileVisible('div[id="loading-spinner"]', function () { +// this.echo("I've waited for CA1 project to load."); +// test.assertTitle("geppetto", "geppetto title is ok"); +// test.assertUrlMatch(/load_project_from_id=3/, "project load attempted"); +// test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); +// test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); +// test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); +// ca1Test(test); +// },null,40000); +// }); +// +// /**Tests EyeWire project**/ +// casper.thenOpen(urlBase+baseFollowUp+eyeWire,function() { +// this.waitForSelector('div[id="Popup1"]', function() { +// this.echo("I've waited for the EyeWireGanglionCell project to load."); +// test.assertTitle("geppetto", "geppetto title is ok"); +// test.assertUrlMatch(/load_project_from_id=9/, "project load attempted"); +// test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); +// test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); +// test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); +// }, null, 50000); +// }); +// +// /**Tests Pharyngeal project**/ +// casper.thenOpen(urlBase+baseFollowUp+Pharyngeal,function() { +// this.waitWhileVisible('div[id="loading-spinner"]', function () { +// this.echo("I've waited for the Pharyngeal project to load."); +// test.assertTitle("geppetto", "geppetto title is ok"); +// test.assertUrlMatch(/load_project_from_id=58/, "project load attempted"); +// test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); +// test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); +// test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); +// pharyngealTest(test); +// },null,50000); +// }); +// +// /**Tests NWB project**/ +// casper.thenOpen(urlBase+baseFollowUp+nwbSample,function() { +// this.waitWhileVisible('div[id="loading-spinner"]', function () { +// this.echo("I've waited for NWB project to load."); +// test.assertTitle("geppetto", "geppetto title is ok"); +// test.assertUrlMatch(/load_project_from_id=18/, "project load attempted"); +// test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); +// test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); +// test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); +// nwbSampleTest(test); +// },null,50000); +// }); // casper.thenOpen(urlBase+baseFollowUp+cElegansConnectome,function() { // this.waitForSelector('div[id="sim-toolbar"]', function() { @@ -170,269 +143,192 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { }); }); +/** + * + * @param test + * @param name + * @returns + */ function hhcellTest(test,name){ + casper.echo("------------STARTING HHCELL TEST--------------"); + testCameraControls(test, [0,0,30.90193733102435]); casper.then(function () { - var defaultColor = [0.00392156862745098,0.6,0.9098039215686274]; - test3DMeshColor(test,defaultColor,"hhcell.hppop[0]"); - - this.echo("Opening controls panel"); - this.evaluate(function() { - $("#controlPanelBtn").click(); + test3DMeshColor(test,defaultColor,"hhcell.hhpop[0]"); + casper.echo("Opening controls panel"); + buttonClick("#controlPanelBtn"); + }); + casper.then(function(){ + testInitialControlPanelValues(test,3); + }) + casper.then(function(){ + testVisibility(test,"hhcell.hhpop[0]","#hhcell_hhpop_0__visibility_ctrlPanel_btn"); + }) + casper.then(function () { + buttonClick("#stateVariablesFilterBtn"); + }); + casper.then(function () { + this.waitUntilVisible('button[id="hhcell_hhpop_0__v_plot_ctrlPanel_btn"]', function () { + buttonClick("#hhcell_hhpop_0__v_plot_ctrlPanel_btn"); + }); + }); + casper.then(function(){ + this.waitUntilVisible('div[id="Plot1"]', function () { + this.echo("I've waited for Plot1 to come up"); + removeAllPlots(); + buttonClick("#anyProjectFilterBtn"); }); - - this.waitUntilVisible('div#controlpanel', function () { - test.assertVisible('div#controlpanel', "The control panel is correctly open."); - + }); + casper.then(function(){ + this.wait(500,function(){ var rows = casper.evaluate(function() { var rows = $(".standard-row").length; return rows; }); - test.assertEquals(rows, 3, "The control panel opened with right amount of rows"); - - casper.evaluate(function(){ - $("#stateVariablesFilterBtn").click(); - }); - - casper.wait(500,function(){ - this.evaluate(function(){ - $("#hhcell_hhpop_0__v_plot_ctrlPanel_btn").click(); - }); - - this.waitUntilVisible('div[id="Plot1"]', function () { - this.echo("I've waited for Plot1 to come up"); - - this.evaluate(function(){ - $("#Plot1").remove(); - $("#anyProjectFilterBtn").click(); - }); - - this.wait(500,function(){ - var rows = casper.evaluate(function() { - var rows = $(".standard-row").length; - return rows; - }); - test.assertEquals(rows, 10, "Correct amount of rows for Global filter"); - - casper.evaluate(function() { - $("#controlpanel").hide(); - }); - - testSpotlight(test, "hhcell.hhpop[0].v",'div[id="Plot1"]',true,true,"hhcell"); - }); - }); - }); - }, null, 500); + test.assertEquals(rows, 10, "Correct amount of rows for Global filter"); + buttonClick("#controlpanel"); + testSpotlight(test, "hhcell.hhpop[0].v",'div[id="Plot1"]',true,true,"hhcell","hhcell.hhpop[0]"); + }); }); + + casper.then(function(){ + closeSpotlight(); + casper.evaluate(function(){ + eval("hhcell.deselect()"); + eval("GEPPETTO.ComponentFactory.addWidget('CANVAS', {name: '3D Canvas',}, function () {this.setName('Widget Canvas');this.setPosition();this.display([hhcell])});") + eval("GEPPETTO.SceneController.addColorFunction(GEPPETTO.ModelFactory.instances.getInstance(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith('.v'),false), window.voltage_color);"); + eval("Project.getActiveExperiment().play({step:1})"); + }); + + testCameraControls(test, [0,0,30.90193733102435]); + + casper.wait(1000, function(){ + test3DMeshColorNotEquals(test,defaultColor,"hhcell.hhpop[0]"); + }); + casper.wait(3000, function(){ + test3DMeshColorNotEquals(test,defaultColor,"hhcell.hhpop[0]"); + }); + casper.wait(5000, function(){ + test3DMeshColorNotEquals(test,defaultColor,"hhcell.hhpop[0]"); + casper.echo("Done Playing, now exiting"); + }) + }) }; -function test3DMeshColor(test,testColor,variableName){ - var color = casper.evaluate(function(variableName) { - var color = Canvas1.engine.getRealMeshesForInstancePath(variableName)[0].material.color; - return [color.r, color.g, color.b]; - },variableName); +/** + * + * @param test + * @returns + */ +function acnetTest(test){ + casper.echo("------------STARTING ACNET TEST--------------"); + testCameraControls(test,[231.95608349343888,508.36555704435455,1849.8390363191731]); + casper.then(function () { + test3DMeshColor(test,defaultColor,"acnet2.pyramidals_48[0]"); + this.echo("Opening controls panel"); + buttonClick("#controlPanelBtn"); + }); - casper.echo("color"+color); - test.assertEquals(testColor[0], color[0], "Red default color is correct"); - test.assertEquals(testColor[1], color[1], "Green default color is correct"); - test.assertEquals(testColor[2], color[2], "Black default color is correct"); -} - -function testSelection(test,variableName){ - casper.mouseEvent('click', 'div#spotlight', "attempting to close spotlight"); - casper.echo("Clicking to close spotlight"); - casper.sendKeys('input#typeahead', casper.page.event.key.Escape, {keepFocus: true}); - casper.echo("Hitting escape to close spotlight"); - - casper.waitWhileVisible('div#spotlight', function () { - casper.echo("Spotlight closed"); - casper.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); - - casper.waitUntilVisible('div#spotlight', function () { - casper.sendKeys('input#typeahead', variableName, {keepFocus: true}); - casper.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); - casper.waitUntilVisible('button#buttonOne', function () { - test.assertVisible('button#buttonOne', "Select button correctly visible"); - this.evaluate(function(){ - $("#buttonOne").click(); - }); - this.wait(500, function () { - var selectColor = [1,0.8,0]; - test3DMeshColor(test,selectColor); - }); - }); - }); - }, null, 1000); -} - -function testSpotlight(test, variableName,plotName,expectButton,testSelect, selectionName){ - test.assertExists('i.fa-search', "Spotlight button exists") - casper.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); - - casper.waitUntilVisible('div#spotlight', function () { - test.assertVisible('div#spotlight', "Spotlight opened"); - - //type in the spotlight - this.sendKeys('input#typeahead', variableName, {keepFocus: true}); - //press enter - this.sendKeys('input#typeahead', this.page.event.key.Return, {keepFocus: true}); + casper.then(function () { + testInitialControlPanelValues(test,10); + }); + casper.then(function(){ + testVisibility(test,"acnet2.pyramidals_48[0]","#acnet2_pyramidals_48_0__visibility_ctrlPanel_btn"); + }) + casper.then(function () { + buttonClick("#stateVariablesFilterBtn"); + }); + + casper.then(function () { + this.waitUntilVisible('button[id="acnet2_pyramidals_48_0__soma_0_v_plot_ctrlPanel_btn"]', function () { + buttonClick("#acnet2_pyramidals_48_0__soma_0_v_plot_ctrlPanel_btn"); + }); + }); - casper.waitUntilVisible('div#spotlight', function () { - casper.then(function () { - this.echo("Waiting to see if the Plot variables button becomes visible"); - if(expectButton){ - casper.waitUntilVisible('button#plot', function () { - test.assertVisible('button#plot', "Plot variables icon correctly visible"); - this.echo("Plot variables button became visible correctly"); - this.evaluate(function(){ - $("#plot").click(); - }); - this.waitUntilVisible(plotName, function () { - this.echo("Plot 2 came up correctly"); - if(testSelect){ - testSelection(test, selectionName); - } - }); - }, null, 8000); - }else{ - casper.wait(1000, function () { - casper.then(function () { - this.echo("Waiting to see if the Plot and watch variable buttons becomes visible"); - test.assertDoesntExist('button#plot', "Plot variables icon correctly invisible"); - test.assertDoesntExist('button#watch', "Watch button correctly hidden"); - this.echo("Variables button are hidden correctly"); - if(testSelect){ - testSelection(test, selectionName); - } - this.evaluate(function(){ - $(".js-plotly-plot").remove(); - }); - - this.waitWhileVisible('div[id="Plot1"]', function () { - this.echo("I've waited for Plot1 to disappear"); - },null,1000); - }); - }); - } - }); + casper.then(function () { + this.waitUntilVisible('div[id="Plot1"]', function () { + test.assertExists('div[id="Plot1"]', "Plot1 exists"); + casper.evaluate(function() { + $("#controlpanel").hide(); + }); }); }); + + casper.then(function () { + testSpotlight(test, "acnet2.pyramidals_48[1].soma_0.v",'div[id="Plot2"]',true,true,"acnet2.pyramidals_48[0]","acnet2.pyramidals_48[0]"); + this.mouseEvent('click', 'i.fa-search', "attempting to close spotlight"); + }); + + casper.then(function () { + testSpotlight(test, "acnet2.pyramidals_48[0].biophys.membraneProperties.Ca_pyr_soma_group.gDensity",'div[id="Plot2"]',false,false,"acnet2.pyramidals_48[0]"); + }); + + casper.then(function(){ + closeSpotlight(); + casper.evaluate(function(){ + eval("acnet2.pyramidals_48[0].deselect()"); + eval("GEPPETTO.ComponentFactory.addWidget('CANVAS', {name: '3D Canvas',}, function () {this.setName('Widget Canvas');this.setPosition();this.display([acnet2])});"); + eval("GEPPETTO.SceneController.addColorFunction(GEPPETTO.ModelFactory.instances.getInstance(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith('.v'),false), window.voltage_color);"); + eval("Project.getActiveExperiment().play({step:10})"); + }); + + casper.wait(2000, function(){ + test3DMeshColorNotEquals(test,defaultColor,"acnet2.baskets_12[2].soma_0"); + }); + casper.wait(3000, function(){ + test3DMeshColorNotEquals(test,defaultColor,"acnet2.baskets_12[2].soma_0"); + }); + casper.wait(5000, function(){ + test3DMeshColorNotEquals(test,defaultColor,"acnet2.baskets_12[2].soma_0"); + removeAllPlots(); + casper.echo("Done Playing, now exiting"); + }) + }) } function c302Test(test){ + casper.echo("------------STARTING C302 TEST--------------"); casper.waitForSelector('div[id="Plot1"]', function() { this.echo("I've waited for Plot1 to load."); test.assertExists('div[id="Plot1"]', "geppetto loads the initial Plot1"); - this.then(function () { - this.echo("Opening controls panel"); - this.evaluate(function() { - $("#controlPanelBtn").click(); - }); - - this.waitUntilVisible('div#controlpanel', function () { - test.assertVisible('div#controlpanel', "The control panel is correctly open."); - - var rows = casper.evaluate(function() { - var rows = $(".standard-row").length; - return rows; - }); - test.assertEquals(rows, 10, "The control panel opened with right amount of rows"); - - casper.evaluate(function(){ - $("#stateVariablesFilterBtn").click(); - }) - - casper.wait(500,function(){ - var plots = this.evaluate(function(){ - $("#c302_ADAL_0__v_plot_ctrlPanel_btn").click(); - var plots = $(".js-plotly-plot").length; - - return plots; - }); - - this.waitUntilVisible('div[id="Plot2"]', function () { - this.echo("I've waited for Plot2 to come up"); - this.evaluate(function(){ - $("#anyProjectFilterBtn").click(); - }); - this.wait(500,function(){ - var rows = casper.evaluate(function() { - var rows = $(".standard-row").length; - return rows; - }); - test.assertEquals(rows, 10, "Correct amount of rows for Global filter"); - - casper.evaluate(function() { - $("#controlpanel").hide(); - }); - - testSpotlight(test, "c302.ADAL[0].v",'div[id="Plot3"]',true,false); - }); - }); - }); - }, null, 500); - }); }, null, 5000); -} - -function acnetTest(test){ + casper.then(function () { - var defaultColor = [0.00392156862745098,0.6,0.9098039215686274]; - test3DMeshColor(test,defaultColor,"acnet2.pyramidals_48[0]"); - this.echo("Opening controls panel"); - this.evaluate(function() { - $("#controlPanelBtn").click(); + buttonClick("#controlPanelBtn"); + }); + + casper.then(function(){ + testInitialControlPanelValues(test,10); + }); + + casper.then(function(){ + var plots = this.evaluate(function(){ + buttonClick("#c302_ADAL_0__v_plot_ctrlPanel_btn"); + var plots = $(".js-plotly-plot").length; + return plots; }); - this.waitUntilVisible('div#controlpanel', function () { - test.assertVisible('div#controlpanel', "The control panel is correctly open."); - - var rows = casper.evaluate(function() { - var rows = $(".standard-row").length; - return rows; - }); - test.assertEquals(rows, 10, "The control panel opened with right amount of rows"); - - casper.evaluate(function(){ - $("#stateVariablesFilterBtn").click(); - }) - - casper.wait(500,function(){ - this.evaluate(function(){ - $("#acnet2_pyramidals_48_0__soma_0_v_plot_ctrlPanel_btn").click(); + this.waitUntilVisible('div[id="Plot2"]', function () { + this.echo("I've waited for Plot2 to come up"); + buttonClick("#anyProjectFilterBtn"); + this.wait(500,function(){ + var rows = casper.evaluate(function() { + var rows = $(".standard-row").length; + return rows; }); - - this.waitUntilVisible('div[id="Plot1"]', function () { - var plots = this.evaluate(function(){ - var plots = $("#Plot1").length; - - return plots; - }); - this.echo("I've waited for Plot1 to come up"); - test.assertEquals(plots, 1,"Amount of plot widgets in existence passed"); - - casper.evaluate(function() { - $("#controlpanel").hide(); - }); - - testSpotlight(test, "acnet2.pyramidals_48[1].soma_0.v",'div[id="Plot2"]',true,true,"acnet2.pyramidals_48[0]"); - this.mouseEvent('click', 'i.fa-search', "attempting to close spotlight"); - testSpotlight(test, "acnet2.pyramidals_48[0].biophys.membraneProperties.Ca_pyr_soma_group.gDensity",'div[id="Plot2"]',false,false); + test.assertEquals(rows, 10, "Correct amount of rows for Global filter"); + casper.evaluate(function() { + $("#controlpanel").hide(); }); + testSpotlight(test, "c302.ADAL[0].v",'div[id="Plot3"]',true,false); }); - }, null, 1000); - }); + }); + }) } function ca1Test(test){ casper.waitForSelector('div[id="TreeVisualiserDAT1"]', function() { this.echo("I've waited for the TreeVisualiserDAT1 to load."); - test.assertTitle("geppetto", "geppetto title is ok"); - test.assertUrlMatch(/load_project_from_id=3/, "project load attempted"); - test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); - test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); - test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial camera controls"); - casper.then(function () { this.echo("Opening controls panel"); this.evaluate(function() { @@ -490,27 +386,25 @@ function ca1Test(test){ } function pharyngealTest(test){ - casper.waitForSelector('div[id="Plot1"]', function() { - this.echo("I've waited for Plot1 to load."); - this.echo("Opening controls panel"); - this.evaluate(function() { - $("#controlPanelBtn").click(); - }); - - this.waitUntilVisible('div#controlpanel', function () { - test.assertVisible('div#controlpanel', "The control panel is correctly open."); - - var rows = casper.evaluate(function() { - var rows = $(".standard-row").length; - return rows; + casper.then(function(){ + casper.waitForSelector('div[id="Plot1"]', function() { + this.echo("I've waited for Plot1 to load."); + this.echo("Opening controls panel"); + this.evaluate(function() { + $("#controlPanelBtn").click(); }); - test.assertEquals(rows, 10, "The control panel opened with right amount of rows"); + }, null, 30000); + }); - this.waitForSelector('div[id="ButtonBar1"]', function() { - this.echo("I've waited for ButtonBar component to load."); - }); - }, null, 500);; - }, null, 30000); + casper.then(function(){ + testInitialControlPanelValues(test,10); + }); + + casper.then(function(){ + casper.waitForSelector('div[id="ButtonBar1"]', function() { + this.echo("I've waited for ButtonBar component to load."); + }); + }); }; function nwbSampleTest(test){ diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js new file mode 100644 index 000000000..281b0d99a --- /dev/null +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js @@ -0,0 +1,264 @@ +var urlBase = "http://127.0.0.1:8080/"; +var baseFollowUp = "org.geppetto.frontend/geppetto?"; + +var hhcellProject = "load_project_from_id=1"; +var c302Project = "load_project_from_id=6"; +var acnetProject = "load_project_from_id=5"; +var ca1Project = "load_project_from_id=3"; +var cElegansConnectome = "load_project_from_id=16"; +var cElegansMuscleModel = "load_project_from_id=4"; +var cElegansPVDR = "load_project_from_id=8"; +var eyeWire = "load_project_from_id=9"; +var nwbSample = "load_project_from_id=18"; +var Pharyngeal = "load_project_from_id=58"; +var defaultColor = [0.00392156862745098,0.6,0.9098039215686274]; +var zoomClicks = 50, panClicks=10, rotateClicks=20; + +function launchTest(test, projectName){ + casper.waitWhileVisible('div[id="loading-spinner"]', function () { + this.echo("I've waited for "+projectName+" project to load."); + test.assertTitle("geppetto", "geppetto title is ok"); + test.assertExists('div[id="sim-toolbar"]', "geppetto loads the initial simulation controls"); + test.assertExists('div[id="controls"]', "geppetto loads the initial camera controls"); + test.assertExists('div[id="foreground-toolbar"]', "geppetto loads the initial foreground controls"); + },null,30000); +} + +function resetCameraTest(test,expectedCameraPosition){ + buttonClick("#panHomeBtn"); + testCameraPosition(test,expectedCameraPosition); +} + +function testInitialControlPanelValues(test, values){ + casper.waitUntilVisible('div#controlpanel', function () { + test.assertVisible('div#controlpanel', "The control panel is correctly open."); + var rows = casper.evaluate(function() { + var rows = $(".standard-row").length; + return rows; + }); + test.assertEquals(rows, values, "The control panel opened with right amount of rows"); + }); +} + +function removeAllPlots(){ + casper.then(function(){ + casper.evaluate(function() { + $("div.js-plotly-plot").remove(); + }); + this.waitWhileVisible('div[id="Plot1"]', function () { + this.echo("I've waited for Plot1 to disappear"); + },null,1000); + }); +} + +function buttonClick(buttonName){ + casper.evaluate(function(buttonName) { + $(buttonName).click(); + },buttonName); +} +/** + * + * @param test + * @param variableName + * @param buttonName + * @returns + */ +function testVisibility(test,variableName, buttonName){ + casper.then(function(){ + testMeshVisibility(test,true,variableName); + }); + + casper.then(function(){ + buttonClick(buttonName); + }); + + casper.then(function(){ + testMeshVisibility(test,false,variableName); + }); + + casper.then(function(){ + buttonClick(buttonName); + }); + + casper.then(function(){ + testMeshVisibility(test,true,variableName); + }); +} + +/** + * + * @param test + * @param visible + * @param variableName + * @returns + */ +function testMeshVisibility(test,visible,variableName){ + var visibility = casper.evaluate(function(variableName) { + var visibility = Canvas1.engine.getRealMeshesForInstancePath(variableName)[0].visible; + return visibility; + },variableName); + + test.assertEquals(visibility,visible, variableName +" visibility correct"); +} + + +function testCameraPosition(test,expectedCamPosition){ + var camPosition = casper.evaluate(function() { + var position = Canvas1.engine.camera.position; + return [position.x, position.y, position.z]; + }); + + test.assertEquals( camPosition[0],expectedCamPosition[0], "Vector's x coordinate is correct as camera poistion"); + test.assertEquals( camPosition[1],expectedCamPosition[1], "Vector's y coordinate is correct as camera poistion"); + test.assertEquals( camPosition[2],expectedCamPosition[2], "Vector's z coordinate is correct as camera poistion"); +} + +/** + * + * @param test + * @param testColor + * @param variableName + * @returns + */ +function test3DMeshColor(test,testColor,variableName){ + var color = casper.evaluate(function(variableName) { + var color = Canvas1.engine.getRealMeshesForInstancePath(variableName)[0].material.color; + return [color.r, color.g, color.b]; + },variableName); + + test.assertEquals(testColor[0], color[0], "Red default color is correct for "+ variableName); + test.assertEquals(testColor[1], color[1], "Green default color is correct for " + variableName); + test.assertEquals(testColor[2], color[2], "Black default color is correct for " +variableName); +} + +function test3DMeshColorNotEquals(test,testColor,variableName){ + var color = casper.evaluate(function(variableName) { + var color = Canvas1.engine.getRealMeshesForInstancePath(variableName)[0].material.color; + return [color.r, color.g, color.b]; + },variableName); + + test.assertNotEquals(testColor[0], color[0], "Red default color is correctly different for "+ variableName); + test.assertNotEquals(testColor[1], color[1], "Green default color is correctly different for " + variableName); + test.assertNotEquals(testColor[2], color[2], "Black default color is correctly different for " +variableName); +} + +/** + * Test Selection + * @param test - Global test variable reference + * @param variableName - Name of instance to apply selection to + * @param selectColorVarName - Expected color to find in istance's mesh material + * @returns + */ +function testSelection(test,variableName,selectColorVarName){ + closeSpotlight(); + casper.waitWhileVisible('div#spotlight', function () { + casper.echo("Spotlight closed"); + casper.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + casper.waitUntilVisible('div#spotlight', function () { + casper.sendKeys('input#typeahead', variableName, {keepFocus: true}); + casper.sendKeys('input#typeahead', casper.page.event.key.Return, {keepFocus: true}); + casper.waitUntilVisible('button#buttonOne', function () { + test.assertVisible('button#buttonOne', "Select button correctly visible"); + buttonClick("#buttonOne"); + this.wait(500, function () { + var selectColor = [1,0.8,0]; + test3DMeshColor(test,selectColor,selectColorVarName); + }); + }); + }); + }, null, 1000); +} + +function closeSpotlight(){ + casper.mouseEvent('click', 'div#spotlight', "attempting to close spotlight"); + casper.echo("Clicking to close spotlight"); + casper.sendKeys('input#typeahead', casper.page.event.key.Escape, {keepFocus: true}); + casper.echo("Hitting escape to close spotlight"); +} + +/** + * + * @param test + * @param variableName + * @param plotName + * @param expectButton + * @param testSelect + * @param selectionName + * @param selectColorVarName + * @returns + */ +function testSpotlight(test, variableName,plotName,expectButton,testSelect, selectionName, selectColorVarName){ + test.assertExists('i.fa-search', "Spotlight button exists") + casper.mouseEvent('click', 'i.fa-search', "attempting to open spotlight"); + + casper.waitUntilVisible('div#spotlight', function () { + test.assertVisible('div#spotlight', "Spotlight opened"); + + //type in the spotlight + this.sendKeys('input#typeahead', variableName, {keepFocus: true}); + //press enter + this.sendKeys('input#typeahead', this.page.event.key.Return, {keepFocus: true}); + + casper.waitUntilVisible('div#spotlight', function () { + casper.then(function () { + this.echo("Waiting to see if the Plot variables button becomes visible"); + if(expectButton){ + casper.waitUntilVisible('button#plot', function () { + test.assertVisible('button#plot', "Plot variables icon correctly visible"); + this.echo("Plot variables button became visible correctly"); + buttonClick("#plot"); + this.waitUntilVisible(plotName, function () { + this.echo("Plot 2 came up correctly"); + if(testSelect){ + testSelection(test, selectionName,selectColorVarName); + } + }); + }, null, 8000); + }else{ + casper.wait(1000, function () { + casper.then(function () { + this.echo("Waiting to see if the Plot and watch variable buttons becomes visible"); + test.assertDoesntExist('button#plot', "Plot variables icon correctly invisible"); + test.assertDoesntExist('button#watch', "Watch button correctly hidden"); + this.echo("Variables button are hidden correctly"); + if(testSelect){ + testSelection(test, selectionName,selectColorVarName); + } + }); + }); + } + }); + }); + }); +} + +function testCameraControls(test, expectedCameraPosition){ + casper.then(function(){ + casper.echo("------Zoom-------") + casper.repeat(zoomClicks, function() { + this.thenClick("button#zoomInBtn", function() {}); + }); + });//zoom in + casper.then(function(){ + resetCameraTest(test, expectedCameraPosition); + });//reset home camera position + casper.then(function(){ + casper.echo("------Pan-------"); + casper.repeat(panClicks, function() { + this.thenClick("button#panRightBtn", function(){}); + }); + });//pan right test + casper.then(function(){ + resetCameraTest(test, expectedCameraPosition); + });//reset home position + casper.then(function(){ + casper.echo("------Rotate-------"); + casper.repeat(rotateClicks, function() { + this.thenClick("button#rotateRightBtn", function(){}); + }); + });//rotate test + casper.then(function(){ + resetCameraTest(test, expectedCameraPosition); + });//reset home +} \ No newline at end of file diff --git a/src/main/webapp/js/pages/tests/casperjs/README.md b/src/main/webapp/js/pages/tests/casperjs/README.md index fe7047c28..13400dcbd 100644 --- a/src/main/webapp/js/pages/tests/casperjs/README.md +++ b/src/main/webapp/js/pages/tests/casperjs/README.md @@ -6,6 +6,8 @@ to test that Casper is properly installed. Run `casperjs test UITests.js --engine=slimerjs` to run Geppetto UI Tests. +Run `casperjs test --includes=CoreTestsUtility.js CoreTests.js ` to run Core projects Tests + ##Prereqs * node.js From ff5fd75bb154a516356b5058490050656f90a33d Mon Sep 17 00:00:00 2001 From: johnidol Date: Tue, 9 May 2017 15:35:38 +0100 Subject: [PATCH 189/339] add open with filter method to control panel API --- .../interface/controlPanel/controlpanel.js | 94 ++++++++++++++----- 1 file changed, 73 insertions(+), 21 deletions(-) diff --git a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js index 848570745..b402ec367 100644 --- a/src/main/webapp/js/components/interface/controlPanel/controlpanel.js +++ b/src/main/webapp/js/components/interface/controlPanel/controlpanel.js @@ -537,6 +537,17 @@ define(function (require) { var FilterComponent = React.createClass({ + optionsMap: { + VISUAL_INSTANCES: ['visualInstancesFilterBtn'], + ACTIVE_STATE_VARIABLES: ['stateVariablesFilterBtn', 'activeExperimentFilterBtn'], + ACTIVE_RECORDED_STATE_VARIABLES: ['stateVariablesFilterBtn', 'activeExperimentFilterBtn', 'recordedFilterBtn'], + ANY_EXPERIMENT_RECORDED_STATE_VARIABLES: ['stateVariablesFilterBtn', 'anyExperimentFilterBtn'], + ANY_PROJECT_GLOBAL_STATE_VARIABLES: ['stateVariablesFilterBtn', 'anyProjectFilterBtn'], + ACTIVE_PARAMETERS: ['parametersFilterBtn', 'activeExperimentFilterBtn'], + ANY_EXPERIMENT_PARAMETERS: ['parametersFilterBtn', 'anyExperimentFilterBtn'], + ANY_PROJECT_PARAMETERS: ['parametersFilterBtn', 'anyProjectFilterBtn'] + }, + getInitialState: function () { return { visualFilterEnabled: true, @@ -616,9 +627,16 @@ define(function (require) { componentDidMount: function () { var that = this; GEPPETTO.on(GEPPETTO.Events.Control_panel_open, function () { - // when control panel is open and we are using the filter component - // if no other main component is toggled show visual instances - if (!that.state.stateVarsFilterToggled && !that.state.paramsFilterToggled) { + // when control panel is open check if we have filter coming down from props + if(that.props.filterOption != undefined){ + var buttonsToToggle = that.optionsMap[that.props.filterOption]; + if(buttonsToToggle != undefined){ + for(var i=0; i + ); } From 8711e01a71298c0449d86ef961a1f6ec2859c1c5 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 9 May 2017 16:11:21 +0100 Subject: [PATCH 190/339] add base ref so we can work with multiple subfolders url --- src/main/webapp/js/pages/geppetto/geppetto.ejs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/pages/geppetto/geppetto.ejs b/src/main/webapp/js/pages/geppetto/geppetto.ejs index 72a84e951..4e877180a 100644 --- a/src/main/webapp/js/pages/geppetto/geppetto.ejs +++ b/src/main/webapp/js/pages/geppetto/geppetto.ejs @@ -4,6 +4,8 @@ geppetto + + @@ -12,7 +14,6 @@ -