diff --git a/src/scripts/collections/baseprovider.js b/src/scripts/collections/baseprovider.js index 4bf0c1c..3eb56b4 100644 --- a/src/scripts/collections/baseprovider.js +++ b/src/scripts/collections/baseprovider.js @@ -64,6 +64,12 @@ module.exports = Backbone.Collection.extend({ expression.type, '(' + expression.value.map(enclose).join(', ') + ')' ].join(' ') + } else if(expression.type === 'is not' || expression.type == 'is'){ + return [ + field, + expression.type, + expression.value + ].join(' ') } else { return [ field, diff --git a/src/scripts/templates/card.html b/src/scripts/templates/card.html index a21d39d..1078225 100644 --- a/src/scripts/templates/card.html +++ b/src/scripts/templates/card.html @@ -21,6 +21,24 @@
  • Export (CSV)
  • + + +
  • + + + Expand this card + +
  • diff --git a/src/scripts/templates/header.html b/src/scripts/templates/header.html index 5aea48d..10e6581 100644 --- a/src/scripts/templates/header.html +++ b/src/scripts/templates/header.html @@ -1,5 +1,5 @@
    -
    +

    <%= data.title %>

    @@ -8,18 +8,23 @@

    <%= data.title %>

    <% } %>
    -
    +
    diff --git a/src/scripts/views/bar.js b/src/scripts/views/bar.js index 89fd3c4..3d45b61 100644 --- a/src/scripts/views/bar.js +++ b/src/scripts/views/bar.js @@ -23,9 +23,18 @@ module.exports = BaseChart.extend({ clustered: false, lineColor: '#97bbcd', balloonFunction: function (item, graph) { - return '' + item.category + + var baloonHtml = '' + item.category + '
    Total: ' + (+item.dataContext.value).toLocaleString() + '
    Filtered Amount: ' + (+item.dataContext.filteredValue).toLocaleString() + try{ + var percentOfTotal = (parseFloat(item.dataContext.filteredValue)/parseFloat(item.dataContext.value)*100).toFixed(2) + if(percentOfTotal != "NaN"){ // NaN becomes "NaN" when converted to Fixed + baloonHtml += ' (' + percentOfTotal + '%)' + } + } catch(err){ + console.log(err) + } + return baloonHtml } } ], @@ -107,7 +116,9 @@ module.exports = BaseChart.extend({ _.bindAll(this, 'onClickCursor', 'onClickBar', 'onClickLabel', 'onHover', 'onClickScroll', 'zoomToBeginning') }, events: { - 'click .scroll a': 'onClickScroll' + 'click .scroll a': 'onClickScroll', + 'click .toggle-base-collection-link': 'onClickToggleBaseCollectionLink', + 'click .zoom-to-filtered-collection-link': 'onClickZoomToFilteredCollectionLink' }, render: function () { BaseChart.prototype.render.apply(this, arguments) @@ -134,6 +145,15 @@ module.exports = BaseChart.extend({ if (this.chart.endIndex - this.chart.startIndex < this.collection.length) { this.$('.scroll').removeClass('hidden') } + + // If there are filters, show the toggle for base collection and zoom filter collection buttons + if(_.isEmpty(this.filteredCollection.getFilters())){ + this.$('.toggle-base-collection').hide() + this.$('.zoom-to-filtered-collection').hide() + } else { + this.$('.toggle-base-collection').show() + this.$('.zoom-to-filtered-collection').show() + } }, zoomToBeginning: function () { if (this.collection.length > this.chart.maxSelectedSeries) { @@ -189,5 +209,50 @@ module.exports = BaseChart.extend({ } }) } + }, + onClickToggleBaseCollectionLink: function (e) { + var target = $(e.target) + // If target was the text, change target to the icon + if(!target.hasClass('toggle-base-collection-icon')){ + target = target.children('.toggle-base-collection-icon') + } + // If the toggle is currently on, hide the first graph (base collection) + if(target.hasClass("fa-toggle-on")){ + this.chart.hideGraph(this.chart.graphs[0]) + } else { + this.chart.showGraph(this.chart.graphs[0]) + } + // No matter what, toggle the icons classes to show the other one + target.filter('span.fa').toggleClass("fa-toggle-on") + target.filter('span.fa').toggleClass("fa-toggle-off") + e.preventDefault() + }, + onClickZoomToFilteredCollectionLink: function (e) { + var target = $(e.target) + // If target was the text, change target to the icon + if(!target.hasClass('zoom-to-filtered-collection-icon')){ + target = target.children('.zoom-to-filtered-collection-icon') + } + + // If the toggle is currently on, hide the first graph (base collection) + if(target.hasClass("fa-search-plus")){ // zoom in + var filteredCollectionValues = _.map(this.filteredCollection.models, function(o){return parseInt(o.attributes.value)}) + var filteredCollectionMaxValue = _.max(filteredCollectionValues) * 1.15 // include some top padding + this.chart.valueAxes[0].maximum = filteredCollectionMaxValue + this.chart.validateNow() + } else { // zoom out + var collectionValues = _.map(this.collection.models, function(o){return parseInt(o.attributes.value)}) + var collectionMaxValue = _.max(collectionValues) * 1.15 // include some top padding + this.chart.valueAxes[0].maximum = collectionMaxValue + this.chart.validateNow() + } + + // No matter what, toggle the icons classes to show the other one + target.filter('span.fa').toggleClass("fa-search-plus") + target.filter('span.fa').toggleClass("fa-search-minus") + e.preventDefault() } + + + }) diff --git a/src/scripts/views/card.js b/src/scripts/views/card.js index 12f3e42..42485f8 100644 --- a/src/scripts/views/card.js +++ b/src/scripts/views/card.js @@ -27,7 +27,8 @@ module.exports = Backbone.View.extend({ // Delegate event here so as not to have it overriden by child classes' events properties this.events = _.extend({ 'click .remove-filter': 'onClickRemoveFilter', - 'click .embed-link': 'onClickEmbedLink' + 'click .embed-link': 'onClickEmbedLink', + 'click .expand-card': 'onClickExpandLink' }, this.events || {}) this.delegateEvents() @@ -105,5 +106,40 @@ module.exports = Backbone.View.extend({ var exportView = new EmbedHelperView({model: new Backbone.Model(this.config)}) this.$el.after(exportView.render().el) e.preventDefault() + }, + onClickExpandLink: function (e) { + var card_el = this.$el + if (card_el.attr("data-expanded") == "true") { + // Contract card height + card_el.attr("style",card_el.attr("data-original-styles")) + card_el.css("min-height",card_el.attr("data-original-min-height")) + card_el.height(card_el.attr("data-original-height")-15) // not sure why I need to make this smaller than it was + // Contract card width + var originalClasses = card_el.attr("data-original-classes") + card_el.addClass("col-sm-12") + card_el.attr("data-original-classes","") + card_el.attr("class",originalClasses) + // Set state as not expanded + card_el.attr("data-expanded","false") + } else { + // Expand width + var originalClasses = card_el.attr("class") + card_el.attr("data-original-classes",originalClasses) + card_el.removeClass(originalClasses) + card_el.addClass("col-sm-12") + // Expand height + card_el.attr("data-original-styles",card_el.attr("style")) + card_el.attr("data-original-height",card_el.height()) + card_el.attr("data-original-min-height",card_el.css("min-height")) + card_el.css("min-height",parseInt(card_el.css("min-height")*1.5)+"px") + // Set state as expanded + card_el.attr("data-expanded","true") + } + this.setHeight() + if (this.config.chartType == "choropleth") { + // Leaflet maps need to be refreshed upon container size change + this.map.invalidateSize() + } + e.preventDefault() } }) diff --git a/src/scripts/views/datetime.js b/src/scripts/views/datetime.js index d628bd4..7ef20b0 100644 --- a/src/scripts/views/datetime.js +++ b/src/scripts/views/datetime.js @@ -1,3 +1,4 @@ +var $ = require('jquery') var _ = require('underscore') var BaseChart = require('./basechart') var numberFormatter = require('../util/number-formatter') @@ -61,7 +62,7 @@ module.exports = BaseChart.extend({ categoryAxis: { autoWrap: true, parseDates: true, - minPeriod: 'MM', + minPeriod: 'DD', gridAlpha: 0, guides: [{ lineThickness: 2, @@ -79,7 +80,7 @@ module.exports = BaseChart.extend({ dataDateFormat: 'YYYY-MM-DDT00:00:00.000', // "2015-04-07T16:21:00.000" creditsPosition: 'top-right', chartCursor: { - categoryBalloonDateFormat: 'MMM YYYY', + categoryBalloonDateFormat: 'DD MMM YYYY', cursorPosition: 'mouse', selectWithoutZooming: true, oneBalloonOnly: true, @@ -92,11 +93,24 @@ module.exports = BaseChart.extend({ _.bindAll(this, 'onClick') }, + events: { + 'click .toggle-base-collection-link': 'onClickToggleBaseCollectionLink', + 'click .zoom-to-filtered-collection-link': 'onClickZoomToFilteredCollectionLink' + }, render: function () { BaseChart.prototype.render.apply(this, arguments) // Listen to when the user selects a range this.chart.chartCursor.addListener('selected', this.onClick) + + if(_.isEmpty(this.filteredCollection.getFilters())){ + this.$('.toggle-base-collection').hide() + this.$('.zoom-to-filtered-collection').hide() + } else { + this.$('.toggle-base-collection').show() + this.$('.zoom-to-filtered-collection').show() + } + }, // When the user clicks on a bar in this chart onClick: function (e) { @@ -130,5 +144,47 @@ module.exports = BaseChart.extend({ ] } }) + }, + onClickToggleBaseCollectionLink: function (e) { + var target = $(e.target) + // If target was the text, change target to the icon + if(!target.hasClass('toggle-base-collection-icon')){ + target = target.children('.toggle-base-collection-icon') + } + // If the toggle is currently on, hide the first graph (base collection) + if(target.hasClass("fa-toggle-on")){ + this.chart.hideGraph(this.chart.graphs[0]) + } else { + this.chart.showGraph(this.chart.graphs[0]) + } + // No matter what, toggle the icons classes to show the other one + target.filter('span.fa').toggleClass("fa-toggle-on") + target.filter('span.fa').toggleClass("fa-toggle-off") + e.preventDefault() + }, + onClickZoomToFilteredCollectionLink: function (e) { + var target = $(e.target) + // If target was the text, change target to the icon + if(!target.hasClass('zoom-to-filtered-collection-icon')){ + target = target.children('.zoom-to-filtered-collection-icon') + } + + // If the toggle is currently on, hide the first graph (base collection) + if(target.hasClass("fa-search-plus")){ // zoom in + var filteredCollectionValues = _.map(this.filteredCollection.models, function(o){return parseInt(o.attributes.value)}) + var filteredCollectionMaxValue = _.max(filteredCollectionValues) * 1.15 // include some top padding + this.chart.valueAxes[0].maximum = filteredCollectionMaxValue + this.chart.validateNow() + } else { // zoom out + var collectionValues = _.map(this.collection.models, function(o){return parseInt(o.attributes.value)}) + var collectionMaxValue = _.max(collectionValues) * 1.15 // include some top padding + this.chart.valueAxes[0].maximum = collectionMaxValue + this.chart.validateNow() + } + + // No matter what, toggle the icons classes to show the other one + target.filter('span.fa').toggleClass("fa-search-plus") + target.filter('span.fa').toggleClass("fa-search-minus") + e.preventDefault() } })