Skip to content
This repository has been archived by the owner on Dec 23, 2017. It is now read-only.

Commit

Permalink
Merge branch 'develop' of github.com:18f/openFEC-web-app into feature…
Browse files Browse the repository at this point in the history
…/election-summary
  • Loading branch information
jmcarp committed Sep 10, 2015
2 parents ee81f73 + 22d474c commit 9056b8b
Show file tree
Hide file tree
Showing 13 changed files with 326 additions and 167 deletions.
9 changes: 9 additions & 0 deletions static/js/modules/columns.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ var _ = require('underscore');
var tables = require('./tables');
var decoders = require('./decoders');

var sizeInfo = {
0: {limits: [0, 199.99], label: 'Under $200'},
200: {limits: [200, 499.99], label: '$200 - $499'},
500: {limits: [500, 999.99], label: '$500 - $999'},
1000: {limits: [1000, 1999.99], label: '$1000 - $1999'},
2000: {limits: [2000, null], label: 'Over $2000'},
};

var filings = {
pdf_url: tables.urlColumn('pdf_url', {data: 'document_description', className: 'all', orderable: false}),
filer_name: {
Expand Down Expand Up @@ -56,5 +64,6 @@ function getColumns (columns, keys) {

module.exports = {
filings: filings,
sizeInfo: sizeInfo,
getColumns: getColumns
};
139 changes: 107 additions & 32 deletions static/js/modules/election-lookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var $ = require('jquery');
var URI = require('URIjs');
var _ = require('underscore');
var moment = require('moment');
var topojson = require('topojson');
var colorbrewer = require('colorbrewer');

var L = require('leaflet');
Expand All @@ -14,8 +15,11 @@ require('leaflet-providers');
var helpers = require('./helpers');
var utils = require('./election-utils');

var states = require('../us.json');
var districts = require('../stateDistricts.json');

var stateFeatures = topojson.feature(states, states.objects.units).features;

var districtTemplate = require('../../templates/districts.hbs');
var resultTemplate = require('../../templates/electionResult.hbs');
var zipWarningTemplate = require('../../templates/electionZipWarning.hbs');
Expand Down Expand Up @@ -100,6 +104,32 @@ function hasOption($select, value) {
return $select.find('option[value="' + value + '"]').length > 0;
}

function getStatePalette(scale) {
var colorOptions = _.map(Object.keys(scale), function(key) {
return parseInt(key);
});
return scale[_.max(colorOptions)];
}

function getDistrictPalette(scale) {
var colorOptions = _.map(Object.keys(scale), function(key) {
return parseInt(key);
});
var minColors = Math.min.apply(null, colorOptions);
var maxColors = Math.max.apply(null, colorOptions);
return _.chain(utils.districtFeatures.features)
.groupBy(function(feature) {
var district = utils.decodeDistrict(feature.id);
return district.state;
})
.map(function(features, state) {
var numColors = Math.max(minColors, Math.min(features.length, maxColors));
return [state, scale[numColors]];
})
.object()
.value();
}

var ElectionFormMixin = {
handleZipChange: function() {
this.$state.val('');
Expand Down Expand Up @@ -306,6 +336,12 @@ ElectionLookupPreview.prototype.init = function() {
this.handleStateChange();
};

var FEATURE_TYPES = {
STATES: 1,
DISTRICTS: 2
};
var STATE_ZOOM_THRESHOLD = 4;

var defaultOpts = {
colorScale: colorbrewer.Set1
};
Expand All @@ -319,34 +355,62 @@ function ElectionLookupMap(elm, opts) {
ElectionLookupMap.prototype.init = function() {
this.overlay = null;
this.districts = null;
this.colorMap = null;
this.map = L.map(this.elm);
this.map.on('zoomend', this.handleZoom.bind(this));
L.tileLayer.provider('Stamen.TonerLite').addTo(this.map);
this.drawDistricts();
this.statePalette = getStatePalette(this.opts.colorScale);
this.districtPalette = getDistrictPalette(this.opts.colorScale);
this.map.setView([37.8, -96], 3);
this.drawStates();
};

ElectionLookupMap.prototype.drawDistricts = function(districts, opts) {
var self = this;
opts = _.extend({reset: true}, opts);
ElectionLookupMap.prototype.drawStates = function() {
if (this.featureType === FEATURE_TYPES.STATES) { return; }
this.featureType = FEATURE_TYPES.STATES;
if (this.overlay) {
this.map.removeLayer(this.overlay);
}
this.districts = null;
// this.setColors(stateFeatures);
this.overlay = L.geoJson(stateFeatures, {
onEachFeature: this.onEachState.bind(this)
}).addTo(this.map);
};

ElectionLookupMap.prototype.drawDistricts = function(districts) {
if (this.featureType === FEATURE_TYPES.DISTRICTS && !districts) { return; }
this.featureType = FEATURE_TYPES.DISTRICTS;
var features = districts ?
this.filterDistricts(districts) :
utils.districtFeatures;
if (this.overlay) {
this.map.removeLayer(this.overlay);
}
if (opts.reset && !_.isEqual(districts, this.districts)) {
this.setColors(features.features);
}
this.districts = districts;
this.overlay = L.geoJson(
features, {
onEachFeature: this.onEachFeature.bind(this)
this.overlay = L.geoJson(features, {
onEachFeature: this.onEachDistrict.bind(this)
}).addTo(this.map);
if (districts) {
this.map.fitBounds(this.overlay.getBounds());
} else {
this.map.setView([37.8, -96], 3);
}
this.drawBackgroundDistricts(districts);
};

ElectionLookupMap.prototype.drawBackgroundDistricts = function(districts) {
if (!districts) { return; }
var states = _.chain(districts)
.map(function(district) {
return Math.floor(district / 100);
})
.unique()
.value();
var stateDistricts = _.filter(utils.districtFeatures.features, function(feature) {
return states.indexOf(Math.floor(feature.id / 100)) !== -1 &&
districts.indexOf(feature.id) === -1;
});
L.geoJson(stateDistricts, {
onEachFeature: _.partial(this.onEachDistrict.bind(this), _, _, {color: '#bbbbbb'})
}).addTo(this.overlay);
};

ElectionLookupMap.prototype.filterDistricts = function(districts) {
Expand All @@ -356,36 +420,47 @@ ElectionLookupMap.prototype.filterDistricts = function(districts) {
};
};

ElectionLookupMap.prototype.setColors = function(features) {
var colorOptions = _.map(Object.keys(this.opts.colorScale), function(key) {
return parseInt(key);
});
var minColors = Math.min.apply(null, colorOptions);
var maxColors = Math.max.apply(null, colorOptions);
var numColors = Math.max(minColors, Math.min(features.length, maxColors));
var colors = this.opts.colorScale[numColors];
this.colorMap = _.chain(features)
.map(function(feature, index) {
return [feature.id, colors[index % colors.length]];
})
.object()
.value();
ElectionLookupMap.prototype.handleStateClick = function(e) {
if (this.opts.handleSelect) {
var state = utils.decodeState(e.target.feature.id.substring(2, 4));
this.opts.handleSelect(state);
}
};

ElectionLookupMap.prototype.onEachFeature = function(feature, layer) {
layer.setStyle({color: this.colorMap[feature.id]});
layer.on('click', this.handleClick.bind(this));
ElectionLookupMap.prototype.onEachState = function(feature, layer) {
var state = parseInt(feature.id.substring(2, 4));
var color = this.statePalette[state % this.statePalette.length];
layer.setStyle({color: color});
layer.on('click', this.handleStateClick.bind(this));
};

ElectionLookupMap.prototype.handleClick = function(e) {
ElectionLookupMap.prototype.onEachDistrict = function(feature, layer, opts) {
opts = opts || {};
var decoded = utils.decodeDistrict(feature.id);
var palette = this.districtPalette[decoded.state];
var color = palette[decoded.district % palette.length];
layer.setStyle({color: opts.color || color});
layer.on('click', this.handleDistrictClick.bind(this));
};

ElectionLookupMap.prototype.handleDistrictClick = function(e) {
this.map.removeLayer(this.overlay);
this.drawDistricts([e.target.feature.id], {reset: false});
this.drawDistricts([e.target.feature.id]);
if (this.opts.handleSelect) {
var district = utils.decodeDistrict(e.target.feature.id);
this.opts.handleSelect(district.state, district.district);
}
};

ElectionLookupMap.prototype.handleZoom = function(e) {
var zoom = e.target.getZoom();
if (zoom <= STATE_ZOOM_THRESHOLD) {
this.drawStates();
} else if (!this.districts) {
this.drawDistricts();
}
};

module.exports = {
ElectionLookup: ElectionLookup,
ElectionLookupMap: ElectionLookupMap,
Expand Down
8 changes: 7 additions & 1 deletion static/js/modules/election-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ function decodeDistrict(district) {
};
}

function decodeState(state) {
state = _.sprintf('%02d', parseInt(state));
return fipsInverse[state];
}

function truncate(value, digits) {
var multiplier = Math.pow(10, digits);
return Math.floor(value / multiplier) * multiplier;
Expand Down Expand Up @@ -62,5 +67,6 @@ module.exports = {
findDistricts: findDistricts,
districtFeatures: districtFeatures,
encodeDistrict: encodeDistrict,
decodeDistrict: decodeDistrict
decodeDistrict: decodeDistrict,
decodeState: decodeState
};
7 changes: 4 additions & 3 deletions static/js/modules/tables.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,11 @@ function buildTotalLink(path, getParams) {
var link = document.createElement('a');
link.textContent = helpers.currency(data);
link.setAttribute('title', 'View individual transactions');
var params = getParams(data, type, row, meta);
var uri = URI(path)
.query({committee_id: row.committee_id})
.addQuery(getParams(row));
link.setAttribute('href', buildAggregateUrl(uri, row.cycle));
.addQuery({committee_id: row.committee_id})
.addQuery(params);
link.setAttribute('href', buildAggregateUrl(uri, _.extend({}, row, params).cycle));
span.appendChild(link);
return span.outerHTML;
};
Expand Down
26 changes: 9 additions & 17 deletions static/js/pages/committee-single.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,21 @@ var tableOpts = {
useHideNull: false
};

var sizeInfo = {
0: {limits: [0, 199.99], label: 'Under $200'},
200: {limits: [200, 499.99], label: '$200 - $499'},
500: {limits: [500, 999.99], label: '$500 - $999'},
1000: {limits: [1000, 1999.99], label: '$1000 - $1999'},
2000: {limits: [2000, null], label: 'Over $2000'},
};

var sizeColumns = [
{
data: 'size',
width: '50%',
className: 'all',
render: function(data, type, row, meta) {
return sizeInfo[data].label;
return columns.sizeInfo[data].label;
}
},
{
data: 'total',
width: '50%',
className: 'all',
render: tables.buildTotalLink('/receipts', function(row) {
var info = sizeInfo[row.size];
render: tables.buildTotalLink('/receipts', function(data, type, row, meta) {
var info = columns.sizeInfo[row.size];
return {
min_amount: info.limits[0],
max_amount: info.limits[1],
Expand All @@ -67,7 +59,7 @@ var committeeColumns = [
data: 'total',
className: 'all',
orderable: false,
render: tables.buildTotalLink('/receipts', function(row) {
render: tables.buildTotalLink('/receipts', function(data, type, row, meta) {
return {
contributor_id: row.contributor_id,
is_individual: 'true'
Expand All @@ -93,7 +85,7 @@ var stateColumns = [
data: 'total',
width: '50%',
className: 'all',
render: tables.buildTotalLink('/receipts', function(row) {
render: tables.buildTotalLink('/receipts', function(data, type, row, meta) {
return {
contributor_state: row.state,
is_individual: 'true'
Expand All @@ -108,7 +100,7 @@ var employerColumns = [
data: 'total',
className: 'all',
orderable: false,
render: tables.buildTotalLink('/receipts', function(row) {
render: tables.buildTotalLink('/receipts', function(data, type, row, meta) {
return {
contributor_employer: row.employer,
is_individual: 'true'
Expand All @@ -123,7 +115,7 @@ var occupationColumns = [
data: 'total',
className: 'all',
orderable: false,
render: tables.buildTotalLink('/receipts', function(row) {
render: tables.buildTotalLink('/receipts', function(data, type, row, meta) {
return {
contributor_occupation: row.occupation,
is_individual: 'true'
Expand Down Expand Up @@ -152,7 +144,7 @@ var disbursementRecipientColumns = [
data: 'total',
className: 'all',
orderable: false,
render: tables.buildTotalLink('/disbursements', function(row) {
render: tables.buildTotalLink('/disbursements', function(data, type, row, meta) {
return {recipient_name: row.recipient_name};
})
}
Expand All @@ -171,7 +163,7 @@ var disbursementRecipientIDColumns = [
data: 'total',
className: 'all',
orderable: false,
render: tables.buildTotalLink('/disbursements', function(row) {
render: tables.buildTotalLink('/disbursements', function(data, type, row, meta) {
return {recipient_committee_id: row.recipient_id};
})
}
Expand Down
Loading

0 comments on commit 9056b8b

Please sign in to comment.