diff --git a/client/models/model.js b/client/models/model.js index b8b59ab441..139c9647a5 100644 --- a/client/models/model.js +++ b/client/models/model.js @@ -91,12 +91,12 @@ module.exports = Model.extend({ this.rules.on('add change remove', this.updateValid, this); }, validateModel: function () { - if(!this.species.validateCollection()) return false; + if(!this.species.validateCollection(this.is_spatial)) return false; if(!this.parameters.validateCollection()) return false; if(!this.reactions.validateCollection()) return false; if(!this.eventsCollection.validateCollection()) return false; if(!this.rules.validateCollection()) return false; - if(this.reactions.length <= 0 && this.eventsCollection.length <= 0 && this.rules.length <= 0) { + if(!this.is_spatial && this.reactions.length <= 0 && this.eventsCollection.length <= 0 && this.rules.length <= 0) { this.error = {"type":"process"} return false; } diff --git a/client/models/species.js b/client/models/species.js index 5ba9377b71..b9ee6dc508 100644 --- a/client/models/species.js +++ b/client/models/species.js @@ -55,8 +55,8 @@ module.exports = Collection.extend({ this.remove(specie); this.parent.updateValid() }, - validateCollection: function () { - if(this.length <= 0) { + validateCollection: function (isSpatial) { + if(this.length <= 0 && !isSpatial) { this.parent.error = {'type':'species'} return false; } diff --git a/client/models/timespan-settings.js b/client/models/timespan-settings.js index 98ab1d7879..b467a2b125 100644 --- a/client/models/timespan-settings.js +++ b/client/models/timespan-settings.js @@ -22,7 +22,8 @@ var State = require('ampersand-state'); module.exports = State.extend({ props: { endSim: 'any', - timeStep: 'any' + timeStep: 'any', + timestepSize: 'number' }, initialize: function (attrs, options) { State.prototype.initialize.apply(this, arguments) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index ce70b0177e..b3b8160138 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -23,23 +23,19 @@ let path = require('path'); var app = require('../app'); var modals = require('../modals'); var tests = require('../views/tests'); +let Tooltips = require("../tooltips"); //views var PageView = require('../pages/base'); var InputView = require('../views/input'); var DomainViewer = require('../views/domain-viewer'); var SpeciesEditorView = require('../views/species-editor'); -var SpeciesViewer = require('../views/species-viewer'); var InitialConditionsEditorView = require('../views/initial-conditions-editor'); var InitialConditionsViewer = require('../views/initial-conditions-viewer'); var ParametersEditorView = require('../views/parameters-editor'); -var ParameterViewer = require('../views/parameters-viewer'); var ParticleViewer = require('../views/view-particle'); var ReactionsEditorView = require('../views/reactions-editor'); -var ReactionsViewer = require('../views/reactions-viewer'); var EventsEditorView = require('../views/events-editor'); -var EventsViewer = require('../views/events-viewer'); var RulesEditorView = require('../views/rules-editor'); -var RulesViewer = require('../views/rules-viewer'); var SBMLComponentView = require('../views/sbml-component-editor'); var TimespanSettingsView = require('../views/timespan-settings'); var ModelStateButtonsView = require('../views/model-state-buttons'); @@ -55,9 +51,13 @@ import initPage from './page.js'; let ModelEditor = PageView.extend({ template: template, events: { + 'change [data-hook=all-continuous]' : 'setDefaultMode', + 'change [data-hook=all-discrete]' : 'setDefaultMode', + 'change [data-hook=advanced]' : 'setDefaultMode', 'click [data-hook=edit-model-help]' : function () { let modal = $(modals.operationInfoModalHtml('model-editor')).modal(); }, + 'change [data-hook=edit-volume]' : 'updateVolumeViewer', 'click [data-hook=collapse-me-advanced-section]' : 'changeCollapseButtonText', 'click [data-hook=project-breadcrumb-link]' : 'handleProjectBreadcrumbClick', 'click [data-hook=toggle-preview-plot]' : 'togglePreviewPlot', @@ -67,6 +67,7 @@ let ModelEditor = PageView.extend({ }, initialize: function (attrs, options) { PageView.prototype.initialize.apply(this, arguments); + this.tooltips = Tooltips.modelEditor var self = this; let urlParams = new URLSearchParams(window.location.search) var directory = urlParams.get('path'); @@ -219,6 +220,12 @@ let ModelEditor = PageView.extend({ } }, renderSubviews: function () { + if(this.model.defaultMode === "" && !this.model.is_spatial){ + this.getInitialDefaultMode(); + }else{ + let dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'} + $(this.queryByHook(dataHooks[this.model.defaultMode])).prop('checked', true) + } this.modelSettings = new TimespanSettingsView({ parent: this, model: this.model.modelSettings, @@ -232,6 +239,7 @@ let ModelEditor = PageView.extend({ app.registerRenderSubview(this, this.modelSettings, 'model-settings-container'); app.registerRenderSubview(this, this.modelStateButtons, 'model-state-buttons-container'); if(this.model.is_spatial) { + $(this.queryByHook("advaced-model-mode")).css("display", "none"); $(this.queryByHook("model-editor-advanced-container")).css("display", "none"); $(this.queryByHook("spatial-beta-message")).css("display", "block"); this.renderDomainViewer(); @@ -243,8 +251,7 @@ let ModelEditor = PageView.extend({ this.renderRulesView(); if(this.model.functionDefinitions.length) { var sbmlComponentView = new SBMLComponentView({ - functionDefinitions: this.model.functionDefinitions, - viewModel: false + functionDefinitions: this.model.functionDefinitions }); app.registerRenderSubview(this, sbmlComponentView, 'sbml-component-container'); } @@ -288,15 +295,15 @@ let ModelEditor = PageView.extend({ app.registerRenderSubview(this, this.domainViewer, 'domain-viewer-container'); } }, - renderSpeciesView: function (mode="edit") { + renderSpeciesView: function () { if(this.speciesEditor) { this.speciesEditor.remove() } - if(mode === "edit") { - this.speciesEditor = new SpeciesEditorView({collection: this.model.species}); - }else{ - this.speciesEditor = new SpeciesViewer({collection: this.model.species}); - } + this.speciesEditor = new SpeciesEditorView({ + collection: this.model.species, + spatial: this.model.is_spatial, + defaultMode: this.model.defaultMode + }); app.registerRenderSubview(this, this.speciesEditor, 'species-editor-container'); }, renderInitialConditions: function (mode="edit", opened=false) { @@ -315,48 +322,32 @@ let ModelEditor = PageView.extend({ } app.registerRenderSubview(this, this.initialConditionsEditor, 'initial-conditions-editor-container'); }, - renderParametersView: function (mode="edit", opened=false) { + renderParametersView: function () { if(this.parametersEditor) { this.parametersEditor.remove() } - if(mode === "edit") { - this.parametersEditor = new ParametersEditorView({collection: this.model.parameters, opened: opened}); - }else{ - this.parametersEditor = new ParameterViewer({collection: this.model.parameters}); - } + this.parametersEditor = new ParametersEditorView({collection: this.model.parameters}); app.registerRenderSubview(this, this.parametersEditor, 'parameters-editor-container'); }, - renderReactionsView: function (mode="edit", opened=false) { + renderReactionsView: function () { if(this.reactionsEditor) { this.reactionsEditor.remove() } - if(mode === "edit") { - this.reactionsEditor = new ReactionsEditorView({collection: this.model.reactions, opened: opened}); - }else{ - this.reactionsEditor = new ReactionsViewer({collection: this.model.reactions}); - } + this.reactionsEditor = new ReactionsEditorView({collection: this.model.reactions}); app.registerRenderSubview(this, this.reactionsEditor, 'reactions-editor-container'); }, - renderEventsView: function (mode="edit", opened=false) { + renderEventsView: function () { if(this.eventsEditor){ this.eventsEditor.remove(); } - if(mode === "edit") { - this.eventsEditor = new EventsEditorView({collection: this.model.eventsCollection, opened: opened}); - }else{ - this.eventsEditor = new EventsViewer({collection: this.model.eventsCollection}); - } + this.eventsEditor = new EventsEditorView({collection: this.model.eventsCollection}); app.registerRenderSubview(this, this.eventsEditor, 'events-editor-container'); }, - renderRulesView: function (mode="edit", opened=false) { + renderRulesView: function () { if(this.rulesEditor){ this.rulesEditor.remove(); } - if(mode === "edit") { - this.rulesEditor = new RulesEditorView({collection: this.model.rules, opened: opened}); - }else{ - this.rulesEditor = new RulesViewer({collection: this.model.rules}) - } + this.rulesEditor = new RulesEditorView({collection: this.model.rules}); app.registerRenderSubview(this, this.rulesEditor, 'rules-editor-container'); }, renderSystemVolumeView: function () { @@ -373,10 +364,11 @@ let ModelEditor = PageView.extend({ valueType: 'number', value: this.model.volume, }); - app.registerRenderSubview(this, this.systemVolumeView, 'volume') + app.registerRenderSubview(this, this.systemVolumeView, 'edit-volume') if(this.model.defaultMode === "continuous") { $(this.queryByHook("system-volume-container")).collapse("hide") } + $(this.queryByHook("view-volume")).html("Volume: " + this.model.volume) }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); @@ -430,6 +422,66 @@ let ModelEditor = PageView.extend({ clickDownloadPNGButton: function (e) { let pngButton = $('div[data-hook=preview-plot-container] a[data-title*="Download plot as a png"]')[0] pngButton.click() + }, + getInitialDefaultMode: function () { + var self = this; + if(document.querySelector('#defaultModeModal')) { + document.querySelector('#defaultModeModal').remove(); + } + let modal = $(modals.renderDefaultModeModalHtml()).modal(); + let continuous = document.querySelector('#defaultModeModal .concentration-btn'); + let discrete = document.querySelector('#defaultModeModal .population-btn'); + let dynamic = document.querySelector('#defaultModeModal .hybrid-btn'); + continuous.addEventListener('click', function (e) { + self.setInitialDefaultMode(modal, "continuous"); + }); + discrete.addEventListener('click', function (e) { + self.setInitialDefaultMode(modal, "discrete"); + }); + dynamic.addEventListener('click', function (e) { + self.setInitialDefaultMode(modal, "dynamic"); + }); + }, + setAllSpeciesModes: function (prevMode) { + let self = this; + this.model.species.forEach(function (specie) { + specie.mode = self.model.defaultMode; + self.model.species.trigger('update-species', specie.compID, specie, false, true); + }); + let switchToDynamic = (!Boolean(prevMode) || prevMode !== "dynamic") && this.model.defaultMode === "dynamic"; + let switchFromDynamic = Boolean(prevMode) && prevMode === "dynamic" && this.model.defaultMode !== "dynamic"; + if(switchToDynamic || switchFromDynamic) { + this.speciesEditor.renderEditSpeciesView(); + this.speciesEditor.renderViewSpeciesView(); + } + }, + setDefaultMode: function (e) { + let prevMode = this.model.defaultMode; + this.model.defaultMode = e.target.dataset.name; + this.speciesEditor.defaultMode = e.target.dataset.name; + this.setAllSpeciesModes(prevMode); + this.toggleVolumeContainer(); + }, + setInitialDefaultMode: function (modal, mode) { + var dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'}; + modal.modal('hide'); + $(this.queryByHook(dataHooks[mode])).prop('checked', true); + this.model.defaultMode = mode; + this.speciesEditor.defaultMode = mode; + this.setAllSpeciesModes(); + this.toggleVolumeContainer(); + }, + toggleVolumeContainer: function () { + if(!this.model.is_spatial) { + if(this.model.defaultMode === "continuous") { + $(this.queryByHook("system-volume-container")).collapse("hide"); + }else{ + $(this.queryByHook("system-volume-container")).collapse("show"); + } + } + }, + updateVolumeViewer: function (e) { + $(this.queryByHook("view-volume")).html("Volume: " + this.model.volume) } }); diff --git a/client/styles/styles.css b/client/styles/styles.css index 29403e2122..e5b21c765d 100644 --- a/client/styles/styles.css +++ b/client/styles/styles.css @@ -293,6 +293,11 @@ input[type="file"]::-ms-browse { display: inline-block; } +.reaction-list-summary { + width: auto !important; + text-align: center; +} + .container-part { max-width: none; } diff --git a/client/templates/includes/editAdvancedSpecie.pug b/client/templates/includes/editAdvancedSpecie.pug deleted file mode 100644 index e341430071..0000000000 --- a/client/templates/includes/editAdvancedSpecie.pug +++ /dev/null @@ -1,19 +0,0 @@ -tr - - td.name: div(data-hook="specie-name")=this.model.name - - td: div(data-hook="specie-mode") - - td - div.switching - input(type="radio", name=this.model.name + "-switch-method", data-hook="switching-tol") - | Switching Tolerance - - div.switching - input(type="radio", name=this.model.name + "-switch-method", data-hook="switching-min") - | Minimum Value for Switching (number of molecules) - - td - div(data-hook="switching-tolerance") - - div(data-hook="switching-threshold") \ No newline at end of file diff --git a/client/templates/includes/editEventAssignment.pug b/client/templates/includes/editEventAssignment.pug index 00c9294936..ecc8b48dda 100644 --- a/client/templates/includes/editEventAssignment.pug +++ b/client/templates/includes/editEventAssignment.pug @@ -1,7 +1,18 @@ -tr +div.mx-1 - td: div(data-hook="event-assignment-variable") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td: div(data-hook="event-assignment-Expression") + div.row - td: button.btn.btn-outline-secondary(data-hook="remove") X \ No newline at end of file + div.col-sm-3 + + div.pl-2(data-hook="event-assignment-variable") + + div.col-sm-7 + + div(data-hook="event-assignment-expression") + + div.col-sm-2 + + button.btn.btn-outline-secondary(data-hook="remove") X diff --git a/client/templates/includes/editFunctionDefinition.pug b/client/templates/includes/editFunctionDefinition.pug index 1e9f7f5a54..74866ea363 100644 --- a/client/templates/includes/editFunctionDefinition.pug +++ b/client/templates/includes/editFunctionDefinition.pug @@ -1,13 +1,20 @@ -tr +div.mx-1 - td=this.model.signature + if(this.model.collection.indexOf(this.model) !== 0) + hr - th + div.row - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.col-sm-8 + + div.pl-2=this.model.signature + + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") - if(!this.parent.viewMode) button.btn.btn-outline-secondary.btn-sm(data-hook="edit-annotation-btn") Edit - - if(!this.parent.viewMode) - td: button.btn.btn-outline-secondary(data-hook="remove") X \ No newline at end of file + + div.col-sm-2 + + button.btn.btn-outline-secondary(data-hook="remove") X diff --git a/client/templates/includes/editParameter.pug b/client/templates/includes/editParameter.pug new file mode 100644 index 0000000000..87c84fa80e --- /dev/null +++ b/client/templates/includes/editParameter.pug @@ -0,0 +1,24 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-3 + + div.pl-2(data-hook="input-name-container") + + div.col-sm-5 + + div(data-hook="input-value-container") + + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-sm-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/editReactionVar.pug b/client/templates/includes/editReactionVar.pug deleted file mode 100644 index 8d416852be..0000000000 --- a/client/templates/includes/editReactionVar.pug +++ /dev/null @@ -1,12 +0,0 @@ -tr - td.name: div(data-hook="input-name-container") - - td: div(data-hook="input-value-container") - - td - - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") - - button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit - - td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file diff --git a/client/templates/includes/editRule.pug b/client/templates/includes/editRule.pug index 30c7547445..cdbb34eaeb 100644 --- a/client/templates/includes/editRule.pug +++ b/client/templates/includes/editRule.pug @@ -1,17 +1,32 @@ -tr +div.mx-1 - td.name: div(data-hook="rule-name") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td: div(data-hook="rule-type") + div.row - td: div(data-hook="rule-variable") + div.col-sm-2 - td: div(data-hook="rule-expression") + div.pl-2(data-hook="rule-name") - td + div.col-sm-2 - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div(data-hook="rule-type") - button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + div.col-sm-2 - td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file + div(data-hook="rule-variable") + + div.col-sm-2 + + div(data-hook="rule-expression") + + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-sm-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/editSpatialSpecie.pug b/client/templates/includes/editSpatialSpecie.pug deleted file mode 100644 index 44aefb3757..0000000000 --- a/client/templates/includes/editSpatialSpecie.pug +++ /dev/null @@ -1,9 +0,0 @@ -tr - - td.name: div(data-hook="input-name-container") - - td: div(data-hook="input-diffusion-coeff-container") - - td: div.row(data-hook="species-types") - - td: button.btn.btn-outline-secondary(data-hook="remove") X \ No newline at end of file diff --git a/client/templates/includes/editSpatialSpecies.pug b/client/templates/includes/editSpatialSpecies.pug new file mode 100644 index 0000000000..ae78da2e75 --- /dev/null +++ b/client/templates/includes/editSpatialSpecies.pug @@ -0,0 +1,28 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-2 + + div.pl-2(data-hook="input-name-container") + + div.col-sm-2 + + div(data-hook="input-value-container") + + div.col-sm-4 + + div.row(data-hook="species-types") + + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-sm-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/editSpecies.pug b/client/templates/includes/editSpecies.pug new file mode 100644 index 0000000000..3c1ad72082 --- /dev/null +++ b/client/templates/includes/editSpecies.pug @@ -0,0 +1,70 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-3 + + div.pl-2(data-hook="input-name-container") + + div.col-sm-5 + + div(data-hook="input-value-container") + + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-sm-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X + + div.mx-1.pl-2(data-hook="advanced-species") + + div.align-items-baseline + + h6.inline Advanced + + button.btn.btn-outline-collapse.mb-2(data-toggle="collapse" data-target="#collapse-species-advanced" + this.model.collection.indexOf(this.model) data-hook="collapse-advanced") + + + div.collapse(id="collapse-species-advanced" + this.model.collection.indexOf(this.model)) + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + h6.inline Mode + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.mode) + + div.col-sm-9 + + h6.inline Switching Settings (Hybrid Only) + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.switchValue) + + div.row.mt-3 + + div.col-sm-3(data-hook="specie-mode") + + div.col-sm-4 + + div.switching + input(type="radio", name=this.model.name + "-switch-method", data-hook="switching-tol") + | Switching Tolerance + + div.switching + input(type="radio", name=this.model.name + "-switch-method", data-hook="switching-min") + | Minimum Value for Switching (number of molecules) + + div.col-sm-5 + + div(data-hook="switching-tolerance") + + div(data-hook="switching-threshold") diff --git a/client/templates/includes/eventAssignmentsEditor.pug b/client/templates/includes/eventAssignmentsEditor.pug index 022cef0cd7..f32e4cf6da 100644 --- a/client/templates/includes/eventAssignmentsEditor.pug +++ b/client/templates/includes/eventAssignmentsEditor.pug @@ -1,26 +1,55 @@ -div +div#event-assignments-editor - table.table + div(data-hook="event-assignments-header") - thead + h5.inline.mr-3 - tr + div + + div.inline Assignments - th(scope="col") - div - div.inline Target + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.assignments) - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.variable) + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-event-assignments" data-hook="collapse-assignments") - - th(scope="col") - div - div.inline Expression + div - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignmentExpression) + div.collapse.show.tab-content(id="collapse-event-assignments" data-hook="event-assignments-list-container") - th(scope="col") Remove + div.tab-pane.active(id="edit-event-assignments" data-hook="edit-event-assignments") - tbody(data-hook="event-assignments-container") + hr - button.btn.btn-outline-primary.box-shadow(data-hook="add-event-assignment", type="button") - | Add Assignment \ No newline at end of file + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + div.inline Target + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.variable) + + div.col-sm-7 + + h6.inline Expression + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.assignmentExpression) + + div.col-sm-2 + + h6 Remove + + div.my-3(data-hook="edit-event-assignments-container") + + button.btn.btn-outline-primary.box-shadow(data-hook="add-event-assignment", type="button") Add Assignment + + div.tab-pane(id="view-event-assignments" data-hook="view-event-assignments") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 Target + + div.col-sm-7 Expression + + div.my-3(data-hook="view-event-assignments-container") diff --git a/client/templates/includes/eventAssignmentsViewer.pug b/client/templates/includes/eventAssignmentsViewer.pug deleted file mode 100644 index 4a3aa846c8..0000000000 --- a/client/templates/includes/eventAssignmentsViewer.pug +++ /dev/null @@ -1,11 +0,0 @@ -table.table - - thead - - tr - - th(scope="col") Target - - th(scope="col") Expression - - tbody(data-hook="view-event-assignments-list") \ No newline at end of file diff --git a/client/templates/includes/eventDetails.pug b/client/templates/includes/eventDetails.pug index 6ccdb91b55..c473f51381 100644 --- a/client/templates/includes/eventDetails.pug +++ b/client/templates/includes/eventDetails.pug @@ -1,14 +1,18 @@ div(data-hook="event-details") - div + div.row - span(for="event-trigger-expression") Trigger Expression: + div + + span(for="event-trigger-expression") Trigger Expression: div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.triggerExpression) - div.col-md-8.inline(id="event-trigger-expression" data-hook="event-trigger-expression") + div.col-md-8(id="event-trigger-expression" data-hook="event-trigger-expression") - div + div(data-hook="event-assignments") + + div.my-1 h6.inline Advanced button.btn.btn-outline-collapse(data-toggle='collapse', data-target='#advanced-events', data-hook='advanced-event-button') + @@ -27,7 +31,7 @@ div(data-hook="event-details") div.col-md-6 - span(for="event-trigger-expression") Prioirty: + span(for="event-trigger-expression") Priority: div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.priority) @@ -61,18 +65,10 @@ div(data-hook="event-details") div.horizontal-space.inline - input(type="radio", name="use-values-from" data-hook="trigger-time" data-name="trigger") + input(type="radio", name="edit-use-values-from" data-hook="edit-trigger-time" data-name="trigger") | Trigger Time div.horizontal-space.inline - input(type="radio", name="use-values-from" data-hook="assignment-time" data-name="assignment") + input(type="radio", name="edit-use-values-from" data-hook="edit-assignment-time" data-name="assignment") | Assignment Time - - h5.inline - div - div.inline Assignments - - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.assignments) - - div(data-hook="event-assignments") \ No newline at end of file diff --git a/client/templates/includes/eventListings.pug b/client/templates/includes/eventListings.pug index c1d7a49696..f67a2ecc19 100644 --- a/client/templates/includes/eventListings.pug +++ b/client/templates/includes/eventListings.pug @@ -1,13 +1,22 @@ -tr +div.mx-1 - td: input(type="radio", data-hook="select", name="event-select") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td.name: div(data-hook="event-name-container") + div.row.align-items-baseline - td + div.col-md-2 - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.pl-3: input(type="radio", data-hook="select", name="event-select") - button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + div.col-md-5(data-hook="event-name-container") - td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file + div.col-md-3 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-md-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/eventsEditor.pug b/client/templates/includes/eventsEditor.pug index 41f5845440..0828d3d9ee 100644 --- a/client/templates/includes/eventsEditor.pug +++ b/client/templates/includes/eventsEditor.pug @@ -1,45 +1,90 @@ -div#events.card.card-body +div#events.card - div + div.card-header.pb-0 - h3.inline Events + h3.inline.mr-3 Events + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="events-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="events-edit-tab" data-toggle="tab" href="#edit-events") Edit + + li.nav-item + + a.nav-link.tab(data-hook="events-view-tab" data-toggle="tab" href="#view-events") View button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#events-container", data-hook="collapse") + - div.collapse(id="events-container", data-hook="events") + div.card-body + + p.mb-0 + | A discontinuous action triggered by the state of the system. + + div.collapse.tab-content(id="events-container", data-hook="events") + + div.tab-pane.active(id="edit-events" data-hook="edit-events") + + div.row.mb-3.align-items-baseline + + div.col-md-6.container-part + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-md-2 + + h6 Edit + + div.col-md-5 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-md-3 + + h6.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-md-2 + + h6 Remove + + div.mt-3(data-hook="edit-event-listing-container") + + div.col-md-6.container-part(data-hook="event-details-container") - div.row + button.btn.btn-outline-primary.box-shadow(data-hook="add-event") Add Event - div.col-md-6.container-part - p - | A discontinuous action triggered by the state of the system. + div.tab-pane(id="view-events" data-hook="view-events") - table.table + hr - thead + div.mx-1.row.head - tr + div.col-sm-2 - th(scope="col") Edit + h6 Name - th(scope="col") - div - div.inline Name + div.col-sm-2 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + h6 Trigger - th(scope="col") - div - div.inline Annotation + div.col-sm-4 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + h6 Assignments - th(scope="col") Remove + div.col-sm-2 - tbody(data-hook="event-listing-container") + h6 Advanced - div.col-md-6.container-part(data-hook="event-details-container") + div.col-sm-2(data-hook="events-annotation-header") - button.btn.btn-outline-primary.box-shadow(data-hook="add-event") Add Event + h6 Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-events") Save Events + div.mt-3(data-hook="view-event-listing-container") diff --git a/client/templates/includes/eventsViewer.pug b/client/templates/includes/eventsViewer.pug deleted file mode 100644 index bf9bed0f61..0000000000 --- a/client/templates/includes/eventsViewer.pug +++ /dev/null @@ -1,30 +0,0 @@ -div#events-viewer.card.card-body - - div - - h3.inline Events - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-model-events-viewer", data-hook="collapse") - - - div.row.collapse(class="show", id="collapse-model-events-viewer") - - table.table - - thead - - tr - - th(scope="col") Name - - th(scope="col") Trigger - - th(scope="col") Assignments - - th(scope="col") Advanced - - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="view-events-container") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-events") Edit Events diff --git a/client/templates/includes/parametersEditor.pug b/client/templates/includes/parametersEditor.pug index 707ada2a8b..5d2c5af0d6 100644 --- a/client/templates/includes/parametersEditor.pug +++ b/client/templates/includes/parametersEditor.pug @@ -1,43 +1,73 @@ -div#parameters-editor.card.card-body +div#parameters-editor.card - div + div.card-header.pb-0 - h3.inline Parameters - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-parameters", data-hook="collapse") + + h3.inline.mr-3 Parameters - div.collapse(id="collapse-parameters" data-hook="parameters-list-container") - p + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="parameter-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="parameters-edit-tab" data-toggle="tab" href="#edit-parameters") Edit + + li.nav-item + + a.nav-link.tab(data-hook="parameters-view-tab" data-toggle="tab" href="#view-parameters") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-parameters" data-hook="collapse") + + + div.card-body + p.mb-0 | Constant values that represents an inherent properties of the system. Used to set the rate of a Reaction. - table.table - thead - tr - th(scope="col") - div - div.inline Name + div.collapse.tab-content(id="collapse-parameters" data-hook="parameters-list-container") + + div.tab-pane.active(id="edit-parameters" data-hook="edit-parameters") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-sm-5 + + h6.inline Value + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.expression) + + div.col-sm-2 + + h6.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-sm-2 + + h6.inline Remove + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + div.my-3(data-hook="edit-parameter-list") - th(scope="col") - div - div.inline Value + button.btn.btn-outline-primary.box-shadow(data-hook="add-parameter") Add Parameter - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.expression) + div.tab-pane(id="view-parameters" data-hook="view-parameters") - th(scope="col") - div - div.inline Annotation + hr - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.mx-1.row.head.align-items-baseline - th(scope="col") - div - div.inline Remove + div.col-sm-3: h6 Name - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) - - tbody(data-hook="parameter-list") + div.col-sm-7: h6 Value - button.btn.btn-outline-primary.box-shadow(data-hook="add-parameter") Add Parameter + div.col-sm-2(data-hook="parameters-annotation-header"): h6 Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-parameters") Save Parameters + div.mt-3(data-hook="view-parameter-list") diff --git a/client/templates/includes/parametersViewer.pug b/client/templates/includes/parametersViewer.pug deleted file mode 100644 index b579141fb2..0000000000 --- a/client/templates/includes/parametersViewer.pug +++ /dev/null @@ -1,21 +0,0 @@ -div#parameters-viewer.card.card-body - - div - - h3.inline Parameters - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-parameters", data-hook="collapse") - - - div.collapse(class="show", id="collapse-parameters") - - table.table - thead - tr - th.col-md-3-view(scope="col") Name - th.col-md-9-view(scope="col") Value - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="parameter-list") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-parameters") Edit Parameters diff --git a/client/templates/includes/reactionListing.pug b/client/templates/includes/reactionListing.pug index bed0ba53e5..3b7ea5cae3 100644 --- a/client/templates/includes/reactionListing.pug +++ b/client/templates/includes/reactionListing.pug @@ -1,14 +1,24 @@ -tr - td: input(type="radio", data-hook="select", name="reaction-select") - - td.name: div(data-hook="input-name-container") - - td: div(data-hook="summary", style="width: auto !important") - - td +div.mx-1 - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + if(this.model.collection.indexOf(this.model) !== 0) + hr - button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + div.row.align-items-baseline - td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file + div.col-md-2 + + div.pl-3: input(type="radio" data-hook="select" name="reaction-select") + + div.col-md-2(data-hook="input-name-container") + + div.col-md-3.reaction-list-summary(data-hook="summary") + + div.col-md-3 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-md-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/reactionsEditor.pug b/client/templates/includes/reactionsEditor.pug index b27791913b..c3b6e5b7ef 100644 --- a/client/templates/includes/reactionsEditor.pug +++ b/client/templates/includes/reactionsEditor.pug @@ -1,78 +1,117 @@ -div#reactions-editor.card.card-body +div#reactions-editor.card - div + div.card-header.pb-0 - h3.inline - div - div.inline Reactions + h3.inline.mr-3 Reactions - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-reaction", data-hook="collapse") + + div.inline.mr-3 - div.collapse(id="collapse-reaction" data-hook="reactions-list-container") + ul.nav.nav-tabs.card-header-tabs(id="reactions-tabs") - p + li.nav-item + + a.nav-link.tab.active(data-hook="reactions-edit-tab" data-toggle="tab" href="#edit-reactions") Edit + + li.nav-item + + a.nav-link.tab(data-hook="reactions-view-tab" data-toggle="tab" href="#view-reactions") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-reaction" data-hook="collapse") + + + div.card-body + + p.mb-0 | A process that transforms a set of Variables (reactants) into a set of Variables (products) at a rate set by a Parameter. Select from the given reaction templates, or use the custom types. The symbol represents null or empty set. - div.row - div.col-md-7.container-part: table.table - thead: tr - th(scope="col") Edit - th(scope="col") - div - div.inline Name + div.collapse.tab-content(id="collapse-reaction" data-hook="reactions-list-container") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) - - th(scope="col") Summary - th(scope="col") - div - div.inline Annotation + div.tab-pane.active(id="edit-reactions" data-hook="edit-reactions") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.row.mb-3 + + div.col-md-7.container-part + + hr - th(scope="col") Remove + div.mx-1.row.head.align-items-baseline + + div.col-md-2: h6.align-bottom Edit + + div.col-md-2 - tbody(data-hook="reaction-list") + h6.inline Name - div.col-md-5.container-part(data-hook="reaction-details-container") + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) - div(data-hook="massaction-message"): p.text-info To add a mass action reaction the model must have at least one parameter - - div(data-hook="process-component-error"): p.text-danger A model must have at least one reaction, event, or rule - - div.dropdown.inline - - button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( - data-hook="add-reaction-full", - data-toggle="dropdown", - aria-haspopup="true", - aria-expanded="false", - type="button" - ) Add Reaction - - ul.dropdown-menu(aria-labelledby="addReactionBtn") - li.dropdown-item(data-hook="creation") Creation Reaction - li.dropdown-item(data-hook="destruction") Destruction Reaction - li.dropdown-item(data-hook="change") Transformation Reaction - li.dropdown-item(data-hook="dimerization") Dimerization Reaction - li.dropdown-item(data-hook="merge") Merge Reaction - li.dropdown-item(data-hook="split") Split Reaction - li.dropdown-item(data-hook="four") Four Reaction - li.dropdown-divider - li.dropdown-item(data-hook="custom-massaction") Custom mass action - li.dropdown-item(data-hook="custom-propensity") Custom propensity - - div.dropdown.inline - - button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( - data-hook="add-reaction-partial", - data-toggle="dropdown", - aria-haspopup="true", - aria-expanded="false", - type="button" - ) Add Reaction - - ul.dropdown-menu(aria-labelledby="addReactionBtn") - li.dropdown-item(data-hook="custom-propensity") Custom propensity - - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-reactions") Save Reactions + div.col-md-3: h6 Summary + + div.col-md-3 + + div.inline + + h6 Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-md-2: h6 Remove + + div.mt-3(data-hook="edit-reaction-list") + + div.col-md-5.container-part(data-hook="reaction-details-container") + + div(data-hook="massaction-message"): p.text-info To add a mass action reaction the model must have at least one parameter + + div.component-valid(data-hook="process-component-error"): p.text-danger A model must have at least one reaction, event, or rule + + div.dropdown.inline + + button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( + data-hook="add-reaction-full", + data-toggle="dropdown", + aria-haspopup="true", + aria-expanded="false", + type="button" + ) Add Reaction + + ul.dropdown-menu(aria-labelledby="addReactionBtn") + li.dropdown-item(data-hook="creation") Creation Reaction + li.dropdown-item(data-hook="destruction") Destruction Reaction + li.dropdown-item(data-hook="change") Transformation Reaction + li.dropdown-item(data-hook="dimerization") Dimerization Reaction + li.dropdown-item(data-hook="merge") Merge Reaction + li.dropdown-item(data-hook="split") Split Reaction + li.dropdown-item(data-hook="four") Four Reaction + li.dropdown-divider + li.dropdown-item(data-hook="custom-massaction") Custom mass action + li.dropdown-item(data-hook="custom-propensity") Custom propensity + + div.dropdown.inline + + button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( + data-hook="add-reaction-partial", + data-toggle="dropdown", + aria-haspopup="true", + aria-expanded="false", + type="button" + ) Add Reaction + + ul.dropdown-menu(aria-labelledby="addReactionBtn") + li.dropdown-item(data-hook="custom-propensity") Custom propensity + + div.tab-pane(id="view-reactions" data-hook="view-reactions") + + hr + + div.mx-1.row.head + + div.col-md-2: h6 Name + + div.col-md-3: h6 Summary + + div.col-md-3: h6 Rate/Propensity + + div.col-md-2(data-hook="reaction-types-header"): h6 Active in Types + + div.col-md-2(data-hook="reaction-annotation-header"): h6 Annotation + + div.mt-3(data-hook="view-reaction-list") diff --git a/client/templates/includes/reactionsViewer.pug b/client/templates/includes/reactionsViewer.pug deleted file mode 100644 index 2ad0a5bb15..0000000000 --- a/client/templates/includes/reactionsViewer.pug +++ /dev/null @@ -1,24 +0,0 @@ -div#reactions-viewer.card.card-body - - div - - h3.inline Reactions - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-reactions", data-hook="collapse") - - - div.collapse(class="show", id="collapse-reactions") - - table.table - thead - tr - th.col-md-3-view(scope="col") Name - th.col-md-3-view(scope="col") Summary - th.col-md-6-view(scope="col") Rate/Propensity - if this.collection.parent.is_spatial - th.col-md-3-view(scope="col") Active in Types - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="reaction-list") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-reactions") Edit Reactions diff --git a/client/templates/includes/ruleEditor.pug b/client/templates/includes/ruleEditor.pug index 7065291d4e..97cfd2c051 100644 --- a/client/templates/includes/ruleEditor.pug +++ b/client/templates/includes/ruleEditor.pug @@ -1,67 +1,109 @@ -div#rules-editor.card.card-body +div#rules-editor.card - div + div.card-header.pb-0 - h3.inline Rules + h3.inline.mr-3 Rules + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="rules-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="rules-edit-tab" data-toggle="tab" href="#edit-rules") Edit + + li.nav-item + + a.nav-link.tab(data-hook="rules-view-tab" data-toggle="tab" href="#view-rules") View button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-rules", data-hook="collapse") + - div.collapse(id="collapse-rules" data-hook="rules-list-container") - p + div.card-body + p.mb-0 | Equations that determine the value (assignment rule) or rates of change (rate rule) of Variables or Parameter. - table.table + div.collapse.tab-content(id="collapse-rules" data-hook="rules-list-container") + + div.tab-pane.active(id="edit-rules" data-hook="edit-rules") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-2 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-sm-2 + + h6.inline Type + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.type) + + div.col-sm-2 + + h6.inline Target + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.variable) + + div.col-sm-2 + + h6.inline Formula + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.expression) + + div.col-sm-2 + + h6.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-sm-2 + + h6 Remove - thead + div.my-3(data-hook="edit-rule-list-container") - tr + div.dropdown - th(scope="col") - div - div.inline Name + button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addRuleBtn( + data-hook="add-rule", + data-toggle="dropdown", + aria-haspopup="true", + aria-expanded="false", + type="button" + ) Add Rule - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + ul.dropdown-menu(aria-labelledby="addRuleBtn") + li.dropdown-item(data-hook="rate-rule" data-name="Rate Rule") Rate Rule + li.dropdown-item(data-hook="assignment-rule" data-name="Assignment Rule") Assignment Rule - th(scope="col") - div - div.inline Type + div.tab-pane(id="view-rules" data-hook="view-rules") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.type) + hr - th(scope="col") - div - div.inline Target + div.mx-1.row.head.align-items-baseline - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.variable) + div.col-sm-2 - th(scope="col") - div - div.inline Formula + h6.inline Name - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.expression) + div.col-sm-2 - th(scope="col") - div - div.inline Annotation + h6.inline Type - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.col-sm-2 - th(scope="col") Remove + h6.inline Target - tbody(data-hook="rule-list-container") + div.col-sm-4 - div.dropdown.inline + h6.inline Formula - button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addRuleBtn( - data-hook="add-rule", - data-toggle="dropdown", - aria-haspopup="true", - aria-expanded="false", - type="button" - ) Add Rule + div.col-sm-2(data-hook="rules-annotation-header") - ul.dropdown-menu(aria-labelledby="addRuleBtn") - li.dropdown-item(data-hook="rate-rule" data-name="Rate Rule") Rate Rule - li.dropdown-item(data-hook="assignment-rule" data-name="Assignment Rule") Assignment Rule + h6.inline Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-rules") Save Rules + div.mt-3(data-hook="view-rules-list-container") diff --git a/client/templates/includes/rulesViewer.pug b/client/templates/includes/rulesViewer.pug deleted file mode 100644 index 1451df8482..0000000000 --- a/client/templates/includes/rulesViewer.pug +++ /dev/null @@ -1,23 +0,0 @@ -div#rules-viewer.card.card-body - - div - - h3.inline Rules - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-rules", data-hook="collapse") - - - div.collapse(class="show", id="collapse-rules") - - table.table - thead - tr - th.col-md-3-view(scope="col") Name - th.col-md-3-view(scope="col") Type - th.col-md-3-view(scope="col") Target - th.col-md-3-view(scope="col") Formula - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="rules-list") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-rules") Edit Rules diff --git a/client/templates/includes/sbmlComponentEditor.pug b/client/templates/includes/sbmlComponentEditor.pug index 7d4ec4423d..84ac3d3fb5 100644 --- a/client/templates/includes/sbmlComponentEditor.pug +++ b/client/templates/includes/sbmlComponentEditor.pug @@ -1,34 +1,75 @@ -div#sbml-components.card.card-body +div#sbml-components.card - div + div.card-header.pb-0 h3.inline SBML Components + button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-sbml-component", data-hook="collapse") + div.collapse(id="collapse-sbml-component") - div + div.card-body + + div#function-definitions.card + + div.card-header.pb-0 + + h5.inline.mr-3 Function Definitions + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="function-definitions-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="function-definitions-edit-tab" data-toggle="tab" href="#edit-function-definitions") Edit + + li.nav-item + + a.nav-link.tab(data-hook="function-definitions-view-tab" data-toggle="tab" href="#view-function-definitions") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-function-definitions" data-hook="collapse-function-definitions") - + + div.card-body + p.mb-0 + | Object representation defining an evaluable function to be used during simulation. + + div.collapse.tab-content(class="show" id="collapse-function-definitions") + + div.tab-pane.active(id="edit-function-definitions" data-hook="edit-function-definitions") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-8 + + h6 Signature + + div.col-sm-2 + + h6.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-sm-2 + + h6 Remove + + div.my-3(data-hook="edit-function-definition-list") + + div.tab-pane(id="view-function-definitions" data-hook="view-function-definitions") - h5.inline Function Definitions - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-function-definitions", data-hook="collapse-function-definitions") - + hr - div.collapse(class="show", id="collapse-function-definitions") + div.mx-1.row.head.align-items-baseline - table.table + div.col-sm-10 - thead - - tr - - th(scope="col") Signature + h6 Signature - th(scope="col") - div - div.inline Annotation + div.col-sm-2(data-hook="function-definition-annotation-header") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + h6.inline Annotation - if(!this.viewMode) - th(scope="col") Remove - - tbody(data-hook="function-definition-list") \ No newline at end of file + div.my-3(data-hook="view-function-definition-list") diff --git a/client/templates/includes/spatialSpeciesEditor.pug b/client/templates/includes/spatialSpeciesEditor.pug index 4adc51dfcb..ae24b9fc88 100644 --- a/client/templates/includes/spatialSpeciesEditor.pug +++ b/client/templates/includes/spatialSpeciesEditor.pug @@ -1,45 +1,85 @@ -div#species-editor.card.card-body +div#species-editor.card - div + div.card-header.pb-0 - h3.inline Variables - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-species", data-hook="collapse") - + h3.inline.mr-3 Variables - div.collapse(class="show", id="collapse-species") + div.inline.mr-3 - p + ul.nav.nav-tabs.card-header-tabs(id="species-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="species-edit-tab" data-toggle="tab" href="#edit-species") Edit + + li.nav-item + + a.nav-link.tab(data-hook="species-view-tab" data-toggle="tab" href="#view-species") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-species" data-hook="collapse") - + + div.card-body + p.mb-0 | Time varying quantities of interest; e.g.: concentrations, populations, or counts of biochemical species, epidemological compartments, chemicals, proteins, or molecules. - table.table - thead - tr - th(scope="col") Name - th(scope="col") Diffusion Constant - th(scope="col") Restricted to Types: - th(scope="col") Remove - - tbody(data-hook="specie-list") + div.collapse.tab-content(class="show" id="collapse-species" data-hook="species-list-container") + + div.tab-pane.active(id="edit-species" data-hook="edit-species") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-2 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-sm-2 + + h6.inline Diffusion Constant + + div.col-sm-4 + + h6.inline Restricted to Types: + + div.col-sm-2 + + h6.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-sm-2 + + h6.inline Remove + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) + + div.my-3(data-hook="edit-specie-list") + + button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable + + div.tab-pane(id="view-species" data-hook="view-species") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-2 + + h6.inline Name - table.table - thead - tr - th(scope="col", colspan="3") + div.col-sm-2 - div + h6.inline Diffusion Constant - div.inline Model Mode + div.col-sm-6 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.spatialSpeciesMode) + h6.inline Restricted to Types: - tbody - tr - td - input(type="radio", name="species-mode", data-hook="all-continuous", data-name="continuous") - | Concentration - td - input(type="radio", name="species-mode", data-hook="all-discrete", data-name="discrete") - | Population + div.col-sm-2(data-hook="species-annotation-header") - button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable + h6.inline Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-species") Save Variables \ No newline at end of file + div.my-3(data-hook="view-specie-list") diff --git a/client/templates/includes/speciesEditor.pug b/client/templates/includes/speciesEditor.pug index 9162e167f8..0c0ae48995 100644 --- a/client/templates/includes/speciesEditor.pug +++ b/client/templates/includes/speciesEditor.pug @@ -1,92 +1,89 @@ -div#species-editor.card.card-body +div#species-editor.card - div + div.card-header.pb-0 - h3.inline Variables - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-species", data-hook="collapse") - + h3.inline.mr-3 Variables - div.collapse(class="show", id="collapse-species" data-hook="species-list-container") - p + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="species-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="species-edit-tab" data-toggle="tab" href="#edit-species") Edit + + li.nav-item + + a.nav-link.tab(data-hook="species-view-tab" data-toggle="tab" href="#view-species") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-species" data-hook="collapse") - + + div.card-body + p.mb-0 | Time varying quantities of interest; e.g.: concentrations, populations, or counts of biochemical species, epidemological compartments, chemicals, proteins, or molecules. - table.table - thead - tr - th(scope="col") - div - div.inline Name + div.collapse.tab-content(class="show" id="collapse-species" data-hook="species-list-container") + + div.tab-pane.active(id="edit-species" data-hook="edit-species") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-sm-5 + + h6.inline Initial Condition + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.initialValue) + + div.col-sm-2 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + h6.inline Annotation - th(scope="col") - div - div.inline Initial Condition + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.initialValue) + div.col-sm-2 - th(scope="col") - div - div.inline Annotation + h6.inline Remove - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - - th(scope="col") - div - div.inline Remove + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) - - tbody(data-hook="specie-list") + div.my-3(data-hook="edit-specie-list") - table.table - thead - tr - th(scope="col", colspan="3") + div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable - div + button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable - div.inline Variable Mode + div.tab-pane(id="view-species" data-hook="view-species") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.speciesMode) + hr - tbody - tr - td - input(type="radio", name="species-mode", data-hook="all-continuous", data-name="continuous") - | Concentration - td - input(type="radio", name="species-mode", data-hook="all-discrete", data-name="discrete") - | Population - td - input(type="radio", name="species-mode", data-hook="advanced", data-name="dynamic") - | Hybrid Concentration/Population - - div.collapse(data-hook="advanced-species") + div.mx-1.row.head.align-items-baseline - table.table + div.col-sm-2 - thead + h6.inline Name - tr + div.col-sm-2 - th(scope="col") Name + h6.inline Initial Condition - th(scope="col") - div - div.inline Mode + div.col-sm-2 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.mode) - - th(scope="col" colspan="2") - div - div.inline Switching Settings (Hybrid Only) + h6.inline Mode - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.switchValue) + div.col-sm-4(data-hook="species-switching-header") - tbody(data-hook="edit-species-mode") + h6.inline Switch Tolerance/Minimum Value for Switching - div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable + div.col-sm-2(data-hook="species-annotation-header") - button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable + h6.inline Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-species") Save Variables + div.my-3(data-hook="view-specie-list") diff --git a/client/templates/includes/speciesViewer.pug b/client/templates/includes/speciesViewer.pug deleted file mode 100644 index f2ad8b30e9..0000000000 --- a/client/templates/includes/speciesViewer.pug +++ /dev/null @@ -1,28 +0,0 @@ -div#species-viewer.card.card-body - - div - - h3.inline Variables - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-species", data-hook="collapse") - - - div.collapse(class="show", id="collapse-species") - - table.table - thead - tr - th.col-md-3-view(scope="col") Name - if this.collection.parent.is_spatial - th.col-md-3-view(scope="col") Diffusion Constant - th.col-md-3-view(scope="col") Restricted to Types: - else - th.col-md-3-view(scope="col") Initial Condition - th.col-md-3-view(scope="col") Mode - if this.collection.parent.defaultMode === "dynamic" - th.col-md-3-view(scope="col") Switch Tolerance/Minimum Value for Switching - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="specie-list") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-species") Edit Variables diff --git a/client/templates/includes/timespanSettings.pug b/client/templates/includes/timespanSettings.pug index 35e7ad3b88..34401ab4e3 100644 --- a/client/templates/includes/timespanSettings.pug +++ b/client/templates/includes/timespanSettings.pug @@ -1,33 +1,113 @@ -div#preview-settings.card.card-body +div#preview-settings.card - div + div.card-header.pb-0 - h3.inline Timespan + h3.inline.mr-3 Timespan - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-preview-settings", data-hook="collapse") - + div.inline.mr-3 - div.collapse(class="show", id="collapse-preview-settings" data-hook="timespan-container") + ul.nav.nav-tabs.card-header-tabs(id="timespan-tabs") - p Timespan settings are used for the preview and all workflows created using this model. + li.nav-item - table.table + a.nav-link.tab.active(data-hook="timespan-edit-tab" data-toggle="tab" href="#edit-timespan") Edit - thead + li.nav-item - tr + a.nav-link.tab(data-hook="timespan-view-tab" data-toggle="tab" href="#view-timespan") View - th(scope="col") Simulation Time + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-preview-settings" data-hook="collapse") - - th(scope="col") - div - div.inline Sample Frequency + div.card-body - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.timeUnits) + p.mb-0 + | Timespan settings are used for the preview and all workflows created using this model. - tbody + div.collapse.tab-content(class="show" id="collapse-preview-settings" data-hook="timespan-container") - tr + div.tab-pane.active(id="edit-timespan" data-hook="edit-timespan") - td: div(data-hook="preview-time") + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + h6 Simulation Time + + div.col-sm-3 + + h6.inline Sample Frequency + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.timeUnits) + + div.col-sm-6 + + if this.parent.model.is_spatial + h6 Timestep Size + + div.mx-1.my-3.row + + div.col-sm-3(data-hook="preview-time") + + div.col-sm-3(data-hook="time-units") + + div.col-sm-6 + + if this.parent.model.is_spatial + div + + div.row + + div.col-sm-3 + + span.inline 1e-5 + + div.col-sm-8 + + span.mr-2.inline Current Value: + + div.inline(data-hook="timestep-size-value") + + div.col-sm-1.d-flex.justify-content-end + + span.inline 1 + + input.custom-range( + type="range" + min="1e-5" + max="1" + step="1e-5" + value=this.model.timestepSize + data-hook="timestep-size-slider" + ) + + div.tab-pane(id="view-timespan" data-hook="view-timespan") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + h6 Simulation Time + + div.col-sm-3 + + h6 Sample Frequency + + div.col-sm-3 + + if this.parent.model.is_spatial + h6 Timestep Size + + div.mx-1.my-3.row + + div.col-sm-3(data-hook="view-end-sim")="0 to " + this.model.endSim + + div.col-sm-3(data-hook="view-time-step")=this.model.timeStep + + if this.parent.model.is_spatial + div.col-sm-6(data-hook="view-timestep-size")=this.model.timestepSize + - td: div(data-hook="time-units") diff --git a/client/templates/includes/viewEventAssignment.pug b/client/templates/includes/viewEventAssignment.pug new file mode 100644 index 0000000000..dc2a1867fc --- /dev/null +++ b/client/templates/includes/viewEventAssignment.pug @@ -0,0 +1,14 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-3 + + div.pl-2=this.model.variable.name + + div.col-sm-7 + + div=this.model.expression diff --git a/client/templates/includes/viewEventAssignments.pug b/client/templates/includes/viewEventAssignments.pug deleted file mode 100644 index 6d9f62e879..0000000000 --- a/client/templates/includes/viewEventAssignments.pug +++ /dev/null @@ -1,5 +0,0 @@ -tr - - td=this.model.variable.name - - td=this.model.expression \ No newline at end of file diff --git a/client/templates/includes/viewEvents.pug b/client/templates/includes/viewEvents.pug index a26dfb151f..ef20893766 100644 --- a/client/templates/includes/viewEvents.pug +++ b/client/templates/includes/viewEvents.pug @@ -1,45 +1,51 @@ -tr +div.mx-1 - td: div.name=this.model.name + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.model.triggerExpression + div.row - td: div(data-hook="assignment-viewer") + div.col-sm-2 - td + div.pl-2=this.model.name - div="Delay: "+this.delay + div.col-sm-2=this.model.triggerExpression - div="Priority: "+this.model.priority + div.col-sm-4(data-hook="assignment-viewer") - div + div.col-sm-2 - span.checkbox(for="initial-value") Initial Value: + div="Delay: "+this.delay - input(type="checkbox", id="initial-value", data-hook="event-trigger-init-value" disabled) + div="Priority: "+this.model.priority - div + div - span.checkbox(for="persistent") Persistent: + span.checkbox(for="initial-value") Initial Value: - input(type="checkbox", id="persistent", data-hook="event-trigger-persistent" disabled) + input(type="checkbox", id="initial-value", data-hook="event-trigger-init-value" disabled) - div.event-adv-header Use Values From + div - div + span.checkbox(for="persistent") Persistent: - input(type="radio", id="trigger-time", name="use-values-from" data-hook="trigger-time" disabled) + input(type="checkbox", id="persistent", data-hook="event-trigger-persistent" disabled) - span.inline.horizontal-space(for="trigger-time") Trigger Time + div.event-adv-header Use Values From - div + div - input(type="radio", id="assignment-time", name="use-values-from" data-hook="assignment-time" disabled) + input(type="radio", id="trigger-time", name="view-use-values-from" data-hook="view-trigger-time" disabled) - span.inline.horizontal-space(for="assignment-time") Assignment Time + span.inline.horizontal-space(for="trigger-time") Trigger Time - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div + input(type="radio", id="assignment-time", name="view-use-values-from" data-hook="view-assignment-time" disabled) + + span.inline.horizontal-space(for="assignment-time") Assignment Time + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/templates/includes/viewFunctionDefinition.pug b/client/templates/includes/viewFunctionDefinition.pug new file mode 100644 index 0000000000..cdfc4ac78a --- /dev/null +++ b/client/templates/includes/viewFunctionDefinition.pug @@ -0,0 +1,15 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-10 + + div.pl-2=this.model.signature + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/templates/includes/viewParameters.pug b/client/templates/includes/viewParameters.pug index a4cae60444..6e338ecbea 100644 --- a/client/templates/includes/viewParameters.pug +++ b/client/templates/includes/viewParameters.pug @@ -1,10 +1,19 @@ -tr - td=this.model.name +div.mx-1 - td=this.model.expression + if(this.model.collection.indexOf(this.model) !== 0) + hr - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.row - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div + div.col-sm-3 + + div.pl-2=this.model.name + + div.col-sm-7 + + div=this.model.expression + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/templates/includes/viewReactions.pug b/client/templates/includes/viewReactions.pug index 582e48dae1..21be6b3275 100644 --- a/client/templates/includes/viewReactions.pug +++ b/client/templates/includes/viewReactions.pug @@ -1,15 +1,24 @@ -tr - td=this.model.name +div.mx-1 - td: div(data-hook="summary") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.rate + div.row.align-items-baseline - if this.model.collection.parent.is_spatial - td: div=this.types.join(', ') + div.col-md-2 - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.pl-2=this.model.name - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div + div.col-md-3(data-hook="summary") + + div.col-md-3=this.rate + + if this.model.collection.parent.is_spatial + div.col-md-2(data-hook="reaction-types") + + div=this.types.join(', ') + + div.col-md-2(data-hook="reaction-annotation-header") + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation) diff --git a/client/templates/includes/viewRules.pug b/client/templates/includes/viewRules.pug index f4b039523b..30bacfe4dd 100644 --- a/client/templates/includes/viewRules.pug +++ b/client/templates/includes/viewRules.pug @@ -1,14 +1,19 @@ -tr - td=this.model.name +div.mx-1 - td=this.model.type + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.model.variable.name + div.row - td=this.model.expression + div.col-sm-2=this.model.name - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.col-sm-2=this.model.type - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div + div.col-sm-2=this.model.variable.name + + div.col-sm-4=this.model.expression + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/templates/includes/viewSpatialSpecies.pug b/client/templates/includes/viewSpatialSpecies.pug new file mode 100644 index 0000000000..4af4396fa9 --- /dev/null +++ b/client/templates/includes/viewSpatialSpecies.pug @@ -0,0 +1,23 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-2 + + div.pl-2=this.model.name + + div.col-sm-2 + + div=this.model.diffusionConst + + div.col-sm-6 + + div=this.types.join(", ") + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/templates/includes/viewSpecies.pug b/client/templates/includes/viewSpecies.pug index e5ab763ec9..a792c44923 100644 --- a/client/templates/includes/viewSpecies.pug +++ b/client/templates/includes/viewSpecies.pug @@ -1,21 +1,27 @@ -tr - td=this.model.name +div.mx-1 - if this.model.collection.parent.is_spatial - td=this.model.diffusionConst + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.types.join(", ") - else - td=this.model.value + div.row - td=this.model.mode + div.col-sm-2 - if this.model.collection.parent.defaultMode === 'dynamic' - td=this.switchingValWithLabel + div.pl-2=this.model.name - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.col-sm-2 - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div - \ No newline at end of file + div=this.model.value + + div.col-sm-2 + + div=this.model.mode + + if this.model.mode === "dynamic" + div.col-sm-4 + div=this.switchingValWithLabel + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/templates/pages/modelEditor.pug b/client/templates/pages/modelEditor.pug index eeb3660d85..31cf797e9d 100644 --- a/client/templates/pages/modelEditor.pug +++ b/client/templates/pages/modelEditor.pug @@ -26,6 +26,26 @@ section.page ol.breadcrumb li.breadcrumb-item.active(aria-current="page")=this.model.name + div.card + + div.card-header.pb-0 + + h3.inline Model Mode + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.modelMode) + + div.card-body + div.row + div.col-sm-4 + input.mr-2(type="radio" name="default-mode" data-hook="all-continuous" data-name="continuous") + span Concentration + div.col-sm-4 + input.mr-2(type="radio" name="default-mode" data-hook="all-discrete" data-name="discrete") + span Population + div.col-sm-4(data-hook="advaced-model-mode") + input.mr-2(type="radio" name="default-mode" data-hook="advanced" data-name="dynamic") + span Hybrid Concentration/Population + div(data-hook="domain-viewer-container") div(data-hook="species-editor-container") @@ -36,9 +56,9 @@ section.page div(data-hook="reactions-editor-container") - div.card.card-body(data-hook="model-editor-advanced-container") + div.card(data-hook="model-editor-advanced-container") - div + div.card-header.pb-0 h3.inline Advanced @@ -46,26 +66,52 @@ section.page div.collapse(id="me-advanced-section", data-hook="me-advanced-section") - div(data-hook="events-editor-container") + div.card-body - div(data-hook="rules-editor-container") + div(data-hook="events-editor-container") - div(data-hook="sbml-component-container") + div(data-hook="rules-editor-container") - div.card.card-body.collapse.show(data-hook="system-volume-container") + div(data-hook="sbml-component-container") - div + div.card.collapse.show(data-hook="system-volume-container") - h3.inline System Volume - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-system-volume", data-hook="collapse-system-volume") + + div.card-header.pb-0 - div.collapse(id="collapse-system-volume" data-hook="system-volume-section") - - p System volume affects custom propensities or stochastic reactions with two reactants. The volume sets the implicit parameter 'vol' which can be used in custom propensity functions. A single well-mixed volume is assumed. Compartments are not currently supported. + h3.inline.mr-3 System Volume + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="volume-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="volume-edit-tab" data-toggle="tab" href="#edit-system-volume") Edit + + li.nav-item + + a.nav-link.tab(data-hook="volume-view-tab" data-toggle="tab" href="#view-system-volume") View + + button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-system-volume", data-hook="collapse-system-volume") + + + div.card-body + + p.mb-0 + | System volume affects custom propensities or stochastic reactions with two reactants. The volume sets the implicit parameter 'vol' which can be used in custom propensity functions. A single well-mixed volume is assumed. Compartments are not currently supported. In most cases this should not be changed. please review your model requirements carefully before changing this property. + + div.collapse.tab-content(id="collapse-system-volume" data-hook="system-volume-section") + + div.tab-pane.active(id="edit-system-volume" data-hook="edit-system-volume") + + hr + + div(data-hook="edit-volume") + + div.tab-pane(id="view-system-volume" data-hook="view-system-volume") - hr + hr - div(data-hook="volume") + div(data-hook="view-volume") div(data-hook="model-settings-container") diff --git a/client/tooltips.js b/client/tooltips.js index 4c426230a2..42e8c77241 100644 --- a/client/tooltips.js +++ b/client/tooltips.js @@ -17,6 +17,13 @@ along with this program. If not, see . */ module.exports = { + modelEditor: { + modelMode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ + "using discrete (integer count) values.
Hybrid Concentration/Population - Allows a variables to be represented using continuous and/or discrete values.", + + spatialModelMode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ + "using discrete (integer count) values." + }, speciesEditor: { name: "Unique identifier for Variable. Cannot share a name with other model components.", @@ -26,12 +33,6 @@ module.exports = { remove: "A variables may only be removed if it is not a part of any reaction, event assignment, or rule.", - speciesMode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ - "using discrete (integer count) values.
Hybrid Concentration/Population - Allows a variables to be represented using continuous and/or discrete values.", - - spatialSpeciesMode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ - "using discrete (integer count) values.", - mode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ "using discrete (integer count) values.
Hybrid Concentration/Population - Allows a variables to be represented using continuous and/or discrete values.", diff --git a/client/views/component-types.js b/client/views/component-types.js index d5856b2693..212ef27413 100644 --- a/client/views/component-types.js +++ b/client/views/component-types.js @@ -46,5 +46,8 @@ module.exports = View.extend({ let index = this.parent.model.types.indexOf(typeID); this.parent.model.types.splice(index, 1); } + if(this.parent.modelType === "species") { + this.parent.model.trigger('change'); + } } }); \ No newline at end of file diff --git a/client/views/edit-advanced-specie.js b/client/views/edit-advanced-specie.js deleted file mode 100644 index 33c6ac446a..0000000000 --- a/client/views/edit-advanced-specie.js +++ /dev/null @@ -1,162 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -var tests = require('./tests'); -//views -var View = require('ampersand-view'); -var SelectView = require('ampersand-select-view'); -var InputView = require('./input'); -//templates -var template = require('../templates/includes/editAdvancedSpecie.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'change [data-hook=switching-tol]' : 'setSwitchingType', - 'change [data-hook=switching-min]' : 'setSwitchingType', - 'change [data-hook=specie-mode]' : 'setSpeciesMode', - }, - initialize: function () { - View.prototype.initialize.apply(this, arguments); - }, - render: function () { - View.prototype.render.apply(this, arguments); - var optionDict = {"continuous":"Concentration", "discrete":"Population", "dynamic":"Hybrid Concentration/Population"} - var modeSelectView = new SelectView({ - label: '', - name: 'mode', - required: true, - idAttributes: 'cid', - options: ['Concentration','Population','Hybrid Concentration/Population'], - value: optionDict[this.model.mode], - }); - app.registerRenderSubview(this, modeSelectView, "specie-mode") - if(this.model.isSwitchTol){ - $(this.queryByHook('switching-tol')).prop('checked', true); - }else{ - $(this.queryByHook('switching-min')).prop('checked', true); - } - this.toggleSwitchingSettings(); - this.updateInputValidation(); - }, - update: function () { - }, - updateValid: function () { - }, - setSpeciesMode: function (e) { - var value = e.target.selectedOptions.item(0).text - var modeDict = {"Concentration":"continuous","Population":"discrete","Hybrid Concentration/Population":"dynamic"} - this.model.mode = modeDict[value] - this.model.collection.trigger('update-species', this.model.compID, this.model, false, false); - this.updateInputValidation(); - this.toggleSwitchingSettings(); - }, - setSwitchingType: function (e) { - this.model.isSwitchTol = $(this.queryByHook('switching-tol')).is(":checked"); - this.updateInputValidation(); - this.toggleSwitchingSettingsInput(); - }, - updateInputValidation: function () { - // Update validation requirements and re-run tests for inputSwitchTol. - // This removes error reporting not using switching tolerance - let shouldValidateTol = this.model.mode === "dynamic" && this.model.isSwitchTol - this.inputSwitchTol.required = shouldValidateTol; - this.inputSwitchTol.tests = shouldValidateTol ? tests.valueTests : [] - this.inputSwitchTol.runTests() - // Update validation requirements and re-run tests for inputSwitchMin. - // This removes error reporting when not using minimum value for switching. - let shouldValidateMin = this.model.mode === "dynamic" && !this.model.isSwitchTol - this.inputSwitchMin.required = shouldValidateMin; - this.inputSwitchMin.tests = shouldValidateMin ? tests.valueTests : [] - this.inputSwitchMin.runTests() - // Add/Remove 'input-invalid' class from inputSwitchTol and inputSwitchMin based on whether - // the user is using switching tolerance or minimum value for switching - let tolInput = $(this.queryByHook('switching-tolerance')).find('input')[0] - let minInput = $(this.queryByHook('switching-threshold')).find('input')[0] - if(this.model.mode !== "dynamic") { - $(tolInput).removeClass('input-invalid') - $(minInput).removeClass('input-invalid') - }else if(this.model.isSwitchTol){ - $(minInput).removeClass('input-invalid') - if(this.model.switchTol === "" || isNaN(this.model.switchTol)){ - $(tolInput).addClass('input-invalid') - } - }else{ - $(tolInput).removeClass('input-invalid') - if(this.model.switchMin === "" || isNaN(this.model.switchMin)){ - $(minInput).addClass('input-invalid') - } - } - }, - toggleSwitchingSettingsInput: function () { - if(this.model.isSwitchTol){ - $(this.queryByHook('switching-threshold')).find('input').prop('disabled', true); - $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', false); - }else{ - $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', true); - $(this.queryByHook('switching-threshold')).find('input').prop('disabled', false); - } - }, - toggleSwitchingSettings: function () { - if(this.model.mode === "dynamic"){ - $(this.queryByHook('switching-tol')).prop('disabled', false); - $(this.queryByHook('switching-min')).prop('disabled', false); - this.toggleSwitchingSettingsInput(); - }else{ - $(this.queryByHook('switching-tol')).prop('disabled', true); - $(this.queryByHook('switching-min')).prop('disabled', true); - $(this.queryByHook('switching-threshold')).find('input').prop('disabled', true); - $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', true); - } - }, - subviews: { - inputSwitchTol: { - hook: 'switching-tolerance', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'switching-tolerance', - label: '', - tests: tests.valueTests, - modelKey: 'switchTol', - valueType: 'number', - value: this.model.switchTol, - }); - }, - }, - inputSwitchMin: { - hook: 'switching-threshold', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'switching-threshold', - label: '', - tests: tests.valueTests, - modelKey: 'switchMin', - valueType: 'number', - value: this.model.switchMin, - }); - }, - }, - }, -}); \ No newline at end of file diff --git a/client/views/edit-event-assignment.js b/client/views/edit-event-assignment.js index 72c4b268d3..8e48f4931f 100644 --- a/client/views/edit-event-assignment.js +++ b/client/views/edit-event-assignment.js @@ -24,31 +24,22 @@ var View = require('ampersand-view'); var InputView = require('./input'); var SelectView = require('ampersand-select-view'); //templates -var template = require('../templates/includes/editEventAssignment.pug'); +var editTemplate = require('../templates/includes/editEventAssignment.pug'); +var viewTemplate = require('../templates/includes/viewEventAssignment.pug'); module.exports = View.extend({ - template: template, events: { 'click [data-hook=remove]' : 'removeAssignment', 'change [data-hook=event-assignment-variable]' : 'selectAssignmentVariable', + 'change [data-hook=event-assignment-expression]' : 'updateViewer' }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); - var options = this.getOptions(); - var variableSelectView = new SelectView({ - label: '', - name: 'variable', - required: true, - idAttributes: 'cid', - options: options, - value: this.model.variable.name, - }); - app.registerRenderSubview(this, variableSelectView, 'event-assignment-variable'); - var inputField = this.queryByHook('event-assignment-Expression').children[0].children[1]; - $(inputField).attr("placeholder", "---No Expression Entered---"); }, update: function () { }, @@ -62,44 +53,66 @@ module.exports = View.extend({ getOptions: function () { var species = this.model.collection.parent.collection.parent.species; var parameters = this.model.collection.parent.collection.parent.parameters; - var speciesNames = species.map(function (specie) { return specie.name }); - var parameterNames = parameters.map(function (parameter) { return parameter.name }); - return speciesNames.concat(parameterNames); + var specs = species.map(function (specie) { + return [specie.compID, specie.name] + }); + var params = parameters.map(function (parameter) { + return [parameter.compID, parameter.name] + }); + let options = [{groupName: "Variables", options: specs}, + {groupName: "Parameters", options: params}] + return options; }, selectAssignmentVariable: function (e) { var species = this.model.collection.parent.collection.parent.species; var parameters = this.model.collection.parent.collection.parent.parameters; - var val = e.target.selectedOptions.item(0).text; + var val = Number(e.target.value); var eventVar = species.filter(function (specie) { - if(specie.name === val) { + if(specie.compID === val) { return specie; } }); if(!eventVar.length) { eventVar = parameters.filter(function (parameter) { - if(parameter.name === val) { + if(parameter.compID === val) { return parameter; } }); } this.model.variable = eventVar[0]; + this.updateViewer(); this.model.collection.parent.collection.trigger('change'); }, + updateViewer: function () { + this.model.collection.parent.trigger('change'); + }, subviews: { inputAssignmentExpression: { - hook: 'event-assignment-Expression', + hook: 'event-assignment-expression', prepareView: function (el) { return new InputView({ parent: this, required: true, name: 'event-assignment-expression', - label: '', - tests: '', + placeholder: "---No Expression Entered---", modelKey: 'expression', valueType: 'string', value: this.model.expression, }); }, }, + variableSelectView: { + hook: 'event-assignment-variable', + prepareView: function (el) { + let options = this.getOptions(); + return new SelectView({ + name: 'variable', + required: true, + idAttributes: 'cid', + groupOptions: options, + value: this.model.variable.compID, + }); + } + } }, }); \ No newline at end of file diff --git a/client/views/edit-function-definition.js b/client/views/edit-function-definition.js index 61a3261b45..bb7146befb 100644 --- a/client/views/edit-function-definition.js +++ b/client/views/edit-function-definition.js @@ -22,18 +22,20 @@ var modals = require('../modals'); //views var View = require('ampersand-view'); //templates -var template = require('../templates/includes/editFunctionDefinition.pug'); +var editTemplate = require('../templates/includes/editFunctionDefinition.pug'); +let viewTemplate = require('../templates/includes/viewFunctionDefinition.pug'); module.exports = View.extend({ - template: template, events: { 'click [data-hook=remove]' : 'removeFunctionDefinition', 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); $(document).on('hide.bs.modal', '.modal', function (e) { e.target.remove() @@ -57,7 +59,7 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderEdirFunctionDefinitionView(); + self.parent.renderEditFunctionDefinitionView(); modal.modal('hide'); }); }, diff --git a/client/views/edit-parameter.js b/client/views/edit-parameter.js index ff96685435..e8b342f24c 100644 --- a/client/views/edit-parameter.js +++ b/client/views/edit-parameter.js @@ -17,6 +17,7 @@ along with this program. If not, see . */ var $ = require('jquery'); +let _ = require('underscore'); //support files var tests = require('./tests'); var modals = require('../modals') @@ -24,10 +25,10 @@ var modals = require('../modals') var View = require('ampersand-view'); var InputView = require('./input'); //templates -var template = require('../templates/includes/editReactionVar.pug'); +var editTemplate = require('../templates/includes/editParameter.pug'); +let viewTemplate = require('../templates/includes/viewParameters.pug'); module.exports = View.extend({ - template: template, bindings: { 'model.inUse': { hook: 'remove', @@ -43,9 +44,14 @@ module.exports = View.extend({ initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.previousName = this.model.name + this.viewMode = attrs.viewMode ? attrs.viewMode : false; }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); + if(!this.viewMode){ + this.model.on('change', _.bind(this.updateViewer, this)) + } $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); }); @@ -96,6 +102,9 @@ module.exports = View.extend({ this.model.collection.trigger('remove') } }, + updateViewer: function () { + this.parent.renderViewParameter() + }, subviews: { inputName: { hook: 'input-name-container', diff --git a/client/views/edit-rule.js b/client/views/edit-rule.js index 9c54a25471..de9884510f 100644 --- a/client/views/edit-rule.js +++ b/client/views/edit-rule.js @@ -17,6 +17,7 @@ along with this program. If not, see . */ var $ = require('jquery'); +let _ = require('underscore'); //support files let app = require('../app'); var tests = require('./tests'); @@ -26,43 +27,26 @@ var View = require('ampersand-view'); var InputView = require('./input'); var SelectView = require('ampersand-select-view'); //templates -var template = require('../templates/includes/editRule.pug'); +var editTemplate = require('../templates/includes/editRule.pug'); +let viewTemplate = require('../templates/includes/viewRules.pug'); module.exports = View.extend({ - template: template, events: { 'change [data-hook=rule-type]' : 'selectRuleType', 'change [data-hook=rule-variable]' : 'selectRuleVariable', 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', 'click [data-hook=remove]' : 'removeRule', }, - initiailize: function (attrs, options) { - View.prototype.initiailize.apply(this, arguments); + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; }, render: function () { - this.renderWithTemplate(); - var inputField = this.queryByHook('rule-expression').children[0].children[1]; - $(inputField).attr("placeholder", "---No Expression Entered---"); - var varOptions = this.getOptions(); - var typeOptions = ['Rate Rule', 'Assignment Rule'] - var typeSelectView = new SelectView({ - label: '', - name: 'type', - required: true, - idAttributes: 'cid', - options: typeOptions, - value: this.model.type, - }); - var variableSelectView = new SelectView({ - label: '', - name: 'variable', - required: true, - idAttributes: 'cid', - options: varOptions, - value: this.model.variable.name, - }); - app.registerRenderSubview(this, typeSelectView, "rule-type"); - app.registerRenderSubview(this, variableSelectView, 'rule-variable'); + this.template = this.viewMode ? viewTemplate : editTemplate; + View.prototype.render.apply(this, arguments); + if(!this.viewMode){ + this.model.on('change', _.bind(this.updateViewer, this)) + } $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); }); @@ -95,33 +79,38 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderRules(); + self.parent.renderEditRules(); modal.modal('hide'); }); }, getOptions: function () { var species = this.model.collection.parent.species; var parameters = this.model.collection.parent.parameters; - var speciesNames = species.map(function (specie) { return specie.name }); - var parameterNames = parameters.map(function (parameter) { return parameter.name }); - return speciesNames.concat(parameterNames); + var specs = species.map(function (specie) { + return [specie.compID, specie.name]; + }); + var params = parameters.map(function (parameter) { + return [parameter.compID, parameter.name]; + }); + let options = [{groupName: "Variables", options: specs}, + {groupName: "Parameters", options: params}]; + return options; }, selectRuleType: function (e) { - var type = e.target.selectedOptions.item(0).text; - this.model.type = type; + this.model.type = e.target.value; }, selectRuleVariable: function (e) { var species = this.model.collection.parent.species; var parameters = this.model.collection.parent.parameters; - var val = e.target.selectedOptions.item(0).text; + var compID = Number(e.target.value); var ruleVar = species.filter(function (specie) { - if(specie.name === val) { + if(specie.compID === compID) { return specie; } }); if(!ruleVar.length) { ruleVar = parameters.filter(function (parameter) { - if(parameter.name === val) { + if(parameter.compID === compID) { return parameter; } }); @@ -131,6 +120,9 @@ module.exports = View.extend({ removeRule: function () { this.model.collection.removeRule(this.model); }, + updateViewer: function () { + this.parent.renderViewRules(); + }, subviews: { inputName: { hook: 'rule-name', @@ -145,7 +137,33 @@ module.exports = View.extend({ valueType: 'string', value: this.model.name, }); - }, + } + }, + selectType: { + hook: 'rule-type', + prepareView: function (el) { + let options = ['Rate Rule', 'Assignment Rule']; + return new SelectView({ + name: 'type', + required: true, + idAttributes: 'cid', + options: options, + value: this.model.type, + }); + } + }, + selectTarget: { + hook: 'rule-variable', + prepareView: function(el) { + let options = this.getOptions(); + return new SelectView({ + name: 'variable', + required: true, + idAttributes: 'cid', + groupOptions: options, + value: this.model.variable.compID, + }); + } }, inputRule: { hook: 'rule-expression', @@ -154,14 +172,12 @@ module.exports = View.extend({ parent: this, required: true, name: 'rule-expression', - label: '', - tests: '', modelKey: 'expression', valueType: 'string', value: this.model.expression, placeholder: "--No Formula Entered--" }); - }, - }, - }, + } + } + } }); \ No newline at end of file diff --git a/client/views/edit-spatial-specie.js b/client/views/edit-spatial-specie.js deleted file mode 100644 index 7a685800b6..0000000000 --- a/client/views/edit-spatial-specie.js +++ /dev/null @@ -1,102 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var tests = require('./tests'); -var _ = require('underscore'); -//views -var View = require('ampersand-view'); -var InputView = require('./input'); -var TypesView = require('./component-types'); -//templates -var template = require('../templates/includes/editSpatialSpecie.pug'); - -module.exports = View.extend({ - template: template, - bindings: { - 'model.inUse': { - hook: 'remove', - type: 'booleanAttribute', - name: 'disabled', - }, - }, - events: { - 'click [data-hook=remove]' : 'removeSpecie', - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.baseModel = this.model.collection.parent; - this.modelType = "species"; - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderTypes(); - }, - update: function () { - }, - updateValid: function () { - }, - removeSpecie: function () { - this.remove(); - this.collection.removeSpecie(this.model); - }, - renderTypes: function () { - if(this.typesView) { - this.typesView.remove(); - } - this.typesView = this.renderCollection( - this.baseModel.domain.types, - TypesView, - this.queryByHook("species-types"), - {"filter": function (model) { - return model.typeID != 0; - }} - ); - }, - subviews: { - inputName: { - hook: 'input-name-container', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'name', - label: '', - tests: tests.nameTests, - modelKey: 'name', - valueType: 'string', - value: this.model.name, - }); - }, - }, - inputValue: { - hook: 'input-diffusion-coeff-container', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'diffusion coeff', - label: '', - tests: tests.valueTests, - modelKey: 'diffusionConst', - valueType: 'number', - value: this.model.diffusionConst, - }); - }, - }, - }, -}); \ No newline at end of file diff --git a/client/views/edit-specie.js b/client/views/edit-specie.js deleted file mode 100644 index b920f7c952..0000000000 --- a/client/views/edit-specie.js +++ /dev/null @@ -1,132 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var $ = require('jquery'); -//support files -var tests = require('./tests'); -var modals = require('../modals'); -//views -var View = require('ampersand-view'); -var InputView = require('./input'); -//templates -var template = require('../templates/includes/editReactionVar.pug'); - -module.exports = View.extend({ - template: template, - bindings: { - 'model.inUse': { - hook: 'remove', - type: 'booleanAttribute', - name: 'disabled', - }, - }, - events: { - 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', - 'click [data-hook=remove]' : 'removeSpecie', - 'change [data-hook=input-name-container]' : 'setSpeciesName', - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.previousName = this.model.name - }, - render: function () { - View.prototype.render.apply(this, arguments); - $(document).on('shown.bs.modal', function (e) { - $('[autofocus]', e.target).focus(); - }); - $(document).on('hide.bs.modal', '.modal', function (e) { - e.target.remove() - }); - if(!this.model.annotation){ - $(this.queryByHook('edit-annotation-btn')).text('Add') - } - }, - update: function () { - }, - updateValid: function (e) { - }, - removeSpecie: function () { - this.remove(); - this.collection.removeSpecie(this.model); - this.parent.toggleSpeciesCollectionError(); - }, - setSpeciesName: function (e) { - if(!e.target.value.trim()) { - this.model.name = this.previousName; - this.parent.renderEditSpeciesView(); - }else{ - this.previousName = this.model.name; - this.model.collection.trigger('update-species', this.model.compID, this.model, true, false); - this.model.collection.trigger('remove'); - } - }, - editAnnotation: function () { - var self = this; - var name = this.model.name; - var annotation = this.model.annotation; - if(document.querySelector('#speciesAnnotationModal')) { - document.querySelector('#speciesAnnotationModal').remove(); - } - let modal = $(modals.annotationModalHtml("species", name, annotation)).modal(); - let okBtn = document.querySelector('#speciesAnnotationModal .ok-model-btn'); - let input = document.querySelector('#speciesAnnotationModal #speciesAnnotationInput'); - input.addEventListener("keyup", function (event) { - if(event.keyCode === 13){ - event.preventDefault(); - okBtn.click(); - } - }); - okBtn.addEventListener('click', function (e) { - self.model.annotation = input.value.trim(); - self.parent.renderEditSpeciesView(); - modal.modal('hide'); - }); - }, - subviews: { - inputName: { - hook: 'input-name-container', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'name', - label: '', - tests: tests.nameTests, - modelKey: 'name', - valueType: 'string', - value: this.model.name, - }); - }, - }, - inputValue: { - hook: 'input-value-container', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'value', - label: '', - tests: tests.valueTests, - modelKey: 'value', - valueType: 'number', - value: this.model.value, - }); - }, - }, - }, -}); \ No newline at end of file diff --git a/client/views/edit-species.js b/client/views/edit-species.js new file mode 100644 index 0000000000..2aad005f6c --- /dev/null +++ b/client/views/edit-species.js @@ -0,0 +1,304 @@ +/* +StochSS is a platform for simulating biochemical systems +Copyright (C) 2019-2020 StochSS developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +let $ = require('jquery'); +let _ = require('underscore'); +//support files +let app = require('../app'); +let tests = require('./tests'); +let modals = require('../modals'); +//views +let InputView = require('./input'); +let View = require('ampersand-view'); +let TypesView = require('./component-types'); +let SelectView = require('ampersand-select-view'); +//templates +let editTemplate = require('../templates/includes/editSpecies.pug'); +let editSpatialTemplate = require('../templates/includes/editSpatialSpecies.pug'); +let viewTemplate = require('../templates/includes/viewSpecies.pug'); +let viewSpatialTemplate = require('../templates/includes/viewSpatialSpecies.pug'); + +module.exports = View.extend({ + bindings: { + 'model.inUse': { + hook: 'remove', + type: 'booleanAttribute', + name: 'disabled', + }, + }, + events: { + 'change [data-hook=input-name-container]' : 'setSpeciesName', + 'change [data-hook=specie-mode]' : 'setSpeciesMode', + 'change [data-hook=switching-tol]' : 'setSwitchingType', + 'change [data-hook=switching-min]' : 'setSwitchingType', + 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', + 'click [data-hook=remove]' : 'removeSpecie', + 'click [data-hook=collapse-advanced]' : 'changeCollapseButtonText' + }, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + this.modelType = "species"; + this.previousName = this.model.name; + this.viewMode = attrs.viewMode ? attrs.viewMode : false; + if(this.viewMode && this.parent.spatial) { + let self = this; + this.types = []; + if(this.model.types) { + this.model.types.forEach(function (index) { + let type = self.model.collection.parent.domain.types.get(index, "typeID"); + self.types.push(type.name); + }); + } + } + if(this.model.mode === null && this.parent.defaultMode !== "") { + this.model.mode = this.parent.defaultMode; + } + this.switchingValWithLabel = this.model.isSwitchTol ? + "Switching Tolerance: " + this.model.switchTol : + "Minimum Value For Switching: " + this.model.switchMin; + }, + render: function () { + if(this.parent.spatial){ + this.template = this.viewMode ? viewSpatialTemplate : editSpatialTemplate; + }else{ + this.template = this.viewMode ? viewTemplate : editTemplate; + } + View.prototype.render.apply(this, arguments); + $(document).on('shown.bs.modal', function (e) { + $('[autofocus]', e.target).focus(); + }); + $(document).on('hide.bs.modal', '.modal', function (e) { + e.target.remove(); + }); + if(!this.model.annotation){ + $(this.queryByHook('edit-annotation-btn')).text('Add'); + } + if(this.parent.defaultMode !== "dynamic") { + $(this.queryByHook("advanced-species")).css("display", "none"); + }else{ + $(this.queryByHook("advanced-species")).css("display", "block"); + } + if(this.model.isSwitchTol){ + $(this.queryByHook('switching-tol')).prop('checked', true); + }else{ + $(this.queryByHook('switching-min')).prop('checked', true); + } + this.toggleSwitchingSettings(); + this.updateInputValidation(); + if(!this.viewMode){ + this.model.on('change', _.bind(this.updateViewer, this)); + if(this.parent.spatial) { + this.renderTypes(); + } + } + }, + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); + }, + editAnnotation: function () { + let self = this; + let name = this.model.name; + let annotation = this.model.annotation; + if(document.querySelector('#speciesAnnotationModal')) { + document.querySelector('#speciesAnnotationModal').remove(); + } + let modal = $(modals.annotationModalHtml("species", name, annotation)).modal(); + let okBtn = document.querySelector('#speciesAnnotationModal .ok-model-btn'); + let input = document.querySelector('#speciesAnnotationModal #speciesAnnotationInput'); + input.addEventListener("keyup", function (event) { + if(event.keyCode === 13){ + event.preventDefault(); + okBtn.click(); + } + }); + okBtn.addEventListener('click', function (e) { + self.model.annotation = input.value.trim(); + self.parent.renderEditSpeciesView(); + modal.modal('hide'); + }); + }, + removeSpecie: function () { + this.remove(); + this.collection.removeSpecie(this.model); + this.parent.toggleSpeciesCollectionError(); + }, + renderTypes: function () { + if(this.typesView) { + this.typesView.remove(); + } + this.typesView = this.renderCollection( + this.model.collection.parent.domain.types, + TypesView, + this.queryByHook("species-types"), + {"filter": function (model) { + return model.typeID != 0; + }} + ); + }, + setSpeciesMode: function (e) { + this.model.mode = e.target.value; + this.model.collection.trigger('update-species', this.model.compID, this.model, false, false); + this.updateInputValidation(); + this.toggleSwitchingSettings(); + }, + setSpeciesName: function (e) { + if(!e.target.value.trim()) { + this.model.name = this.previousName; + this.parent.renderEditSpeciesView(); + }else{ + this.previousName = this.model.name; + this.model.collection.trigger('update-species', this.model.compID, this.model, true, false); + } + }, + setSwitchingType: function (e) { + this.model.isSwitchTol = $(this.queryByHook('switching-tol')).is(":checked"); + this.updateInputValidation(); + this.toggleSwitchingSettingsInput(); + }, + toggleSwitchingSettings: function () { + if(this.model.mode === "dynamic"){ + $(this.queryByHook('switching-tol')).prop('disabled', false); + $(this.queryByHook('switching-min')).prop('disabled', false); + this.toggleSwitchingSettingsInput(); + }else{ + $(this.queryByHook('switching-tol')).prop('disabled', true); + $(this.queryByHook('switching-min')).prop('disabled', true); + $(this.queryByHook('switching-threshold')).find('input').prop('disabled', true); + $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', true); + } + }, + toggleSwitchingSettingsInput: function () { + if(this.model.isSwitchTol){ + $(this.queryByHook('switching-threshold')).find('input').prop('disabled', true); + $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', false); + }else{ + $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', true); + $(this.queryByHook('switching-threshold')).find('input').prop('disabled', false); + } + }, + update: function () {}, + updateInputValidation: function () { + if(this.viewMode || this.parent.spatial) {return} + // Update validation requirements and re-run tests for inputSwitchTol. + // This removes error reporting not using switching tolerance + let shouldValidateTol = this.model.mode === "dynamic" && this.model.isSwitchTol + this.inputSwitchTol.required = shouldValidateTol; + this.inputSwitchTol.tests = shouldValidateTol ? tests.valueTests : [] + this.inputSwitchTol.runTests() + // Update validation requirements and re-run tests for inputSwitchMin. + // This removes error reporting when not using minimum value for switching. + let shouldValidateMin = this.model.mode === "dynamic" && !this.model.isSwitchTol + this.inputSwitchMin.required = shouldValidateMin; + this.inputSwitchMin.tests = shouldValidateMin ? tests.valueTests : [] + this.inputSwitchMin.runTests() + // Add/Remove 'input-invalid' class from inputSwitchTol and inputSwitchMin based on whether + // the user is using switching tolerance or minimum value for switching + let tolInput = $(this.queryByHook('switching-tolerance')).find('input')[0] + let minInput = $(this.queryByHook('switching-threshold')).find('input')[0] + if(this.model.mode !== "dynamic") { + $(tolInput).removeClass('input-invalid') + $(minInput).removeClass('input-invalid') + }else if(this.model.isSwitchTol){ + $(minInput).removeClass('input-invalid') + if(this.model.switchTol === "" || isNaN(this.model.switchTol)){ + $(tolInput).addClass('input-invalid') + } + }else{ + $(tolInput).removeClass('input-invalid') + if(this.model.switchMin === "" || isNaN(this.model.switchMin)){ + $(minInput).addClass('input-invalid') + } + } + }, + updateValid: function (e) {}, + updateViewer: function () { + this.parent.renderViewSpeciesView() + }, + subviews: { + inputName: { + hook: 'input-name-container', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'name', + tests: tests.nameTests, + modelKey: 'name', + valueType: 'string', + value: this.model.name, + }); + } + }, + inputValue: { + hook: 'input-value-container', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: this.parent.spatial ? 'diffusion constant' : 'value', + tests: tests.valueTests, + modelKey: this.parent.spatial ? 'diffusionConst' : 'value', + valueType: 'number', + value: this.parent.spatial ? this.model.diffusionConst : this.model.value, + }); + } + }, + selectMode: { + hook: 'specie-mode', + prepareView: function (el) { + options = [['continuous', 'Concentration'], ['discrete', 'Population'], + ['dynamic', 'Hybrid Concentration/Population']] + return new SelectView({ + name: 'mode', + required: true, + idAttributes: 'cid', + options: options, + value: this.model.mode, + }); + } + }, + inputSwitchTol: { + hook: 'switching-tolerance', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'switching-tolerance', + tests: tests.valueTests, + modelKey: 'switchTol', + valueType: 'number', + value: this.model.switchTol, + }); + } + }, + inputSwitchMin: { + hook: 'switching-threshold', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'switching-threshold', + tests: tests.valueTests, + modelKey: 'switchMin', + valueType: 'number', + value: this.model.switchMin, + }); + } + } + } +}); diff --git a/client/views/event-assignments-editor.js b/client/views/event-assignments-editor.js index 917820a137..7f790869f5 100644 --- a/client/views/event-assignments-editor.js +++ b/client/views/event-assignments-editor.js @@ -16,6 +16,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +let $ = require('jquery'); +//support files +let app = require('../app'); //views var View = require('ampersand-view'); var EditEventAssignment = require('./edit-event-assignment'); @@ -25,25 +28,56 @@ var template = require('../templates/includes/eventAssignmentsEditor.pug'); module.exports = View.extend({ template: template, events: { - 'click [data-hook=add-event-assignment]' : 'addAssignment', + 'click [data-hook=collapse-assignments]' : 'changeCollapseButtonText', + 'click [data-hook=add-event-assignment]' : 'addAssignment' }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; + this.tooltips = attrs.tooltips; }, render: function () { View.prototype.render.apply(this, arguments); - this.renderCollection( + if(this.readOnly) { + $(this.queryByHook('edit-event-assignments')).removeClass('active'); + $(this.queryByHook('view-event-assignments')).addClass('active'); + $(this.queryByHook('event-assignments-header')).css("display", "none"); + this.renderViewEventAssignment(); + }else{ + this.renderEditEventAssignment(); + } + }, + addAssignment: function () { + this.collection.addEventAssignment(); + this.collection.parent.collection.trigger('change') + }, + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); + }, + renderEditEventAssignment: function () { + if(this.editEventAssignments) { + this.editEventAssignments.remove(); + } + this.editEventAssignments = this.renderCollection( this.collection, EditEventAssignment, - this.queryByHook('event-assignments-container') + this.queryByHook('edit-event-assignments-container') + ); + }, + renderViewEventAssignment: function () { + if(this.viewEventAssignments) { + this.viewEventAssignments.remove(); + } + let options = {viewOptions: {viewMode: true}} + this.viewEventAssignments = this.renderCollection( + this.collection, + EditEventAssignment, + this.queryByHook('view-event-assignments-container'), + options ); }, update: function () { }, updateValid: function () { - }, - addAssignment: function () { - this.collection.addEventAssignment(); - this.collection.parent.collection.trigger('change') - }, + } }) \ No newline at end of file diff --git a/client/views/event-assignments-viewer.js b/client/views/event-assignments-viewer.js deleted file mode 100644 index ee3551a067..0000000000 --- a/client/views/event-assignments-viewer.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -//views -var View = require('ampersand-view'); -var ViewAssignments = require('./view-event-assignments'); -//templates -var template = require('../templates/includes/eventAssignmentsViewer.pug'); - -module.exports = View.extend({ - template: template, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewAssignments, 'view-event-assignments-list'); - }, -}); \ No newline at end of file diff --git a/client/views/event-details.js b/client/views/event-details.js index 3d64cdb23d..b0cc2aca09 100644 --- a/client/views/event-details.js +++ b/client/views/event-details.js @@ -44,8 +44,8 @@ module.exports = View.extend({ events: { 'change [data-hook=event-trigger-init-value]' : 'setTriggerInitialValue', 'change [data-hook=event-trigger-persistent]' : 'setTriggerPersistent', - 'change [data-hook=trigger-time]' : 'setUseValuesFromTriggerTime', - 'change [data-hook=assignment-time]' : 'setUseValuesFromTriggerTime', + 'change [data-hook=edit-trigger-time]' : 'setUseValuesFromTriggerTime', + 'change [data-hook=edit-assignment-time]' : 'setUseValuesFromTriggerTime', 'click [data-hook=advanced-event-button]' : 'changeCollapseButtonText', }, initialize: function (attrs, options) { @@ -54,16 +54,12 @@ module.exports = View.extend({ render: function () { View.prototype.render.apply(this, arguments); this.renderEventAssignments(); - var triggerExpressionField = this.queryByHook('event-trigger-expression').children[0].children[1]; - $(triggerExpressionField).attr("placeholder", "---No Expression Entered---"); - var delayField = this.queryByHook('event-delay').children[0].children[1]; - $(delayField).attr("placeholder", "---No Expression Entered---"); if(this.model.useValuesFromTriggerTime){ - $(this.queryByHook('trigger-time')).prop('checked', true) + $(this.queryByHook('edit-trigger-time')).prop('checked', true) }else{ - $(this.queryByHook('assignment-time')).prop('checked', true) + $(this.queryByHook('edit-assignment-time')).prop('checked', true) } - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -81,6 +77,7 @@ module.exports = View.extend({ } this.eventAssignmentsView = new EventAssignment({ collection: this.model.eventAssignments, + tooltips: this.parent.tooltips }); app.registerRenderSubview(this, this.eventAssignmentsView, 'event-assignments'); }, @@ -111,8 +108,7 @@ module.exports = View.extend({ parent: this, required: false, name: 'delay', - label: '', - tests: '', + placeholder: '---No Expression Entered---', modelKey: 'delay', valueType: 'string', value: this.model.delay, @@ -126,8 +122,6 @@ module.exports = View.extend({ parent: this, required: true, name: 'priority', - label: '', - tests: '', modelKey: 'priority', valueType: 'string', value: this.model.priority, @@ -141,8 +135,7 @@ module.exports = View.extend({ parent: this, required: true, name: 'trigger-expression', - label: '', - tests: '', + placeholder: '---No Expression Entered---', modelKey: 'triggerExpression', valueType: 'string', value: this.model.triggerExpression, diff --git a/client/views/event-listings.js b/client/views/event-listings.js index 2f248816f7..658828a165 100644 --- a/client/views/event-listings.js +++ b/client/views/event-listings.js @@ -17,52 +17,36 @@ along with this program. If not, see . */ var $ = require('jquery'); +let _ = require('underscore'); //support files +let app = require('../app'); var tests = require('./tests'); var modals = require('../modals'); //views var View = require('ampersand-view'); var InputView = require('./input'); +var EventAssignment = require('./event-assignments-editor'); //templates -var template = require('../templates/includes/eventListings.pug'); - -let eventAnnotationModalHtml = (eventName, annotation) => { - return ` - - ` -} +var editTemplate = require('../templates/includes/eventListings.pug'); +let viewTemplate = require('../templates/includes/viewEvents.pug'); module.exports = View.extend({ - template: template, bindings: { - 'model.name' : { - type: 'value', - hook: 'input-name-container' - }, 'model.selected' : { type: function (el, value, previousValue) { el.checked = value; }, hook: 'select' + }, + 'model.initialValue': { + hook: 'event-trigger-init-value', + type: 'booleanAttribute', + name: 'checked', + }, + 'model.persistent': { + hook: 'event-trigger-persistent', + type: 'booleanAttribute', + name: 'checked', } }, events: { @@ -72,8 +56,15 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; + if(this.viewMode) { + this.delay = this.model.delay === "" ? "None" : this.model.delay; + }else{ + this.model.on("change", _.bind(this.updateViewer, this)); + } }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); @@ -82,7 +73,15 @@ module.exports = View.extend({ e.target.remove() }); if(!this.model.annotation){ - $(this.queryByHook('edit-annotation-btn')).text('Add') + $(this.queryByHook('edit-annotation-btn')).text('Add'); + } + if(this.viewMode) { + this.renderViewEventAssignments(); + if(this.model.useValuesFromTriggerTime) { + $(this.queryByHook("view-trigger-time")).prop('checked', true); + }else{ + $(this.queryByHook("view-assignment-time")).prop('checked', true); + } } }, update: function () { @@ -110,7 +109,7 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderEventListingsView(); + self.parent.renderEditEventListingsView(); modal.modal('hide'); }); }, @@ -118,6 +117,20 @@ module.exports = View.extend({ this.remove(); this.collection.removeEvent(this.model); }, + renderViewEventAssignments: function () { + if(this.viewEventAssignmentsView){ + this.viewEventAssignmentsView.remove() + } + this.viewEventAssignmentsView = new EventAssignment({ + collection: this.model.eventAssignments, + tooltips: this.parent.tooltips, + readOnly: true + }); + app.registerRenderSubview(this, this.viewEventAssignmentsView, 'assignment-viewer'); + }, + updateViewer: function () { + this.parent.renderViewEventListingView(); + }, subviews: { inputName: { hook: 'event-name-container', diff --git a/client/views/events-editor.js b/client/views/events-editor.js index 73bfbcca24..189c16fad5 100644 --- a/client/views/events-editor.js +++ b/client/views/events-editor.js @@ -37,54 +37,89 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.tooltips = Tooltips.eventsEditor - this.opened = attrs.opened - this.collection.on("select", function (event) { - this.setSelectedEvent(event); - this.setDetailsView(event); - }, this); - this.collection.on("remove", function (event) { - // Select the last event by default - // But only if there are other events other than the one we're removing - if (event.detailsView) - event.detailsView.remove(); - this.collection.removeEvent(event); - if (this.collection.length) { - var selected = this.collection.at(this.collection.length-1); - this.collection.trigger("select", selected); - } - }, this); - this.collection.parent.species.on('add remove', this.toggleAddEventButton, this); - this.collection.parent.parameters.on('add remove', this.toggleAddEventButton, this); + if(!this.readOnly) { + this.collection.on("select", function (event) { + this.setSelectedEvent(event); + this.setDetailsView(event); + }, this); + this.collection.on("remove", function (event) { + // Select the last event by default + // But only if there are other events other than the one we're removing + if (event.detailsView) + event.detailsView.remove(); + this.collection.removeEvent(event); + if (this.collection.length) { + var selected = this.collection.at(this.collection.length-1); + this.collection.trigger("select", selected); + } + }, this); + this.collection.parent.species.on('add remove', this.toggleAddEventButton, this); + this.collection.parent.parameters.on('add remove', this.toggleAddEventButton, this); + } }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEventListingsView(); - this.detailsContainer = this.queryByHook('event-details-container'); - this.detailsViewSwitcher = new ViewSwitcher({ - el: this.detailsContainer, - }); - if (this.collection.length) { - this.setSelectedEvent(this.collection.at(0)); - this.collection.trigger("select", this.selectedEvent); - } - this.toggleAddEventButton() - if(this.opened) { - this.openEventsContainer(); + if(this.readOnly) { + $(this.queryByHook('events-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('events-view-tab')).tab('show'); + $(this.queryByHook('edit-events')).removeClass('active'); + $(this.queryByHook('view-events')).addClass('active'); + }else { + this.renderEditEventListingsView(); + this.detailsContainer = this.queryByHook('event-details-container'); + this.detailsViewSwitcher = new ViewSwitcher({ + el: this.detailsContainer, + }); + if(this.collection.length) { + this.setSelectedEvent(this.collection.at(0)); + this.collection.trigger("select", this.selectedEvent); + } + this.toggleAddEventButton() } + this.renderViewEventListingView(); }, update: function () { }, updateValid: function () { }, - renderEventListingsView: function () { - if(this.eventListingsView){ - this.eventListingsView.remove(); + renderEditEventListingsView: function () { + if(this.editEventListingsView){ + this.editEventListingsView.remove(); + } + this.editEventListingsView = this.renderCollection( + this.collection, + EventListings, + this.queryByHook('edit-event-listing-container') + ); + $(document).ready(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); + }, + renderViewEventListingView: function () { + if(this.viewEventListingsView) { + this.viewEventListingsView.remove(); } - this.eventListingsView = this.renderCollection( + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("events-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("events-annotation-header")).css("display", "block"); + } + let options = {viewOptions: {parent: this, viewMode: true}}; + this.viewEventListingsView = this.renderCollection( this.collection, EventListings, - this.queryByHook('event-listing-container') + this.queryByHook('view-event-listing-container'), + options ); $(document).ready(function () { $('[data-toggle="tooltip"]').tooltip(); @@ -134,11 +169,6 @@ module.exports = View.extend({ this.parent.modelStateButtons.clickSaveHandler(e); this.parent.renderEventsView(mode="view"); }, - openEventsContainer: function () { - $(this.queryByHook('events')).collapse('show'); - let collapseBtn = $(this.queryByHook('collapse')) - collapseBtn.trigger('click') - }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); }, diff --git a/client/views/events-viewer.js b/client/views/events-viewer.js deleted file mode 100644 index fd5326b366..0000000000 --- a/client/views/events-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewEvent = require('./view-events'); -//templates -var template = require('../templates/includes/eventsViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-events]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewEvent, this.queryByHook('view-events-container')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderEventsView("edit", true); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index d35fe7885a..b1563030d3 100644 --- a/client/views/model-viewer.js +++ b/client/views/model-viewer.js @@ -20,11 +20,11 @@ along with this program. If not, see . let app = require('../app'); //views let View = require('ampersand-view'); -let RulesViewer = require('./rules-viewer'); -let EventsViewer = require('./events-viewer'); -let SpeciesViewer = require('./species-viewer'); -let ReactionsViewer = require('./reactions-viewer'); -let ParametersViewer = require('./parameters-viewer'); +let EventsViewer = require('./events-editor'); +let RulesViewer = require('./rules-editor'); +let SpeciesViewer = require('./species-editor'); +let ReactionsViewer = require('./reactions-editor'); +let ParametersViewer = require('./parameters-editor'); let SBMLComponentsView = require('./sbml-component-editor'); //templates let template = require('../templates/includes/modelViewer.pug'); @@ -48,38 +48,45 @@ module.exports = View.extend({ }, renderEventsView: function () { let eventsViewer = new EventsViewer({ - collection: this.model.eventsCollection + collection: this.model.eventsCollection, + readOnly: true }); app.registerRenderSubview(this, eventsViewer, "events-viewer-container"); }, renderParametersView: function () { let parametersViewer = new ParametersViewer({ - collection: this.model.parameters + collection: this.model.parameters, + readOnly: true }); app.registerRenderSubview(this, parametersViewer, "parameters-viewer-container"); }, renderReactionsView: function () { let reactionsViewer = new ReactionsViewer({ - collection: this.model.reactions + collection: this.model.reactions, + readOnly: true }); app.registerRenderSubview(this, reactionsViewer, "reactions-viewer-container"); }, renderRulesView: function () { let rulesViewer = new RulesViewer({ - collection: this.model.rules + collection: this.model.rules, + readOnly: true }); app.registerRenderSubview(this, rulesViewer, "rules-viewer-container"); }, renderSBMLComponentsView: function () { let sbmlComponentsView = new SBMLComponentsView({ functionDefinitions: this.model.functionDefinitions, - viewMode: true + readOnly: true }); app.registerRenderSubview(this, sbmlComponentsView, "sbml-components-viewer-container"); }, renderSpeciesView: function () { let speciesViewer = new SpeciesViewer({ - collection: this.model.species + collection: this.model.species, + spatial: this.model.is_spatial, + defaultMode: this.model.defaultMode, + readOnly: true }); app.registerRenderSubview(this, speciesViewer, "species-viewer-container"); }, diff --git a/client/views/parameters-editor.js b/client/views/parameters-editor.js index c8e69b06b5..aa72c100c5 100644 --- a/client/views/parameters-editor.js +++ b/client/views/parameters-editor.js @@ -30,14 +30,13 @@ module.exports = View.extend({ template: template, events: { 'click [data-hook=add-parameter]' : 'addParameter', - 'click [data-hook=save-parameters]' : 'switchToViewMode', 'click [data-hook=collapse]' : 'changeCollapseButtonText', }, initialize: function (attrs, options) { var self = this; View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.tooltips = Tooltips.parametersEditor - this.opened = attrs.opened this.collection.on('update-parameters', function (compID, parameter) { self.collection.parent.reactions.map(function (reaction) { if(reaction.rate && reaction.rate.compID === compID){ @@ -63,10 +62,19 @@ module.exports = View.extend({ }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEditParameter(); - if(this.opened) { - this.openParametersContainer(); + if(this.readOnly) { + $(this.queryByHook('parameters-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('parameters-view-tab')).tab('show'); + $(this.queryByHook('edit-parameters')).removeClass('active'); + $(this.queryByHook('view-parameters')).addClass('active'); + }else{ + this.renderEditParameter(); } + this.renderViewParameter(); }, update: function () { }, @@ -79,15 +87,33 @@ module.exports = View.extend({ this.editParameterView = this.renderCollection( this.collection, EditParameterView, - this.queryByHook('parameter-list') + this.queryByHook('edit-parameter-list') ); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); }); }); }, + renderViewParameter: function () { + if(this.viewParameterView) { + this.viewParameterView.remove(); + } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("parameters-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("parameters-annotation-header")).css("display", "block"); + } + let options = {viewOptions: {viewMode: true}}; + this.viewParameterView = this.renderCollection( + this.collection, + EditParameterView, + this.queryByHook('view-parameter-list'), + options + ); + }, addParameter: function () { this.collection.addParameter(); $(document).ready(function () { @@ -98,15 +124,6 @@ module.exports = View.extend({ }); }); }, - switchToViewMode: function (e) { - this.parent.modelStateButtons.clickSaveHandler(e); - this.parent.renderParametersView(mode="view"); - }, - openParametersContainer: function () { - $(this.queryByHook('parameters-list-container')).collapse('show'); - let collapseBtn = $(this.queryByHook('collapse')) - collapseBtn.trigger('click') - }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); }, diff --git a/client/views/parameters-viewer.js b/client/views/parameters-viewer.js deleted file mode 100644 index e58ba726fd..0000000000 --- a/client/views/parameters-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewParameter = require('./view-parameter'); -//templates -var template = require('../templates/includes/parametersViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-parameters]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewParameter, this.queryByHook('parameter-list')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderParametersView("edit", true); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/reaction-details.js b/client/views/reaction-details.js index 36e1949844..aa074e024b 100644 --- a/client/views/reaction-details.js +++ b/client/views/reaction-details.js @@ -182,6 +182,7 @@ module.exports = View.extend({ var val = e.target.selectedOptions.item(0).text; var param = this.getRateFromParameters(val); this.model.rate = param || this.model.rate; + this.model.trigger("change"); this.model.collection.trigger("change"); } }, diff --git a/client/views/reaction-listing.js b/client/views/reaction-listing.js index 864c24a43d..6de8a56d3e 100644 --- a/client/views/reaction-listing.js +++ b/client/views/reaction-listing.js @@ -18,6 +18,7 @@ along with this program. If not, see . var $ = require('jquery'); var katex = require('katex'); +let _ = require('underscore'); //support files var tests = require('./tests'); var modals = require('../modals'); @@ -25,10 +26,10 @@ var modals = require('../modals'); var View = require('ampersand-view'); var InputView = require('./input'); //templates -var template = require('../templates/includes/reactionListing.pug'); +var editTemplate = require('../templates/includes/reactionListing.pug'); +let viewTemplate = require('../templates/includes/viewReactions.pug'); module.exports = View.extend({ - template: template, bindings: { 'model.name' : { type: 'value', @@ -58,8 +59,23 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; + if(this.viewMode) { + this.rate = this.model.reactionType === "custom-propensity" ? this.model.propensity : this.model.rate.name; + this.types = []; + let self = this; + if(this.model.types) { + this.model.types.forEach(function (index) { + let type = self.model.collection.parent.domain.types.get(index, "typeID"); + self.types.push(type.name) + }); + } + }else{ + this.model.on('change', _.bind(this.updateViewer, this)); + } }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); @@ -100,10 +116,13 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderReactionListingView(); + self.parent.renderEditReactionListingView(); modal.modal('hide'); }); }, + updateViewer: function () { + this.parent.renderViewReactionView(); + }, subviews: { inputName: { hook: 'input-name-container', diff --git a/client/views/reactions-editor.js b/client/views/reactions-editor.js index c54ef00484..dc9b56ed77 100644 --- a/client/views/reactions-editor.js +++ b/client/views/reactions-editor.js @@ -45,69 +45,83 @@ module.exports = View.extend({ 'click [data-hook=four]' : 'handleAddReactionClick', 'click [data-hook=custom-massaction]' : 'handleAddReactionClick', 'click [data-hook=custom-propensity]' : 'handleAddReactionClick', - 'click [data-hook=save-reactions]' : 'switchToViewMode', 'click [data-hook=collapse]' : 'changeCollapseButtonText' }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.tooltips = Tooltips.reactionsEditor - this.opened = attrs.opened - this.collection.on("select", function (reaction) { - this.setSelectedReaction(reaction); - this.setDetailsView(reaction); - }, this); - this.collection.on("remove", function (reaction) { - // Select the last reaction by default - // But only if there are other reactions other than the one we're removing - if (reaction.detailsView) - reaction.detailsView.remove(); - this.collection.removeReaction(reaction); - if (this.collection.length) { - var selected = this.collection.at(this.collection.length-1); - this.collection.trigger("select", selected); - } - }, this); - this.collection.parent.species.on('add remove', this.toggleAddReactionButton, this); - this.collection.parent.parameters.on('add remove', this.toggleReactionTypes, this); - this.collection.parent.on('change', this.toggleProcessError, this) + this.readOnly = attrs.readOnly ? attrs.readOnly : false; + if(!this.readOnly) { + this.collection.on("select", function (reaction) { + this.setSelectedReaction(reaction); + this.setDetailsView(reaction); + }, this); + this.collection.on("remove", function (reaction) { + // Select the last reaction by default + // But only if there are other reactions other than the one we're removing + if (reaction.detailsView) + reaction.detailsView.remove(); + this.collection.removeReaction(reaction); + if (this.collection.length) { + var selected = this.collection.at(this.collection.length-1); + this.collection.trigger("select", selected); + } + }, this); + this.collection.parent.species.on('add remove', this.toggleAddReactionButton, this); + this.collection.parent.parameters.on('add remove', this.toggleReactionTypes, this); + this.collection.parent.on('change', this.toggleProcessError, this) + } }, render: function () { View.prototype.render.apply(this, arguments); - this.renderReactionListingView(); - this.detailsContainer = this.queryByHook('reaction-details-container'); - this.detailsViewSwitcher = new ViewSwitcher({ - el: this.detailsContainer, - }); - if (this.collection.length) { - this.setSelectedReaction(this.collection.at(0)); - this.collection.trigger("select", this.selectedReaction); - } - this.collection.trigger("change"); - this.toggleAddReactionButton(); - if(this.collection.parent.parameters.length > 0){ - $(this.queryByHook('add-reaction-partial')).prop('hidden', true); - } - else{ - $(this.queryByHook('add-reaction-full')).prop('hidden', true); - } - this.renderReactionTypes(); - katex.render("\\emptyset", this.queryByHook('emptyset'), { - displayMode: false, - output: 'html', - }); - if(this.opened) { - this.openReactionsContainer(); + if(this.readOnly) { + this.renderViewReactionView() + $(this.queryByHook('reactions-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('reactions-view-tab')).tab('show'); + $(this.queryByHook('edit-reactions')).removeClass('active'); + $(this.queryByHook('view-reactions')).addClass('active'); + }else{ + this.renderReactionListingViews(); + this.detailsContainer = this.queryByHook('reaction-details-container'); + this.detailsViewSwitcher = new ViewSwitcher({ + el: this.detailsContainer, + }); + if (this.collection.length) { + this.setSelectedReaction(this.collection.at(0)); + this.collection.trigger("select", this.selectedReaction); + } + this.collection.trigger("change"); + this.toggleAddReactionButton(); + if(this.collection.parent.parameters.length > 0){ + $(this.queryByHook('add-reaction-partial')).prop('hidden', true); + } + else{ + $(this.queryByHook('add-reaction-full')).prop('hidden', true); + } + this.renderReactionTypes(); + katex.render("\\emptyset", this.queryByHook('emptyset'), { + displayMode: false, + output: 'html', + }); + this.toggleProcessError() + $(this.queryByHook('massaction-message')).prop('hidden', this.collection.parent.parameters.length > 0); } - this.toggleProcessError() - $(this.queryByHook('massaction-message')).prop('hidden', this.collection.parent.parameters.length > 0); }, update: function () { }, updateValid: function () { }, - renderReactionListingView: function () { - if(this.reactionListingView){ - this.reactionListingView.remove(); + renderReactionListingViews: function () { + this.renderEditReactionListingView(); + this.renderViewReactionView(); + }, + renderEditReactionListingView: function () { + if(this.editReactionListingView){ + this.editReactionListingView.remove(); } if(this.collection.parent.parameters.length <= 0) { for(var i = 0; i < this.collection.length; i++) { @@ -116,10 +130,10 @@ module.exports = View.extend({ } } } - this.reactionListingView = this.renderCollection( + this.editReactionListingView = this.renderCollection( this.collection, ReactionListingView, - this.queryByHook('reaction-list') + this.queryByHook('edit-reaction-list') ); $(document).ready(function () { $('[data-toggle="tooltip"]').tooltip(); @@ -128,6 +142,27 @@ module.exports = View.extend({ }); }); }, + renderViewReactionView: function () { + if(this.viewReactionListingView){ + this.viewReactionListingView.remove(); + } + if(!this.collection.parent.is_spatial){ + $(this.queryByHook("reaction-types-header")).css("display", "none"); + } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("reaction-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("reaction-annotation-header")).css("display", "block"); + } + let options = {viewOptions: {viewMode: true, hasAnnotations: this.containsMdlWithAnn}} + this.viewReactionListingView = this.renderCollection( + this.collection, + ReactionListingView, + this.queryByHook('view-reaction-list'), + options + ); + }, toggleAddReactionButton: function () { $(this.queryByHook('add-reaction-full')).prop('disabled', (this.collection.parent.species.length <= 0)); $(this.queryByHook('add-reaction-partial')).prop('disabled', (this.collection.parent.species.length <= 0)); @@ -186,10 +221,6 @@ module.exports = View.extend({ detailsView.parent = this; return detailsView }, - switchToViewMode: function (e) { - this.parent.modelStateButtons.clickSaveHandler(e); - this.parent.renderReactionsView(mode="view"); - }, openReactionsContainer: function () { $(this.queryByHook('reactions-list-container')).collapse('show'); let collapseBtn = $(this.queryByHook('collapse')) @@ -206,8 +237,9 @@ module.exports = View.extend({ return ReactionTypes[type].label }, toggleProcessError: function () { - let errorMsg = $(this.queryByHook('process-component-error')) let model = this.collection.parent + if(model.is_spatial) {return}; + let errorMsg = $(this.queryByHook('process-component-error')) if(this.collection.length <= 0 && model.eventsCollection.length <= 0 && model.rules.length <= 0) { errorMsg.addClass('component-invalid') errorMsg.removeClass('component-valid') diff --git a/client/views/reactions-viewer.js b/client/views/reactions-viewer.js deleted file mode 100644 index ee31d9b390..0000000000 --- a/client/views/reactions-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewReactions = require('./view-reactions'); -//templates -var template = require('../templates/includes/reactionsViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-reactions]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewReactions, this.queryByHook('reaction-list')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderReactionsView("edit", true); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/rules-editor.js b/client/views/rules-editor.js index fdba4cb842..6c0145acad 100644 --- a/client/views/rules-editor.js +++ b/client/views/rules-editor.js @@ -31,38 +31,70 @@ module.exports = View.extend({ events: { 'click [data-hook=rate-rule]' : 'addRule', 'click [data-hook=assignment-rule]' : 'addRule', - 'click [data-hook=save-rules]' : 'switchToViewMode', 'click [data-hook=collapse]' : 'changeCollapseButtonText', }, - initialize: function (args) { + initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.collection.parent.species.on('add remove', this.toggleAddRuleButton, this); this.collection.parent.parameters.on('add remove', this.toggleAddRuleButton, this); this.tooltips = Tooltips.rulesEditor - this.opened = args.opened }, render: function () { View.prototype.render.apply(this, arguments); - this.renderRules(); - this.toggleAddRuleButton() - if(this.opened) { - this.openRulesContainer(); + if(this.readOnly) { + $(this.queryByHook('rules-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('rules-view-tab')).tab('show'); + $(this.queryByHook('edit-rules')).removeClass('active'); + $(this.queryByHook('view-rules')).addClass('active'); + }else { + this.renderEditRules(); + this.toggleAddRuleButton(); } + this.renderViewRules(); }, update: function () { }, updateValid: function () { }, - renderRules: function () { + renderEditRules: function () { if(this.rulesView) { this.rulesView.remove(); } this.rulesView = this.renderCollection( this.collection, RuleView, - this.queryByHook('rule-list-container') + this.queryByHook('edit-rule-list-container') + ); + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); + }, + renderViewRules: function () { + if(this.viewRulesView) { + this.viewRulesView.remove(); + } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("rules-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("rules-annotation-header")).css("display", "block"); + } + let options = {viewOptions: {viewMode: true}}; + this.viewRulesView = this.renderCollection( + this.collection, + RuleView, + this.queryByHook('view-rules-list-container'), + options ); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -70,7 +102,7 @@ module.exports = View.extend({ }); }, toggleAddRuleButton: function () { - this.renderRules(); + this.renderEditRules(); var numSpecies = this.collection.parent.species.length; var numParameters = this.collection.parent.parameters.length; var disabled = numSpecies <= 0 && numParameters <= 0 @@ -79,7 +111,7 @@ module.exports = View.extend({ addRule: function (e) { var type = e.target.dataset.name this.collection.addRule(type); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -87,15 +119,6 @@ module.exports = View.extend({ }); }); }, - switchToViewMode: function (e) { - this.parent.modelStateButtons.clickSaveHandler(e); - this.parent.renderRulesView(mode="view"); - }, - openRulesContainer: function () { - $(this.queryByHook('rules-list-container')).collapse('show'); - let collapseBtn = $(this.queryByHook('collapse')) - collapseBtn.trigger('click') - }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); }, diff --git a/client/views/rules-viewer.js b/client/views/rules-viewer.js deleted file mode 100644 index a8fce201da..0000000000 --- a/client/views/rules-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewRules = require('./view-rules'); -//templates -var template = require('../templates/includes/rulesViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-rules]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewRules, this.queryByHook('rules-list')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderRulesView("edit", true); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/sbml-component-editor.js b/client/views/sbml-component-editor.js index 565b097bc4..e7f58940ab 100644 --- a/client/views/sbml-component-editor.js +++ b/client/views/sbml-component-editor.js @@ -36,22 +36,58 @@ module.exports = View.extend({ View.prototype.initialize.apply(this, arguments); this.tooltips = Tooltips.sbmlComponentsEditor this.functionDefinitions = attrs.functionDefinitions; - this.viewMode = attrs.viewMode; + this.readOnly = attrs.readOnly ? attrs.readOnly : false; }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEdirFunctionDefinitionView(); + if(this.readOnly) { + $(this.queryByHook('function-definitions-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('function-definitions-view-tab')).tab('show'); + $(this.queryByHook('edit-function-definitions')).removeClass('active'); + $(this.queryByHook('view-function-definitions')).addClass('active'); + }else { + this.renderEditFunctionDefinitionView(); + } + this.renderViewFunctionDefinitionView(); }, - renderEdirFunctionDefinitionView: function () { + renderEditFunctionDefinitionView: function () { if(this.editFunctionDefinitionView){ this.editFunctionDefinitionView.remove(); } this.editFunctionDefinitionView = this.renderCollection( this.functionDefinitions, EditFunctionDefinition, - this.queryByHook('function-definition-list') + this.queryByHook('edit-function-definition-list') + ); + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); + }, + renderViewFunctionDefinitionView: function () { + if(this.viewFunctionDefinitionView) { + this.viewFunctionDefinitionView.remove(); + } + let options = {viewOptions: {viewMode: true}}; + this.containsMdlWithAnn = this.functionDefinitions.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("function-definition-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("function-definition-annotation-header")).css("display", "block"); + } + this.viewFunctionDefinitionView = this.renderCollection( + this.functionDefinitions, + EditFunctionDefinition, + this.queryByHook('view-function-definition-list'), + options ); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); diff --git a/client/views/species-editor.js b/client/views/species-editor.js index e604138e74..6eb3a6b445 100644 --- a/client/views/species-editor.js +++ b/client/views/species-editor.js @@ -16,54 +16,53 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -var $ = require('jquery'); +let $ = require('jquery'); //support files let app = require('../app'); -var modals = require('../modals'); -var Tooltips = require('../tooltips'); +let Tooltips = require('../tooltips'); //views -var View = require('ampersand-view'); -var EditNonspatialSpecieView = require('./edit-specie'); -var EditSpatialSpecieView = require('./edit-spatial-specie'); -var EditAdvancedSpecie = require('./edit-advanced-specie'); +let View = require('ampersand-view'); +let SpecieView = require('./edit-species'); //templates -var nonspatialSpecieTemplate = require('../templates/includes/speciesEditor.pug'); -var spatialSpecieTemplate = require('../templates/includes/spatialSpeciesEditor.pug'); +let speciesTemplate = require('../templates/includes/speciesEditor.pug'); +let spatialSpeciesTemplate = require('../templates/includes/spatialSpeciesEditor.pug'); module.exports = View.extend({ events: { - 'change [data-hook=all-continuous]' : 'getDefaultSpeciesMode', - 'change [data-hook=all-discrete]' : 'getDefaultSpeciesMode', - 'change [data-hook=advanced]' : 'getDefaultSpeciesMode', - 'click [data-hook=add-species]' : 'handleAddSpeciesClick', - 'click [data-hook=save-species]' : 'switchToViewMode', 'click [data-hook=collapse]' : 'changeCollapseButtonText', + 'click [data-hook=add-species]' : 'handleAddSpeciesClick' }, initialize: function (attrs, options) { - var self = this; View.prototype.initialize.apply(this, arguments); - this.baseModel = this.collection.parent; - this.tooltips = Tooltips.speciesEditor + this.spatial = attrs.spatial + this.readOnly = attrs.readOnly ? attrs.readOnly : false; + this.template = this.spatial ? spatialSpeciesTemplate : speciesTemplate; + this.tooltips = Tooltips.speciesEditor; + this.defaultMode = attrs.defaultMode; + let self = this this.collection.on('update-species', function (compID, specie, isNameUpdate, isDefaultMode) { - self.collection.parent.reactions.map(function (reaction) { - reaction.reactants.map(function (reactant) { + self.collection.parent.reactions.forEach(function (reaction) { + reaction.reactants.forEach(function (reactant) { if(reactant.specie.compID === compID) { reactant.specie = specie; } }); - reaction.products.map(function (product) { + reaction.products.forEach(function (product) { if(product.specie.compID === compID) { product.specie = specie; } }); if(isNameUpdate) { reaction.buildSummary(); + if(reaction.selected) { + self.parent.reactionsEditor.setDetailsView(reaction); + } }else if(!isDefaultMode || specie.compID === self.collection.models[self.collection.length-1].compID){ reaction.checkModes(); } }); - self.collection.parent.eventsCollection.map(function (event) { - event.eventAssignments.map(function (assignment) { + self.collection.parent.eventsCollection.forEach(function (event) { + event.eventAssignments.forEach(function (assignment) { if(assignment.variable.compID === compID) { assignment.variable = specie; } @@ -72,138 +71,105 @@ module.exports = View.extend({ event.detailsView.renderEventAssignments(); } }); - self.collection.parent.rules.map(function (rule) { + self.collection.parent.rules.forEach(function (rule) { if(rule.variable.compID === compID) { rule.variable = specie; } }); if(isNameUpdate) { - self.renderSpeciesAdvancedView(); self.parent.renderRulesView(); } }); }, render: function () { - this.template = this.parent.model.is_spatial ? spatialSpecieTemplate : nonspatialSpecieTemplate; View.prototype.render.apply(this, arguments); - var defaultMode = this.collection.parent.defaultMode; - if(defaultMode === "" && !this.collection.parent.is_spatial){ - this.getInitialDefaultSpeciesMode(); + if(this.readOnly) { + $(this.queryByHook('species-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('species-view-tab')).tab('show'); + $(this.queryByHook('edit-species')).removeClass('active'); + $(this.queryByHook('view-species')).addClass('active'); }else{ - var dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'} - $(this.queryByHook(dataHooks[this.collection.parent.defaultMode])).prop('checked', true) - if(defaultMode === "dynamic"){ - $(this.queryByHook('advanced-species')).collapse('show'); - } + this.toggleSpeciesCollectionError(); + this.renderEditSpeciesView(); } - this.renderEditSpeciesView(); - this.renderSpeciesAdvancedView(); - this.toggleSpeciesCollectionError(); + this.renderViewSpeciesView(); }, - update: function () { - }, - updateValid: function (e) { - }, - getInitialDefaultSpeciesMode: function () { - var self = this; - if(document.querySelector('#defaultModeModal')) { - document.querySelector('#defaultModeModal').remove() + addSpecies: function () { + if(this.parent.model.domain.types) { + var types = this.parent.model.domain.types.map(function (type) { + return type.typeID; + }); + types.shift() + }else{ + var types = [] } - let modal = $(modals.renderDefaultModeModalHtml()).modal(); - let continuous = document.querySelector('#defaultModeModal .concentration-btn'); - let discrete = document.querySelector('#defaultModeModal .population-btn'); - let dynamic = document.querySelector('#defaultModeModal .hybrid-btn'); - continuous.addEventListener('click', function (e) { - self.setInitialDefaultMode(modal, "continuous"); - }); - discrete.addEventListener('click', function (e) { - self.setInitialDefaultMode(modal, "discrete"); - }); - dynamic.addEventListener('click', function (e) { - self.setInitialDefaultMode(modal, "dynamic"); + this.collection.addSpecie(types); + this.toggleSpeciesCollectionError() + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + + }); }); }, - setInitialDefaultMode: function (modal, mode) { - var dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'} - modal.modal('hide') - $(this.queryByHook(dataHooks[mode])).prop('checked', true) - this.setAllSpeciesModes(mode) - }, - getDefaultSpeciesMode: function (e) { - var self = this; - this.setAllSpeciesModes(e.target.dataset.name, function (specie) { - self.collection.trigger('update-species', specie.compID, specie, false, true) - }); + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); }, - setAllSpeciesModes: function (defaultMode, cb) { - this.collection.parent.defaultMode = defaultMode; - if(!this.collection.parent.is_spatial) { - if(defaultMode === "continuous") { - $(this.parent.queryByHook("system-volume-container")).collapse("hide") - }else{ - $(this.parent.queryByHook("system-volume-container")).collapse("show") - } - this.collection.map(function (specie) { - specie.mode = defaultMode - cb(specie) - }); - if(defaultMode === "dynamic"){ - this.renderSpeciesAdvancedView() - $(this.queryByHook('advanced-species')).collapse('show'); - } - else{ - this.speciesAdvancedView.views[0].updateInputValidation() - $(this.queryByHook('advanced-species')).collapse('hide'); - } + handleAddSpeciesClick: function (e) { + let self = this; + let defaultMode = this.collection.parent.defaultMode; + if(defaultMode === "" && !this.collection.parent.is_spatial){ + this.parent.getInitialDefaultMode(); + }else{ + this.addSpecies(); } }, renderEditSpeciesView: function () { if(this.editSpeciesView){ this.editSpeciesView.remove(); } - var editSpecieView = !this.collection.parent.is_spatial ? EditNonspatialSpecieView : EditSpatialSpecieView; + let options = {viewOptions: {parent: this}}; this.editSpeciesView = this.renderCollection( this.collection, - editSpecieView, - this.queryByHook('specie-list') + SpecieView, + this.queryByHook('edit-specie-list') ); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); + $('[data-toggle="tooltip"]').tooltip("hide"); + + }); }); }, - renderSpeciesAdvancedView: function () { - if(this.collection.parent.is_spatial) { - return - } - if(this.speciesAdvancedView) { - this.speciesAdvancedView.remove() + renderViewSpeciesView: function () { + if(this.viewSpeciesView){ + this.viewSpeciesView.remove(); } - this.speciesAdvancedView = this.renderCollection(this.collection, EditAdvancedSpecie, this.queryByHook('edit-species-mode')); - }, - handleAddSpeciesClick: function (e) { - var self = this; - var defaultMode = this.collection.parent.defaultMode; - if(defaultMode === "" && !this.collection.parent.is_spatial){ - this.getInitialDefaultSpeciesMode(); + if(this.defaultMode !== "dynamic") { + $(this.queryByHook("species-switching-header")).css("display", "none"); }else{ - this.addSpecies(); + $(this.queryByHook("species-switching-header")).css("display", "block"); } - }, - addSpecies: function () { - if(this.parent.model.domain.types) { - var types = this.parent.model.domain.types.map(function (type) { - return type.typeID; - }); - types.shift() + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("species-annotation-header")).css("display", "none"); }else{ - var types = [] + $(this.queryByHook("species-annotation-header")).css("display", "block"); } - this.collection.addSpecie(types); - this.toggleSpeciesCollectionError() - $(document).ready(function () { + let options = {viewOptions: {parent: this, viewMode: true}}; + this.viewSpeciesView = this.renderCollection( + this.collection, + SpecieView, + this.queryByHook('view-specie-list'), + options + ); + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -212,6 +178,7 @@ module.exports = View.extend({ }); }, toggleSpeciesCollectionError: function () { + if(this.spatial) {return}; let errorMsg = $(this.queryByHook('species-collection-error')) if(this.collection.length <= 0) { errorMsg.addClass('component-invalid') @@ -220,12 +187,5 @@ module.exports = View.extend({ errorMsg.addClass('component-valid') errorMsg.removeClass('component-invalid') } - }, - switchToViewMode: function (e) { - this.parent.modelStateButtons.clickSaveHandler(e); - this.parent.renderSpeciesView(mode="view"); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); } -}); \ No newline at end of file +}); diff --git a/client/views/species-viewer.js b/client/views/species-viewer.js deleted file mode 100644 index 7087d36bca..0000000000 --- a/client/views/species-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewSpecie = require('./view-specie'); -//templates -var template = require('../templates/includes/speciesViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-species]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewSpecie, this.queryByHook('specie-list')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderSpeciesView(); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/timespan-settings.js b/client/views/timespan-settings.js index 22b8cd3fbf..fa5f87cb4b 100644 --- a/client/views/timespan-settings.js +++ b/client/views/timespan-settings.js @@ -31,15 +31,29 @@ module.exports = View.extend({ template: template, events: { 'click [data-hook=collapse]' : 'changeCollapseButtonText', - }, - bindings: { + 'input [data-hook=timestep-size-slider]' : 'viewTimestepValue', + 'change [data-hook=preview-time]' : 'updateViewer', + 'change [data-hook=time-units]' : 'updateViewer', + 'change [data-hook=timestep-size-slider]' : 'setTimestepSize' }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.tooltips = Tooltips.modelSettings }, render: function () { View.prototype.render.apply(this, arguments); + if(this.readOnly) { + $(this.queryByHook('timespan-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('timespan-view-tab')).tab('show'); + $(this.queryByHook('edit-timespan')).removeClass('active'); + $(this.queryByHook('view-timespan')).addClass('active'); + } + $(this.queryByHook("timestep-size-value")).html(this.model.timestepSize); }, update: function (e) { }, @@ -49,6 +63,18 @@ module.exports = View.extend({ changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); }, + setTimestepSize: function (e) { + this.model.timestepSize = Number(e.target.value); + $(this.queryByHook("view-timestep-size")).html(this.model.timestepSize); + }, + updateViewer: function (e) { + $(this.queryByHook("view-end-sim")).html("0 to " + this.model.endSim); + $(this.queryByHook("view-time-step")).html(this.model.timeStep); + }, + viewTimestepValue: function (e) { + let value = e.target.value; + $(this.queryByHook("timestep-size-value")).html(value); + }, subviews: { inputSimEnd: { hook: 'preview-time', diff --git a/client/views/view-event-assignments.js b/client/views/view-event-assignments.js deleted file mode 100644 index 709bab1ce8..0000000000 --- a/client/views/view-event-assignments.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewEventAssignments.pug'); - -module.exports = View.extend({ - template: template, -}); \ No newline at end of file diff --git a/client/views/view-events.js b/client/views/view-events.js deleted file mode 100644 index 0b04864060..0000000000 --- a/client/views/view-events.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var AssignmentsViewer = require('./event-assignments-viewer'); -//templates -var template = require('../templates/includes/viewEvents.pug'); - -module.exports = View.extend({ - template: template, - bindings: { - 'model.initialValue': { - hook: 'event-trigger-init-value', - type: 'booleanAttribute', - name: 'checked', - }, - 'model.persistent': { - hook: 'event-trigger-persistent', - type: 'booleanAttribute', - name: 'checked', - } - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.delay = this.model.delay === "" ? "None" : this.model.delay - }, - render: function () { - View.prototype.render.apply(this, arguments); - var assignmentsViewer = new AssignmentsViewer({ - collection: this.model.eventAssignments - }); - app.registerRenderSubview(this, assignmentsViewer, 'assignment-viewer'); - if(this.model.useValuesFromTriggerTime) { - $(this.queryByHook("trigger-time")).prop('checked', true) - }else{ - $(this.queryByHook("assignment-time")).prop('checked', true) - } - } -}); \ No newline at end of file diff --git a/client/views/view-parameter.js b/client/views/view-parameter.js deleted file mode 100644 index a5ba33c0a4..0000000000 --- a/client/views/view-parameter.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewParameters.pug'); - -module.exports = View.extend({ - template: template, -}); \ No newline at end of file diff --git a/client/views/view-reactions.js b/client/views/view-reactions.js deleted file mode 100644 index be5a565ab5..0000000000 --- a/client/views/view-reactions.js +++ /dev/null @@ -1,48 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -var katex = require('katex'); -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewReactions.pug'); - -module.exports = View.extend({ - template: template, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - let self = this; - this.types = []; - if(this.model.types) { - this.model.types.forEach(function (index) { - let type = self.model.collection.parent.domain.types.get(index, "typeID"); - self.types.push(type.name) - }); - } - this.rate = this.model.reactionType === "custom-propensity" ? - this.model.propensity : this.model.rate.name - }, - render: function () { - View.prototype.render.apply(this, arguments); - katex.render(this.model.summary, this.queryByHook('summary'), { - displayMode: false, - output: 'html', - throwOnError: false - }); - }, -}); \ No newline at end of file diff --git a/client/views/view-rules.js b/client/views/view-rules.js deleted file mode 100644 index c4939f45d4..0000000000 --- a/client/views/view-rules.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewRules.pug'); - -module.exports = View.extend({ - template: template, -}); \ No newline at end of file diff --git a/client/views/view-specie.js b/client/views/view-specie.js deleted file mode 100644 index f0a5ee22be..0000000000 --- a/client/views/view-specie.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewSpecies.pug'); - -module.exports = View.extend({ - template: template, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - let self = this; - this.types = []; - if(this.model.types) { - this.model.types.forEach(function (index) { - let type = self.model.collection.parent.domain.types.get(index, "typeID"); - self.types.push(type.name) - }); - } - this.switchingValWithLabel = this.model.isSwitchTol ? - "Switching Tolerance: " + this.model.switchTol : - "Minimum Value For Switching: " + this.model.switchMin - }, -}); \ No newline at end of file diff --git a/stochss/handlers/models.py b/stochss/handlers/models.py index a55a29a947..b4a4a62755 100644 --- a/stochss/handlers/models.py +++ b/stochss/handlers/models.py @@ -215,7 +215,8 @@ async def get(self): exec_cmd = ['/stochss/stochss/handlers/util/scripts/run_preview.py', f'{path}', f'{outfile}'] if species is not None: - exec_cmd.append(f"{species}") + exec_cmd.insert(1, "--species") + exec_cmd.insert(2, f"{species}") log.debug("Script commands for running a preview: %s", exec_cmd) subprocess.Popen(exec_cmd) resp['Running'] = True diff --git a/stochss/handlers/util/ensemble_simulation.py b/stochss/handlers/util/ensemble_simulation.py index 925922e2c2..2b220e21fb 100644 --- a/stochss/handlers/util/ensemble_simulation.py +++ b/stochss/handlers/util/ensemble_simulation.py @@ -119,7 +119,7 @@ def __update_timespan(self): if "endSim" in keys and "timeStep" in keys: end = self.settings['timespanSettings']['endSim'] step_size = self.settings['timespanSettings']['timeStep'] - self.g_model.timespan(numpy.arange(0, end, step_size)) + self.g_model.timespan(numpy.arange(0, end + step_size, step_size)) def run(self, preview=False, verbose=True): diff --git a/stochss/handlers/util/parameter_sweep.py b/stochss/handlers/util/parameter_sweep.py index 20635e18f6..d1a4a909c2 100644 --- a/stochss/handlers/util/parameter_sweep.py +++ b/stochss/handlers/util/parameter_sweep.py @@ -197,7 +197,7 @@ def configure(self): if "endSim" in keys and "timeStep" in keys: end = self.settings['timespanSettings']['endSim'] step_size = self.settings['timespanSettings']['timeStep'] - self.g_model.timespan(numpy.arange(0, end, step_size)) + self.g_model.timespan(numpy.arange(0, end + step_size, step_size)) kwargs = {"model":self.g_model, "settings":run_settings} parameters = [] for param in self.settings['parameterSweepSettings']['parameters']: diff --git a/stochss/handlers/util/stochss_model.py b/stochss/handlers/util/stochss_model.py index be448194b1..6b617b2385 100644 --- a/stochss/handlers/util/stochss_model.py +++ b/stochss/handlers/util/stochss_model.py @@ -134,7 +134,7 @@ def __convert_model_settings(self): try: end = self.model['modelSettings']['endSim'] step_size = self.model['modelSettings']['timeStep'] - return numpy.arange(0, end, step_size) + return numpy.arange(0, end + step_size, step_size) except KeyError as err: message = "Model settings are not properly formatted or " message += f"are referenced incorrectly: {str(err)}" @@ -380,6 +380,8 @@ def convert_to_spatial(self): if self.model is None: model = self.load() model['is_spatial'] = True + if "timestepSize" not in self.model['modelSettings'].keys(): + self.model['modelSettings']['timestepSize'] = self.model['modelSettings']['timeStep'] if "domain" not in model.keys(): model['domain'] = self.get_model_template()['domain'] for species in model['species']: diff --git a/stochss/handlers/util/stochss_notebook.py b/stochss/handlers/util/stochss_notebook.py index 2a02836cd8..41840f149e 100644 --- a/stochss/handlers/util/stochss_notebook.py +++ b/stochss/handlers/util/stochss_notebook.py @@ -265,7 +265,6 @@ def __create_model_cell(self): self.__create_initial_condition_strings(model=model, pad=pad) self.__create_parameter_strings(model=model, pad=pad) self.__create_reaction_strings(model=model, pad=pad) - self.__create_tspan_string(model=model, pad=pad) else: model = [f"class {self.__get_class_name()}(Model):", " def __init__(self, parameter_values=None):", @@ -277,7 +276,7 @@ def __create_model_cell(self): self.__create_event_strings(model=model, pad=pad) self.__create_rules_strings(model=model, pad=pad) self.__create_function_definition_strings(model=model, pad=pad) - self.__create_tspan_string(model=model, pad=pad) + self.__create_tspan_string(model=model, pad=pad) return nbf.new_code_cell("\n".join(model)) def __create_parameter_strings(self, model, pad): @@ -770,13 +769,14 @@ def __create_stoich_spec_string(self, stoich_species): def __create_tspan_string(self, model, pad): end = self.s_model['modelSettings']['endSim'] - step = self.s_model['modelSettings']['timeStep'] + output_freq = self.s_model['modelSettings']['timeStep'] + step_size = self.s_model['modelSettings']['timestepSize'] tspan = ["", f"{pad}# Timespan"] - ts_str = f'{pad}self.timespan(np.arange(0, {end}, {step})' if self.s_model['is_spatial']: - ts_str += f", timestep_size={step})" + ts_str = f'{pad}self.timespan(np.arange(0, {end + step_size}, {output_freq})' + ts_str += f", timestep_size={step_size})" else: - ts_str += ")" + ts_str = f'{pad}self.timespan(np.arange(0, {end + output_freq}, {output_freq}))' tspan.append(ts_str) model.extend(tspan) diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index e0f444ca0e..b667f7b020 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -169,8 +169,9 @@ def __convert_initial_conditions(self, model): def __convert_model_settings(self, model): try: end = self.model['modelSettings']['endSim'] - step_size = self.model['modelSettings']['timeStep'] - tspan = numpy.arange(0, end, step_size) + output_freq = self.model['modelSettings']['timeStep'] + step_size = self.model['modelSettings']['timestepSize'] + tspan = numpy.arange(0, end + step_size, output_freq) model.timespan(tspan, timestep_size=step_size) except KeyError as err: message = "Spatial model settings are not properly formatted or " @@ -525,6 +526,8 @@ def load(self): self.model['name'] = self.get_name() if not self.model['defaultMode']: self.model['defaultMode'] = "discrete" + if "timestepSize" not in self.model['modelSettings'].keys(): + self.model['modelSettings']['timestepSize'] = self.model['modelSettings']['timeStep'] if "domain" not in self.model.keys() or len(self.model['domain'].keys()) < 6: self.model['domain'] = self.get_model_template()['domain'] elif "static" not in self.model['domain'].keys(): diff --git a/stochss_templates/nonSpatialModelTemplate.json b/stochss_templates/nonSpatialModelTemplate.json index 248896e2cc..514f8cd8a6 100644 --- a/stochss_templates/nonSpatialModelTemplate.json +++ b/stochss_templates/nonSpatialModelTemplate.json @@ -6,7 +6,8 @@ "volume": 1, "modelSettings": { "endSim": 20, - "timeStep": 0.05 + "timeStep": 0.05, + "timestepSize": 1e-5 }, "domain": { "size": null, diff --git a/stochss_templates/workflowSettingsTemplate.json b/stochss_templates/workflowSettingsTemplate.json index 85bfd2de1a..b1a402f656 100644 --- a/stochss_templates/workflowSettingsTemplate.json +++ b/stochss_templates/workflowSettingsTemplate.json @@ -19,6 +19,7 @@ }, "timespanSettings": { "endSim": 20, - "timeStep": 0.05 + "timeStep": 0.05, + "timestepSize": 1e-5 } } \ No newline at end of file