diff --git a/Gruntfile.js b/Gruntfile.js index 0e3fd264..18c8d94a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -76,7 +76,7 @@ module.exports = function (grunt) { stylus: { minerva: { files: [{ - src: ['<%= plugin.minerva.external %>/stylesheets/**/*.styl'], + src: ['<%= plugin.minerva.external %>/stylesheets/main.styl'], dest: '<%= plugin.minerva.static %>/minerva.min.css' }] } @@ -99,6 +99,7 @@ module.exports = function (grunt) { '<%= plugin.minerva.source %>/models/SourceModel.js', '<%= plugin.minerva.source %>/models/**/*.js', '<%= plugin.minerva.source %>/collections/**/*.js', + '<%= plugin.minerva.source %>/views/body/Panel.js', '<%= plugin.minerva.source %>/views/**/*.js' ], dest: '<%= plugin.minerva.static %>/minerva.min.js' @@ -269,7 +270,6 @@ module.exports = function (grunt) { '/clients/web/static/built/libs.min.js', '/' + staticDir + '/geo.ext.min.js', // '/' + rootStaticDir + '/libs.min.js', // libs included in jade template - '/' + staticDir + '/jquery.gridster.js', '/' + staticDir + '/jquery-ui.min.js', '/' + staticDir + '/geo.min.js', '/' + rootStaticDir + '/app.min.js' @@ -306,7 +306,6 @@ module.exports = function (grunt) { '/' + rootStaticLibDir + '/bootstrap/css/bootstrap.min.css', '/' + rootStaticLibDir + '/fontello/css/fontello.css', '/' + rootStaticLibDir + '/fontello/css/animation.css', - '/' + staticDir + '/jquery.gridster.min.css', '/' + staticDir + '/jquery-ui.min.css', '/' + rootStaticDir + '/app.min.css' ]; diff --git a/docs/developer-documentation.rst b/docs/developer-documentation.rst index 9cff0943..52255573 100644 --- a/docs/developer-documentation.rst +++ b/docs/developer-documentation.rst @@ -6,5 +6,6 @@ Developer Documentation installation api-documentation + extending-minerva creating-a-source diff --git a/docs/extending-minerva.rst b/docs/extending-minerva.rst new file mode 100644 index 00000000..e7ab89bf --- /dev/null +++ b/docs/extending-minerva.rst @@ -0,0 +1,77 @@ +Extending Minerva +================= + +Creating a Minerva Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~ +Minerva plugins are **identical** to Girder plugins with the exception that they have a hard dependency on Minerva. This ensures that Minerva will be loaded before your plugin is. + +Minerva utilizes the Girder plugin system, so it will be worthwhile to familiarize yourself with their section on `Plugin Development `_. + + +Below is an example configuration for a Minerva plugin, note the dependency on Minerva: + +.. code-block:: python + + { + "name": "My Minerva Plugin", + "dependencies": ["minerva"] + } + +Your new Minerva plugin should provide no new functionality at this point, and `can be enabled through the administration console `_. + + +Extending the look and feel of Minerva +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While Minerva plugins follow the guidelines provided by Girder's client side, there are additional concerns when writing for Minerva. + +.. note:: Girder has documentation on `extending the client-side application `_ which also applies to Minerva. + +Minerva Panel Views +------------------- +Minerva renders panels in a particular way, as a result there are some guidelines that need be followed when creating your own Panels: + +- Panels should never call render within their initialize function +- Panel views must extend the base Panel view + +.. code-block:: javascript + + minerva.views.CoolNewPanel = minerva.views.Panel.extend({ + +- Panel views need to call their parent initialize method to take advantage of collapsible, removable, configurable panels + +.. code-block:: javascript + + // inside CoolNewPanel's initialize function + minerva.views.Panel.prototype.initialize.apply(this); + + +Configure Minerva's layout +-------------------------- +Taking our example plugin from before, we can alter how Minerva displays different panels. + +Let's pretend our use case of Minerva deems the Jobs Panel useless, and is more focused on the datasets available. In this case one might want to disable the Jobs Panel entirely, and move the datasets panel to the top. + +From your plugin root, create a JavaScript file at ``web_client/js/some-file.js`` and add the following code: + +.. code-block:: javascript + + girder.events.once('m:pre-render-panel-groups', function (sessionView) { + var leftPanelGroup = sessionView.getPanelGroup('m-left-panel-group'); + + // Disable/remove the jobs panel + sessionView.disablePanel('m-jobs-panel'); + + // Move the 'Available Datasets' panel to the top + leftPanelGroup.panelViews.sort(function (a, b) { + if (a.id === 'm-data-panel') { + return -1; + } else if (b.id === 'm-data-panel') { + return 1; + } else { + return 0; + } + }); + }); + +Above we utilize the ``m:pre-render-panel-groups`` event to hook into Minerva before any panels are actually rendered, this gives full control over what the final layout looks like. diff --git a/plugin_tests/minervaSpec.js b/plugin_tests/minervaSpec.js index 4c98cb45..e8d16125 100644 --- a/plugin_tests/minervaSpec.js +++ b/plugin_tests/minervaSpec.js @@ -11,8 +11,8 @@ describe('a test for the minerva plugin application', function () { it('displays the minerva app', function () { waitsFor(function () { - return $('a.m-maps-link:visible').length === 1; - }, 'the maps button to be visible'); + return $('.m-sessions-search-container:visible').length === 1; + }, 'the main panel to be visible'); }); }); diff --git a/server/minerva.mako b/server/minerva.mako index 10929519..8e34db3b 100644 --- a/server/minerva.mako +++ b/server/minerva.mako @@ -18,14 +18,15 @@ href="${staticRoot}/built/plugins/minerva/jquery-ui.min.css"> + % for plugin in pluginCss: % if plugin != 'minerva': % endif % endfor - + diff --git a/server/rest/geocode.py b/server/rest/geocode.py index f1f34f0e..c0d55823 100644 --- a/server/rest/geocode.py +++ b/server/rest/geocode.py @@ -14,7 +14,6 @@ class Geonames(Resource): - """API Endpoint for managing Geonames database.""" _geonames_folder = None diff --git a/web_external/js/models/SessionModel.js b/web_external/js/models/SessionModel.js index f094a815..29518440 100644 --- a/web_external/js/models/SessionModel.js +++ b/web_external/js/models/SessionModel.js @@ -25,6 +25,18 @@ minerva.models.SessionModel = girder.models.ItemModel.extend({ return _.some(this.sessionJsonContents.features, datasetFinder); }, + addLayoutAttributes: function (panelView, attributes) { + if (!_.has(this.sessionJsonContents, 'layout')) { + this.sessionJsonContents.layout = {}; + } + + if (!_.has(_.keys(this.sessionJsonContents.layout, panelView))) { + this.sessionJsonContents.layout[panelView] = attributes; + } else { + _.extend(this.sessionJsonContents.layout[panelView], attributes); + } + }, + addDataset: function (dataset) { // for now just add them to a list // may want to unify caching of geojson file id @@ -49,8 +61,12 @@ minerva.models.SessionModel = girder.models.ItemModel.extend({ // when we create the session item, want to create a session.json file in it var sessionJsonContents = {}; sessionJsonContents.basemap = 'osm'; + sessionJsonContents.basemap_args = { + tileUrl: 'https://{s}.tiles.mapbox.com/v3/datamade.hn83a654/{z}/{x}/{y}.png', + attribution: 'Terms & Feedback' + }; sessionJsonContents.center = {x: -100, y: 36.5}; - sessionJsonContents.zoom = 1; + sessionJsonContents.zoom = 4; sessionJsonContents.features = []; // now save this as session.json as a file in the item this.sessionJsonFile = new girder.models.FileModel(); diff --git a/web_external/js/views/body/AnalysisPanel.js b/web_external/js/views/body/AnalysisPanel.js index 3811fccb..258e2ba0 100644 --- a/web_external/js/views/body/AnalysisPanel.js +++ b/web_external/js/views/body/AnalysisPanel.js @@ -10,7 +10,7 @@ return name + 'Widget'; }; - minerva.views.AnalysisPanel = minerva.View.extend({ + minerva.views.AnalysisPanel = minerva.views.Panel.extend({ events: { 'click .m-attempt-analysis': 'attemptAnalysis' @@ -57,9 +57,9 @@ }, initialize: function (settings) { - this.collection = settings.collection; - this.datasetCollection = settings.datasetCollection; - this.sourceCollection = settings.sourceCollection; + this.collection = settings.session.analysisCollection; + this.datasetCollection = settings.session.datasetsCollection; + this.sourceCollection = settings.session.sourceCollection; this.listenTo(this.collection, 'g:changed', function () { console.log('AP g:changed'); this.render(); @@ -79,8 +79,8 @@ console.log('AP remove'); this.render(); }, this); - // this.render(); + minerva.views.Panel.prototype.initialize.apply(this); }, render: function () { diff --git a/web_external/js/views/body/DataPanel.js b/web_external/js/views/body/DataPanel.js index 72585886..0c65570f 100644 --- a/web_external/js/views/body/DataPanel.js +++ b/web_external/js/views/body/DataPanel.js @@ -1,4 +1,4 @@ -minerva.views.DataPanel = minerva.View.extend({ +minerva.views.DataPanel = minerva.views.Panel.extend({ events: { 'click .add-dataset-to-session': 'addDatasetToSessionEvent', 'click .delete-dataset': 'deleteDatasetEvent', @@ -82,7 +82,7 @@ minerva.views.DataPanel = minerva.View.extend({ }, initialize: function (settings) { - this.collection = settings.collection; + this.collection = settings.session.datasetsCollection; this.listenTo(this.collection, 'g:changed', function () { this.render(); }, this).listenTo(this.collection, 'change', function () { @@ -104,6 +104,7 @@ minerva.views.DataPanel = minerva.View.extend({ } }, this)); + minerva.views.Panel.prototype.initialize.apply(this); }, render: function () { diff --git a/web_external/js/views/body/JobsPanel.js b/web_external/js/views/body/JobsPanel.js index 1acbfb50..48f7aa9f 100644 --- a/web_external/js/views/body/JobsPanel.js +++ b/web_external/js/views/body/JobsPanel.js @@ -1,4 +1,4 @@ -minerva.views.JobsPanel = minerva.View.extend({ +minerva.views.JobsPanel = minerva.views.Panel.extend({ initialize: function () { var columnEnum = girder.views.jobs_JobListWidget.prototype.columnEnum; @@ -28,6 +28,7 @@ minerva.views.JobsPanel = minerva.View.extend({ this.jobListWidget.collection.fetch({}, true); }, this); + minerva.views.Panel.prototype.initialize.apply(this); }, render: function () { diff --git a/web_external/js/views/body/LayersPanel.js b/web_external/js/views/body/LayersPanel.js index 9574a070..c2c022ce 100644 --- a/web_external/js/views/body/LayersPanel.js +++ b/web_external/js/views/body/LayersPanel.js @@ -1,4 +1,4 @@ -minerva.views.LayersPanel = minerva.View.extend({ +minerva.views.LayersPanel = minerva.views.Panel.extend({ events: { 'click .m-remove-dataset-from-layer': 'removeDatasetEvent', @@ -81,7 +81,7 @@ minerva.views.LayersPanel = minerva.View.extend({ initialize: function (settings) { settings = settings || {}; - this.collection = settings.collection; + this.collection = settings.session.datasetsCollection; this.layersOrderOptions = [ {'title': 'move up', 'method': 'moveUp', 'class': 'up'}, {'title': 'move down', 'method': 'moveDown', 'class': 'down'}, @@ -92,6 +92,8 @@ minerva.views.LayersPanel = minerva.View.extend({ this.listenTo(this.collection, 'change:displayed change:order', function () { this.render(); }, this); + + minerva.views.Panel.prototype.initialize.apply(this); }, render: function () { diff --git a/web_external/js/views/body/MapPanel.js b/web_external/js/views/body/MapPanel.js index b0d88b7f..65bcf160 100644 --- a/web_external/js/views/body/MapPanel.js +++ b/web_external/js/views/body/MapPanel.js @@ -1,4 +1,4 @@ -minerva.views.MapPanel = minerva.View.extend({ +minerva.views.MapPanel = minerva.views.Panel.extend({ events: { 'click .m-save-current-baselayer': function () { @@ -104,12 +104,13 @@ minerva.views.MapPanel = minerva.View.extend({ session: this.model, parentView: this }); - this.map.featureInfoWidget.setElement($('.mapPanel')).render(); + this.map.featureInfoWidget.setElement($('#m-map-panel')).render(); this.map.geoOn(geo.event.mouseclick, function (evt) { this.featureInfoWidget.content = ''; this.featureInfoWidget.callInfo(0, evt.geo); }); } + this.uiLayer = this.map.createLayer('ui'); this.map.draw(); } else { // Assume the dataset provides a reader, so load the data @@ -127,7 +128,6 @@ minerva.views.MapPanel = minerva.View.extend({ reader.read(dataset.fileData, _.bind(function () { // Add the UI slider back this.uiLayer = this.map.createLayer('ui'); - this.uiLayer.createWidget('slider'); this.map.draw(); }, this)); }, this); @@ -161,7 +161,7 @@ minerva.views.MapPanel = minerva.View.extend({ }, initialize: function (settings) { - this.session = settings.session; + this.session = settings.session.model; this.listenTo(this.session, 'm:mapUpdated', function () { // TODO for now only dealing with center if (this.map) { @@ -172,7 +172,7 @@ minerva.views.MapPanel = minerva.View.extend({ this.datasetLayers = {}; this.legendWidget = {}; - this.collection = settings.collection; + this.collection = settings.session.datasetsCollection; this.listenTo(this.collection, 'change:displayed', function (dataset) { // There is a slight danger of a user trying to add a dataset // to a session while the map is not yet created. If the map isn't @@ -198,12 +198,14 @@ minerva.views.MapPanel = minerva.View.extend({ this.changeLayerZIndex(dataset); } }, this); + + minerva.views.Panel.prototype.initialize.apply(this); }, renderMap: function () { if (!this.map) { this.map = geo.map({ - node: '.mapPanelMap', + node: '.m-map-panel-map', center: this.session.sessionJsonContents.center, zoom: this.session.sessionJsonContents.zoom, interactor: geo.mapInteractor({ @@ -214,9 +216,10 @@ minerva.views.MapPanel = minerva.View.extend({ } }) }); - this.map.createLayer(this.session.sessionJsonContents.basemap); + this.map.createLayer(this.session.sessionJsonContents.basemap, + _.has(this.session.sessionJsonContents, 'basemap_args') ? + this.session.sessionJsonContents.basemap_args : {}); this.uiLayer = this.map.createLayer('ui'); - this.uiLayer.createWidget('slider'); this.mapCreated = true; _.each(this.collection.models, function (dataset) { if (dataset.get('displayed')) { diff --git a/web_external/js/views/body/Panel.js b/web_external/js/views/body/Panel.js new file mode 100644 index 00000000..504a45a3 --- /dev/null +++ b/web_external/js/views/body/Panel.js @@ -0,0 +1,57 @@ +minerva.views.Panel = minerva.View.extend({ + /** + * The panel view isn't meant to be instantiated on it's own. + **/ + events: { + 'click .icon-cancel': 'removePanel', + 'show.bs.collapse': 'expandPanel', + 'hide.bs.collapse': 'collapsePanel' + }, + + initialize: function () { + _.extend(this.events, minerva.views.Panel.prototype.events); + }, + + getSessionView: function () { + return this.parentView.parentView; + }, + + getSessionModel: function () { + return this.getSessionView().model; + }, + + /** + * Upon confirmation, physically removes the DOM element while + * storing the change in the session attributes. This also + * enables the save button. + **/ + removePanel: function () { + if (confirm('Are you sure you want to remove this panel?')) { + this.getSessionView().disablePanel(this.el.id); + this.getSessionView()._enableSave(); + this.remove(); + minerva.View.prototype.remove.call(this); + } + }, + + /** + * Handles the aftermath of a panel being expanded. Meaning it changes the + * expand/collapse icon, marks the state of the panel in the session attributes, + * and enables the user to save the change. + **/ + expandPanel: function (e) { + $(e.currentTarget).find('i.icon-down-open').attr('class', 'icon-up-open'); + this.getSessionModel().addLayoutAttributes(this.el.id, { + collapsed: false + }); + this.getSessionView()._enableSave(); + }, + + collapsePanel: function (e) { + $(e.currentTarget).find('i.icon-up-open').attr('class', 'icon-down-open'); + this.getSessionModel().addLayoutAttributes(this.el.id, { + collapsed: true + }); + this.getSessionView()._enableSave(); + } +}); diff --git a/web_external/js/views/body/PanelGroup.js b/web_external/js/views/body/PanelGroup.js new file mode 100644 index 00000000..7b5fb53a --- /dev/null +++ b/web_external/js/views/body/PanelGroup.js @@ -0,0 +1,21 @@ +minerva.views.PanelGroup = minerva.View.extend({ + initialize: function (settings) { + this.id = settings.id; + this.panelViews = settings.panelViews || []; + }, + + render: function () { + // Render each of our panels + _.each(this.panelViews, function (panelViewSpec) { + var panelView = new panelViewSpec.view({ + parentView: this, + session: this.parentView + }); + + this.$el.append('
'); + panelView.setElement(this.$('#' + panelViewSpec.id)).render(); + }, this); + + return this; + } +}); diff --git a/web_external/js/views/body/SessionView.js b/web_external/js/views/body/SessionView.js index fe58ae87..b2b9e899 100644 --- a/web_external/js/views/body/SessionView.js +++ b/web_external/js/views/body/SessionView.js @@ -65,6 +65,45 @@ minerva.views.SessionView = minerva.View.extend({ this.$('.m-save-session-button').removeClass('btn-success'); }, + getEnabledPanelGroups: function () { + if (!_.has(this.model.sessionJsonContents, 'layout')) { + return this.layout.panelGroups; + } + + return _.filter(this.layout.panelGroups, function (panelGroup) { + return !(_.has(this.model.sessionJsonContents.layout, panelGroup.id) && + _.has(this.model.sessionJsonContents.layout[panelGroup.id], 'disabled') && + this.model.sessionJsonContents.layout[panelGroup.id].disabled === true); + }, this); + }, + + getEnabledPanelViews: function (panelGroup) { + if (!_.has(this.model.sessionJsonContents, 'layout')) { + return panelGroup.panelViews; + } + + return _.filter(panelGroup.panelViews, function (panelView) { + return !(_.has(this.model.sessionJsonContents.layout, panelView.id) && + _.has(this.model.sessionJsonContents.layout[panelView.id], 'disabled') && + this.model.sessionJsonContents.layout[panelView.id].disabled === true); + }, this); + }, + + getPanelGroup: function (id) { + return _.find(this.layout.panelGroups, function (panelGroup) { + return panelGroup.id === id; + }); + }, + + /** + * Disables the panel by way of modifying the session json. + */ + disablePanel: function (id) { + this.model.addLayoutAttributes(id, { + disabled: true + }); + }, + initialize: function (settings) { this.model = settings.session; this.datasetsCollection = settings.datasetsCollection; @@ -93,40 +132,46 @@ minerva.views.SessionView = minerva.View.extend({ this._disableSave(); }); - this.dataPanel = new minerva.views.DataPanel({ - session: this.model, - collection: this.datasetsCollection, - parentView: this - }); - - this.mapPanel = new minerva.views.MapPanel({ - session: this.model, - collection: this.datasetsCollection, - parentView: this - }); - - this.layersPanel = new minerva.views.LayersPanel({ - collection: this.datasetsCollection, - parentView: this - }); - - this.jobsPanel = new minerva.views.JobsPanel({ - parentView: this - }); - - this.analysisPanel = new minerva.views.AnalysisPanel({ - parentView: this, - collection: this.analysisCollection, - datasetCollection: this.datasetCollection, - sourceCollection: this.sourceCollection - }); - - this.sourcePanel = new minerva.views.SourcePanel({ - session: this.model, - sourceCollection: this.sourceCollection, - datasetCollection: this.datasetsCollection, - parentView: this - }); + this.layout = { + panelGroups: [ + { + id: 'm-main-panel-group', + view: minerva.views.PanelGroup, + panelViews: [ + { + id: 'm-map-panel', + view: minerva.views.MapPanel + } + ] + }, + { + id: 'm-left-panel-group', + view: minerva.views.PanelGroup, + panelViews: [ + { + id: 'm-analysis-panel', + view: minerva.views.AnalysisPanel + }, + { + id: 'm-source-panel', + view: minerva.views.SourcePanel + }, + { + id: 'm-data-panel', + view: minerva.views.DataPanel + }, + { + id: 'm-layer-panel', + view: minerva.views.LayersPanel + }, + { + id: 'm-jobs-panel', + view: minerva.views.JobsPanel + } + ] + } + ] + }; this.render(); }, @@ -142,25 +187,28 @@ minerva.views.SessionView = minerva.View.extend({ girder: girder })); - this.$('.gridster > ul').gridster({ - widget_margins: [10, 10], - widget_base_dimensions: [210, 210], - draggable: { - handle: '.panelTitle' - }, - resize: { - enabled: true, - min_size: [1, 1] - } - }); - - this.dataPanel.setElement(this.$('.dataPanel')).render(); - this.mapPanel.setElement(this.$('.mapPanel')).render(); - this.layersPanel.setElement(this.$('.layersPanel')).render(); - this.jobsPanel.setElement(this.$('.jobsPanel')).render(); - this.analysisPanel.setElement(this.$('.analysisPanel')).render(); - this.sourcePanel.setElement(this.$('.m-source-panel')).render(); - + // Render each panel group, which is responsible for rendering + // each panel view + girder.events.trigger('m:pre-render-panel-groups', this); + _.each(this.getEnabledPanelGroups(), function (panelGroupSpec) { + var panelGroup = new panelGroupSpec.view({ + parentView: this, + session: this.model, + panelViews: this.getEnabledPanelViews(panelGroupSpec) + }); + + this.$('#m-panel-groups').append('
'); + panelGroup.setElement(this.$('#' + panelGroupSpec.id)).render(); + }, this); + + // Restore state of collapsed panels + if (_.has(this.model.sessionJsonContents, 'layout')) { + _.each(this.model.sessionJsonContents.layout, function (panelView, panelViewId) { + if (_.has(panelView, 'collapsed') && panelView.collapsed === true) { + $('#' + panelViewId).find('i.icon-up-open').trigger('click'); + } + }, this); + } }, this)); return this; diff --git a/web_external/js/views/body/SourcePanel.js b/web_external/js/views/body/SourcePanel.js index 22496318..096eba66 100644 --- a/web_external/js/views/body/SourcePanel.js +++ b/web_external/js/views/body/SourcePanel.js @@ -1,4 +1,4 @@ -minerva.views.SourcePanel = minerva.View.extend({ +minerva.views.SourcePanel = minerva.views.Panel.extend({ events: { 'click .m-add-source': 'addSourceDialog', @@ -105,9 +105,9 @@ minerva.views.SourcePanel = minerva.View.extend({ }, initialize: function (settings) { - this.session = settings.session; - this.sourceCollection = settings.sourceCollection; - this.datasetCollection = settings.datasetCollection; + this.session = settings.session.model; + this.sourceCollection = settings.session.sourceCollection; + this.datasetCollection = settings.session.datasetsCollection; // TODO similar to addSourceWidget, // would be nice if new source types could register themselves, @@ -152,6 +152,8 @@ minerva.views.SourcePanel = minerva.View.extend({ this.sourceCollection.fetch({}, true); } }, this)); + + minerva.views.Panel.prototype.initialize.apply(this); }, render: function () { diff --git a/web_external/js/views/layout/HeaderView.js b/web_external/js/views/layout/HeaderView.js index 858a8406..618a2411 100644 --- a/web_external/js/views/layout/HeaderView.js +++ b/web_external/js/views/layout/HeaderView.js @@ -3,7 +3,6 @@ minerva.views.LayoutHeaderView = minerva.View.extend({ }, render: function () { - this.$el.html(minerva.templates.layoutHeader()); this.$('a[title]').tooltip({ placement: 'bottom', diff --git a/web_external/js/views/widgets/WMSFeatureInfoWidget.js b/web_external/js/views/widgets/WMSFeatureInfoWidget.js index d3a7e15f..197a7cb8 100644 --- a/web_external/js/views/widgets/WMSFeatureInfoWidget.js +++ b/web_external/js/views/widgets/WMSFeatureInfoWidget.js @@ -62,7 +62,7 @@ minerva.views.WmsFeatureInfoWidget = minerva.View.extend({ this.version = settings.version; this.layers = settings.layers; this.callback = settings.callback; - this.el = $('.mapPanel'); + this.el = $('#m-map-panel'); this.content = ''; this.fixedParams = 'REQUEST=GetFeatureInfo&' + 'EXCEPTIONS=application%2Fvnd.ogc.se_xml&' + diff --git a/web_external/stylesheets/body/analysisPanel.styl b/web_external/stylesheets/body/analysisPanel.styl index f56b0be9..7551854a 100644 --- a/web_external/stylesheets/body/analysisPanel.styl +++ b/web_external/stylesheets/body/analysisPanel.styl @@ -1,17 +1,10 @@ -.m-analysisPanelContainer - - .m-analysisList - - .m-analyses - - .m-analysis - - padding-left 0px - text-align left - border 0 - margin 2px - padding 2px - - .icon-play-circled - - float left +.m-analyses + .m-analysis + padding-left 0px + text-align left + border 0 + margin 2px + padding 2px + + .icon-play-circled + float left diff --git a/web_external/stylesheets/body/dataPanel.styl b/web_external/stylesheets/body/dataPanel.styl index 3759f77b..395165a2 100644 --- a/web_external/stylesheets/body/dataPanel.styl +++ b/web_external/stylesheets/body/dataPanel.styl @@ -1,42 +1,25 @@ -.dataPanelContainer - text-align center - - .savedDatasets - margin-top 20px - - .savedDatasetsTitle - margin-bottom 20px - - .savedDataList - - .dataset - padding-left 0px - text-align left - border 0 - margin 2px - padding 2px - - i - color #444 - - .icon-disabled - color #f0f0f0 - - .icon-enabled:hover - color #66a - cursor pointer - - .icon-info-circled - float right - - .icon-right-circled - float left - - .icon-trash - float right - - .icon-eye - float left - - .csv-mapping - float right +#m-data-panel + text-align center + + .dataset + padding-left 0px + text-align left + border 0 + margin 2px + padding 2px + + i + color #444 + .icon-disabled + color #f0f0f0 + .icon-enabled:hover + color #66a + cursor pointer + .icon-info-circled + float right + .icon-right-circled + float left + .icon-trash + float right + .icon-eye + float left diff --git a/web_external/stylesheets/body/frontPage.styl b/web_external/stylesheets/body/frontPage.styl deleted file mode 100644 index 1fd7b760..00000000 --- a/web_external/stylesheets/body/frontPage.styl +++ /dev/null @@ -1,4 +0,0 @@ -.m-map - width 600px - height 805px - overflow hidden diff --git a/web_external/stylesheets/body/mapPanel.styl b/web_external/stylesheets/body/mapPanel.styl index 49045a22..d2e9d300 100644 --- a/web_external/stylesheets/body/mapPanel.styl +++ b/web_external/stylesheets/body/mapPanel.styl @@ -1,18 +1,10 @@ -.mapPanel > .panelTitle - margin 0px +#m-map-panel + height 100% !important + width 100% !important - .m-save-current-baselayer - float right - - .m-save-current-baselayer:hover - color #66a - cursor pointer - - -.mapPanelMap - width 670px - height 860px - position absolute +.m-map-panel-map + width 100% + height 100% .m-map-legend-container position: absolute diff --git a/web_external/stylesheets/body/panelGroups.styl b/web_external/stylesheets/body/panelGroups.styl new file mode 100644 index 00000000..2035ba80 --- /dev/null +++ b/web_external/stylesheets/body/panelGroups.styl @@ -0,0 +1,36 @@ +#m-left-panel-group + float left + width 280px + position absolute + z-index 999 + margin-left 5px + top 0 + +#m-main-panel-group + height 100% + width 100% + position absolute + top 0 + +#m-panel-groups + height "calc(100% - %s)" % $headerHeight + width 100% + position relative + +// @todo assumes all panelgroups are divs, and all panel views are encapsulated in a div +#m-panel-groups > div > div + background-color #ffffff + opacity .95 + margin-bottom 2px + border-radius 3px + height auto + border 1px solid #d6d6d6 + +.m-panel-content + margin 5px + +.m-panel-content:empty + margin 0px + +.m-panel-title > span + float right diff --git a/web_external/stylesheets/body/sessionPage.styl b/web_external/stylesheets/body/sessionPage.styl index 5f3c8c0d..150b3f2f 100644 --- a/web_external/stylesheets/body/sessionPage.styl +++ b/web_external/stylesheets/body/sessionPage.styl @@ -1,5 +1,5 @@ .m-session-header - border-bottom 1px solid #ddd + padding 12px 15px margin-bottom 10px padding-bottom 5px min-height 40px @@ -18,20 +18,9 @@ .m-save-session-button margin-right 10px -.gridster - - > ul > li - - border 0px - padding 0px - margin 5px - list-style-type none - background-color #f8f8f8 - -.panelTitle - font-weight bold - text-align center - background-color #cfcfcf - margin-bottom 20px - cursor move - padding 10px 5px +.m-panel-title + font-weight bold + text-align left + background-color #f0f0f0 + cursor pointer + padding 5px 5px diff --git a/web_external/stylesheets/body/sourcePanel.styl b/web_external/stylesheets/body/sourcePanel.styl index ecf72353..a2c8586f 100644 --- a/web_external/stylesheets/body/sourcePanel.styl +++ b/web_external/stylesheets/body/sourcePanel.styl @@ -1,21 +1,2 @@ -.m-source-panel - - .m-source - margin 2px - padding 2px - - .m-icon-enabled:hover - color #66a - cursor pointer - - .m-add-source - float right - - .m-display-source-action - float: left - - .m-delete-source - float right - - .m-icon-info - float right +#m-source-panel + display block diff --git a/web_external/stylesheets/layout.styl b/web_external/stylesheets/layout.styl index 9fb53137..7593eef5 100644 --- a/web_external/stylesheets/layout.styl +++ b/web_external/stylesheets/layout.styl @@ -8,7 +8,6 @@ $portraitHeight = 38px padding-right 20px padding-left 20px background-color rgba(255, 255, 255, 0.88) - border-bottom 1px solid #e6e6e6 transition background-color 0.4s ease-in-out position fixed top 0 @@ -69,7 +68,11 @@ $portraitHeight = 38px #g-app-body-container margin 0 - margin-top $headerHeight min-height 0 - padding 12px 15px + height 100% +body + height 100% + +html + height 100% diff --git a/web_external/stylesheets/main.styl b/web_external/stylesheets/main.styl new file mode 100644 index 00000000..8622871e --- /dev/null +++ b/web_external/stylesheets/main.styl @@ -0,0 +1,3 @@ +// main stylus file that imports all other files +@import 'layout.styl' +@import '*/*.styl' diff --git a/web_external/templates/body/analysisPanel.jade b/web_external/templates/body/analysisPanel.jade index d61b93d9..1430cb1d 100644 --- a/web_external/templates/body/analysisPanel.jade +++ b/web_external/templates/body/analysisPanel.jade @@ -1,11 +1,12 @@ -.m-analysisPanelContainer - .panelTitle - | Analysis Panel - .m-analysisList - .m-analyses - each analysis in analyses - if analysis.metadata() - .m-analysis= analysis.get('name') - - var attributes = {'m-analysis-id': analysis.get('_id')} - - var classes = 'icon-play-circled m-attempt-analysis' - i(class=classes)&attributes(attributes) +include ../layout/panelMixins.jade + ++panel-title('Analyses', 'm-analysis-panel') + ++panel-content('collapse in') + .m-analyses + each analysis in analyses + if analysis.metadata() + .m-analysis= analysis.get('name') + - var attributes = {'m-analysis-id': analysis.get('_id')} + - var classes = 'icon-play-circled m-attempt-analysis' + i(class=classes)&attributes(attributes) diff --git a/web_external/templates/body/dataPanel.jade b/web_external/templates/body/dataPanel.jade index 0bcc2e85..e6fcfa99 100644 --- a/web_external/templates/body/dataPanel.jade +++ b/web_external/templates/body/dataPanel.jade @@ -1,30 +1,29 @@ -.dataPanelContainer - .panelTitle - | Available Datasets - .savedDatasets - .savedDatasetsTitle - .savedDataList - .datasets - each dataset in datasets - if dataset.metadata() - .dataset - span(title=dataset.get('name'))= dataset.get('name').length > 13 ? dataset.get('name').slice(0,13) + "..." : dataset.get('name') - - var attributes = {'m-dataset-id': dataset.get('_id')} - if dataset.isRenderable() - //- Eye icon to move dataset into current session - - var classes = (dataset.get('displayed') ? 'icon-eye icon-disabled dataset-in-session' : 'icon-eye icon-enabled add-dataset-to-session') - i(class=classes)&attributes(attributes) - //- trash icon to delete dataset - - var classes = (dataset.get('displayed') ? 'icon-trash delete-dataset icon-disabled dataset-in-session' : 'icon-trash delete-dataset icon-enabled') - i(class=classes)&attributes(attributes) - //- info icon for minerva metadata display - i.icon-info-circled.icon-enabled.dataset-info&attributes(attributes) - if dataset.get('meta').minerva.original_type === 'csv' || dataset.get('meta').minerva.original_type === 'json' || (dataset.get('meta').minerva.original_type === 'mongo' && !('spatial_field' in dataset.get('meta').minerva)) - //- table icon for mapping columns of csv/json - - var displayed = dataset.get('displayed') - if !displayed && (dataset.get('meta').minerva.original_type === 'json' || dataset.get('meta').minerva.original_type === 'mongo') && (!dataset.get('meta').minerva.json_row) - //- when there is no json data, disable the table icon, as if the dataset was displayed - - displayed = true - - var classes = (displayed ? 'icon-table csv-mapping icon-disabled dataset-in-session' : 'icon-table csv-mapping icon-enabled') - i(class=classes)&attributes(attributes) +include ../layout/panelMixins.jade ++panel-title('Available Datasets', 'm-data-panel') + +if datasets + +panel-content('collapse in') + .datasets + each dataset in datasets + if dataset.metadata() + .dataset + span(title=dataset.get('name'))= dataset.get('name').length > 13 ? dataset.get('name').slice(0,13) + "..." : dataset.get('name') + - var attributes = {'m-dataset-id': dataset.get('_id')} + if dataset.isRenderable() + //- Eye icon to move dataset into current session + - var classes = (dataset.get('displayed') ? 'icon-eye icon-disabled dataset-in-session' : 'icon-eye icon-enabled add-dataset-to-session') + i(class=classes)&attributes(attributes) + //- trash icon to delete dataset + - var classes = (dataset.get('displayed') ? 'icon-trash delete-dataset icon-disabled dataset-in-session' : 'icon-trash delete-dataset icon-enabled') + i(class=classes)&attributes(attributes) + //- info icon for minerva metadata display + i.icon-info-circled.icon-enabled.dataset-info&attributes(attributes) + if dataset.get('meta').minerva.original_type === 'csv' || dataset.get('meta').minerva.original_type === 'json' || (dataset.get('meta').minerva.original_type === 'mongo' && !('spatial_field' in dataset.get('meta').minerva)) + //- table icon for mapping columns of csv/json + - var displayed = dataset.get('displayed') + if !displayed && (dataset.get('meta').minerva.original_type === 'json' || dataset.get('meta').minerva.original_type === 'mongo') && (!dataset.get('meta').minerva.json_row) + //- when there is no json data, disable the table icon, as if the dataset was displayed + - displayed = true + - var classes = (displayed ? 'icon-table csv-mapping icon-disabled dataset-in-session' : 'icon-table csv-mapping icon-enabled') + i(class=classes)&attributes(attributes) diff --git a/web_external/templates/body/jobsPanel.jade b/web_external/templates/body/jobsPanel.jade index a7d0a2a3..2d0471dd 100644 --- a/web_external/templates/body/jobsPanel.jade +++ b/web_external/templates/body/jobsPanel.jade @@ -1,4 +1,6 @@ -.m-jobsPanelContainer - .panelTitle - | Jobs +include ../layout/panelMixins.jade + ++panel-title('Jobs', 'm-jobs-panel') + ++panel-content('collapse in') .m-jobsListContainer diff --git a/web_external/templates/body/layersPanel.jade b/web_external/templates/body/layersPanel.jade index 0ecdc649..b2014f20 100644 --- a/web_external/templates/body/layersPanel.jade +++ b/web_external/templates/body/layersPanel.jade @@ -1,19 +1,21 @@ -.layersPanelContainer - .panelTitle - | Session Layers - .layersList - ul.datasets - each dataset, index in datasets - li.dataset= dataset.get('name') - i.icon-eye-off.m-remove-dataset-from-layer&attributes({'m-dataset-id': dataset.get('_id')}) - .m-layer-control-container - each option in layersOrderOptions - if ((index === 0 && option.class.indexOf('up') > -1) || (index === datasets.length - 1 && option.class.indexOf('down') > -1 )) - - var disable = 'm-disable-text' - else - - var disable = '' - i(title='#{option.title}' class='icon-angle-#{option.class} m-order-layer #{disable}')&attributes({'m-dataset-id': dataset.get('_id'), 'm-order-option': option.method}) - if dataset.getDatasetType() === 'wms' - .m-layer-control-container - i.icon-ajust - input.m-opacity-range&attributes({'m-dataset-id': dataset.get('_id')})(type='range', min=0, max=1, step=0.01, value=dataset.get('opacity')) +include ../layout/panelMixins.jade + ++panel-title('Session Layers', 'm-layer-panel') + ++panel-content('collapse in') + .layersList + ul.datasets + each dataset, index in datasets + li.dataset= dataset.get('name') + i.icon-eye-off.m-remove-dataset-from-layer&attributes({'m-dataset-id': dataset.get('_id')}) + .m-layer-control-container + each option in layersOrderOptions + if ((index === 0 && option.class.indexOf('up') > -1) || (index === datasets.length - 1 && option.class.indexOf('down') > -1 )) + - var disable = 'm-disable-text' + else + - var disable = '' + i(title='#{option.title}' class='icon-angle-#{option.class} m-order-layer #{disable}')&attributes({'m-dataset-id': dataset.get('_id'), 'm-order-option': option.method}) + if dataset.getDatasetType() === 'wms' + .m-layer-control-container + i.icon-ajust + input.m-opacity-range&attributes({'m-dataset-id': dataset.get('_id')})(type='range', min=0, max=1, step=0.01, value=dataset.get('opacity')) diff --git a/web_external/templates/body/mapPanel.jade b/web_external/templates/body/mapPanel.jade index e6f42942..c1bb781d 100644 --- a/web_external/templates/body/mapPanel.jade +++ b/web_external/templates/body/mapPanel.jade @@ -1,5 +1 @@ -.panelTitle - | Current Map - .m-map-legend-container - i.m-save-current-baselayer.icon-target(title="Save current baselayer settings") -.mapPanelMap +.m-map-panel-map diff --git a/web_external/templates/body/sessionPage.jade b/web_external/templates/body/sessionPage.jade index 3fd4a9ae..635bfcc7 100644 --- a/web_external/templates/body/sessionPage.jade +++ b/web_external/templates/body/sessionPage.jade @@ -1,5 +1,4 @@ .m-session-header - .m-session-header-buttons if (accessLevel >= girder.AccessType.WRITE) button.m-save-session-button.btn.bn-small(title="Save session", disabled="disabled") @@ -32,11 +31,4 @@ .m-session-title= session.get('name') .m-session-description= session.get('description') -.gridster - ul - li.m-source-panel&attributes({'data-row': '1', 'data-col': '1', 'data-sizex': '1', 'data-sizey': '2'}) - li.analysisPanel&attributes({'data-row': '1', 'data-col': '2', 'data-sizex': '1', 'data-sizey': '2'}) - li.mapPanel&attributes({'data-row': '1', 'data-col': '3', 'data-sizex': '3', 'data-sizey': '4'}) - li.dataPanel&attributes({'data-row': '3', 'data-col': '1', 'data-sizex': '1', 'data-sizey': '2'}) - li.layersPanel&attributes({'data-row': '3', 'data-col': '2', 'data-sizex': '1', 'data-sizey': '2'}) - li.jobsPanel&attributes({'data-row': '5', 'data-col': '2', 'data-sizex': '1', 'data-sizey': '2'}) +#m-panel-groups diff --git a/web_external/templates/body/sourcePanel.jade b/web_external/templates/body/sourcePanel.jade index a63da9a0..de2c03fb 100644 --- a/web_external/templates/body/sourcePanel.jade +++ b/web_external/templates/body/sourcePanel.jade @@ -1,21 +1,24 @@ -.m-source-panel-container - .panelTitle - | Sources - i.m-add-source.m-icon-enabled.icon-plus-squared(title="Add new source") - .m-sourceList +include ../layout/panelMixins.jade + ++panel-title('Sources', 'm-source-panel') + i.m-add-source.m-icon-enabled.icon-plus-squared(title="Add new source") + +if sources + +panel-content('collapse in') .m-sources - each source in sources - if source.metadata() - .m-source - span(title=source.get('name'))= source.get('name').length > 13 ? source.get('name').slice(0,13) + "..." : source.get('name') - - var attributes = {'cid': source.cid} - - var sourceType = sourceTypes[source.getSourceType()] + each source in sources + if source.metadata() + .m-source + span(title=source.get('name'))= source.get('name').length > 13 ? source.get('name').slice(0,13) + "..." : source.get('name') + - var attributes = {'cid': source.cid} + - var sourceType = sourceTypes[source.getSourceType()] - if sourceType - - var classes = 'm-icon-enabled m-display-source-action ' + sourceType.icon + ' ' + sourceType.action - i(class=classes)&attributes(attributes) - - classes = 'icon-trash m-icon-enabled m-delete-source' - i(title='delete source', class=classes)&attributes(attributes) - //- info icon for minerva metadata display - i(title='display source info').icon-info-circled.m-icon-info.m-icon-enabled&attributes(attributes) -F \ No newline at end of file + span(style='float:right') + if sourceType + - var classes = 'm-icon-enabled m-display-source-action ' + sourceType.icon + ' ' + sourceType.action + i(class=classes)&attributes(attributes) + //- trash icon to delete source + - classes = 'icon-trash m-icon-enabled m-delete-source' + i(title='delete source', class=classes)&attributes(attributes) + //- info icon for minerva metadata display + i(title='display source info').icon-info-circled.m-icon-info.m-icon-enabled&attributes(attributes) diff --git a/web_external/templates/layout/panelMixins.jade b/web_external/templates/layout/panelMixins.jade new file mode 100644 index 00000000..71247eb8 --- /dev/null +++ b/web_external/templates/layout/panelMixins.jade @@ -0,0 +1,15 @@ +mixin panel-title(title, panelId) + .m-panel-title + | #{title} + span + block + i(class='icon-up-open', + data-toggle='collapse', + data-target='##{panelId} > .m-panel-content') + i(class='icon-cancel') + + +mixin panel-content(cls) + .m-panel-content(class=cls) + if block + block