├── img ├── .gitignore ├── no.jpg ├── yes.jpg ├── c4dc.png ├── wamu.png ├── bowser.jpg ├── catania.jpg ├── schwartz.jpg └── anonymous.gif ├── .gitignore ├── CNAME ├── data ├── contests.csv ├── candidates.csv ├── results.csv └── vtd.csv ├── js ├── leaflet-master │ ├── images │ │ ├── layers.png │ │ ├── layers-2x.png │ │ ├── marker-icon.png │ │ ├── marker-icon-2x.png │ │ └── marker-shadow.png │ └── leaflet.css ├── plugins.js ├── topojson.v1.min.js ├── jquery.csv-0.71.min.js ├── vendor │ └── modernizr-2.6.2.min.js ├── lodash.min.js └── main.js ├── civic.json ├── update-data.rb ├── readme.md ├── license.txt ├── index.html └── css ├── normalize.css └── main.css /img/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.yml -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | electionmap.wamu.org -------------------------------------------------------------------------------- /img/no.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/img/no.jpg -------------------------------------------------------------------------------- /img/yes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/img/yes.jpg -------------------------------------------------------------------------------- /img/c4dc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/img/c4dc.png -------------------------------------------------------------------------------- /img/wamu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/img/wamu.png -------------------------------------------------------------------------------- /img/bowser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/img/bowser.jpg -------------------------------------------------------------------------------- /img/catania.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/img/catania.jpg -------------------------------------------------------------------------------- /img/schwartz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/img/schwartz.jpg -------------------------------------------------------------------------------- /img/anonymous.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/img/anonymous.gif -------------------------------------------------------------------------------- /data/contests.csv: -------------------------------------------------------------------------------- 1 | id,name,initiative,winner 2 | mayor,Mayor,, 3 | ag,Attorney General,, 4 | initiative_71,Initative 71,TRUE, -------------------------------------------------------------------------------- /js/leaflet-master/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/js/leaflet-master/images/layers.png -------------------------------------------------------------------------------- /js/leaflet-master/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/js/leaflet-master/images/layers-2x.png -------------------------------------------------------------------------------- /js/leaflet-master/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/js/leaflet-master/images/marker-icon.png -------------------------------------------------------------------------------- /js/leaflet-master/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/js/leaflet-master/images/marker-icon-2x.png -------------------------------------------------------------------------------- /js/leaflet-master/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmgiven/open-election-map/HEAD/js/leaflet-master/images/marker-shadow.png -------------------------------------------------------------------------------- /js/plugins.js: -------------------------------------------------------------------------------- 1 | // Avoid `console` errors in browsers that lack a console. 2 | (function() { 3 | var method; 4 | var noop = function () {}; 5 | var methods = [ 6 | 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 7 | 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 8 | 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 9 | 'timeStamp', 'trace', 'warn' 10 | ]; 11 | var length = methods.length; 12 | var console = (window.console = window.console || {}); 13 | 14 | while (length--) { 15 | method = methods[length]; 16 | 17 | // Only stub undefined methods. 18 | if (!console[method]) { 19 | console[method] = noop; 20 | } 21 | } 22 | }()); -------------------------------------------------------------------------------- /data/candidates.csv: -------------------------------------------------------------------------------- 1 | id,first_name,last_name,party,contest,other,color,photo 2 | bowser,Muriel,Bowser,Democratic,mayor,,#278D28,img/bowser.jpg 3 | catania,David,Catania,Independent,mayor,,#3E59B4,img/catania.jpg 4 | djonkam,Nestor,Djonkam,Independent,mayor,TRUE,, 5 | faith,,Faith,Statehood Green,mayor,TRUE,, 6 | majors,Bruce,Majors,Libertarian,mayor,TRUE,, 7 | schwartz,Carol,Schwartz,Independent,mayor,,#E35D00,img/schwartz.jpg 8 | write_in,Write-in,,,mayor,TRUE,, 9 | racine,Karl,Racine,Democratic,ag,,#3E59B4, 10 | smith,Edward,Smith,Democratic,ag,,#278D28, 11 | masters,Lorie,Masters,Democratic,ag,,#E35D00, 12 | zukerberg,Paul,Zukerberg,Democratic,ag,,#AE000F, 13 | williams,Lateefah,Williams,Democratic,ag,,#943DA9, 14 | ag_write_in,Write-in,,Democratic,ag,TRUE,, 15 | 71_yes,,For,,initiative_71,,#1D895F,img/yes.jpg 16 | 71_no,,Against,,initiative_71,,#AE000F,img/no.jpg -------------------------------------------------------------------------------- /civic.json: -------------------------------------------------------------------------------- 1 | { 2 | "conformsTo": "http://codefordc.org/resources/specification.html", 3 | "status": "Archival", 4 | "contact": { 5 | "name": "Chris Given", 6 | "email": "cmgiven@gmail.com", 7 | "twitter": "@cmgiven" 8 | }, 9 | "bornAt": "Code for DC", 10 | "geography": "Washington, DC", 11 | "communityPartner": { 12 | "WAMU 88.5": "http://wamu.org" 13 | }, 14 | "type": "Web app", 15 | "data": { 16 | "DC election results": "https://www.dcboee.org/election_info/election_results/2014/November-4-General-Election" 17 | }, 18 | "needs": [], 19 | "categories": [ 20 | {"category":"Politics"}, 21 | {"category":"Elections"}, 22 | {"category":"Mayor"}, 23 | {"category":"Attorney General"}, 24 | {"category":"Marijuana Legalization"} 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /update-data.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Downloads data from a Google Spreadsheet and pushes an automatic commit. 4 | # Execute by running the command './update-data.rb' from your project directory. 5 | 6 | require 'open-uri' 7 | require 'digest/md5' 8 | 9 | OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax') 10 | OpenURI::Buffer.const_set 'StringMax', 40960 11 | 12 | # Update these constants to your Google Spreadsheet. 13 | SHEET_ID = '1xZXetat3Up0qHRJfRs8jQIlTJgRT9zzuAEAlyZ-p4RU' 14 | GIDS = { 15 | 'results' => '0', 16 | 'vtd' => '1722258999', 17 | 'candidates' => '222813760', 18 | 'contests' => '1654121249' 19 | } 20 | 21 | # Values can be found in the URL of your Google Spreadsheet. 22 | # Example: https://docs.google.com/spreadsheets/d/1xZXetat3Up0qHRJfRs8jQIlTJgRT9zzuAEAlyZ-p4RU/edit#gid=0 23 | # ******************************************** * 24 | 25 | def get_google_sheet (gid) 26 | open("https://docs.google.com/spreadsheets/d/" + SHEET_ID + "/export?format=csv&id=" + SHEET_ID + "&gid=" + gid).string 27 | end 28 | 29 | changed_files = [] 30 | 31 | GIDS.each do |sheet, gid| 32 | path = 'data/' + sheet + '.csv' 33 | 34 | old_hash = Digest::MD5.hexdigest(IO.read(path)) 35 | 36 | latest_data = get_google_sheet(gid) 37 | new_hash = Digest::MD5.hexdigest(latest_data) 38 | 39 | if new_hash != old_hash then 40 | File.open(path, 'w') do |f| 41 | f.write(latest_data) 42 | end 43 | changed_files.push(path) 44 | puts "Updated " + path 45 | end 46 | end 47 | 48 | if changed_files.length > 0 then 49 | files = changed_files.join(' ') 50 | message = 'Auto-update data' 51 | 52 | `git add #{files}` 53 | `git commit -m #{message}` 54 | `git push` 55 | `git push -f origin master:gh-pages` 56 | end -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Created by [Code for DC](http://codefordc.org). Published as a public domain work under the [CC0 License](http://creativecommons.org/publicdomain/zero/1.0/). 2 | 3 | # So you want a election map? 4 | 5 | ### You will need 6 | * A topojson file showing the boundaries of voting districts in your jurisdiction. You'll probably need to find a shapefile (.shp) of these boundaries, perhaps from a board of elections or government data portal, and convert it to the topojson format (look in the 'Converting Data' section of this [useful tutorial](http://bost.ocks.org/mike/map/)). Save this file as data/precinct-boundaries.json. 7 | * A Google Spreadsheet matching the format of [this one](https://docs.google.com/spreadsheets/d/1xZXetat3Up0qHRJfRs8jQIlTJgRT9zzuAEAlyZ-p4RU/edit#gid=0), but with your own candidates and results. A starting point for demographic data about voting districts is the US Census Bureau's [American FactFinder](http://factfinder2.census.gov/faces/nav/jsf/pages/index.xhtml) website, where you'll need to select the geographic type 'Voting District' from the advanced search interface. 8 | 9 | ### You can easily customize 10 | * The header and footer in the index.html file. 11 | * The Google Analytics tracking code in the index.html file. 12 | * The location of your Google Spreadsheet in the update-data.rb file. 13 | * The REFRESH_DELAY in the js/main.js file to enable automatic refreshing of data. 14 | * The TOOLTIP_DESCRIPTION in the js/main.js file to customize how a voting district is identified with additional information from the vtd spreadsheet (i.e. to describe "Precinct 48" as "North Springfield"). 15 | * The AVAILABLE_FILTERS in the js/main.js file to customize the filtering options based on the data you have available. 16 | 17 | ### To get started 18 | * Easiest way to host the map is by using [Github Pages](https://pages.github.com). 19 | * Run the command ```./update-data.rb``` from the project directory in order to grab the latest data from your Google Spreadsheet (you'll either need a Mac or need a Linux machine with Ruby installed). Alternatively, you can download your Google Spreadsheet as a CSV file and replace the data in the data/ directory. -------------------------------------------------------------------------------- /js/topojson.v1.min.js: -------------------------------------------------------------------------------- 1 | !function(){function t(n,t){function r(t){var r,e=n.arcs[0>t?~t:t],o=e[0];return n.transform?(r=[0,0],e.forEach(function(n){r[0]+=n[0],r[1]+=n[1]})):r=e[e.length-1],0>t?[r,o]:[o,r]}function e(n,t){for(var r in n){var e=n[r];delete t[e.start],delete e.start,delete e.end,e.forEach(function(n){o[0>n?~n:n]=1}),f.push(e)}}var o={},i={},u={},f=[],c=-1;return t.forEach(function(r,e){var o,i=n.arcs[0>r?~r:r];i.length<3&&!i[1][0]&&!i[1][1]&&(o=t[++c],t[c]=r,t[e]=o)}),t.forEach(function(n){var t,e,o=r(n),f=o[0],c=o[1];if(t=u[f])if(delete u[t.end],t.push(n),t.end=c,e=i[c]){delete i[e.start];var a=e===t?t:t.concat(e);i[a.start=t.start]=u[a.end=e.end]=a}else i[t.start]=u[t.end]=t;else if(t=i[c])if(delete i[t.start],t.unshift(n),t.start=f,e=u[f]){delete u[e.end];var s=e===t?t:e.concat(t);i[s.start=e.start]=u[s.end=t.end]=s}else i[t.start]=u[t.end]=t;else t=[n],i[t.start=f]=u[t.end=c]=t}),e(u,i),e(i,u),t.forEach(function(n){o[0>n?~n:n]||f.push([n])}),f}function r(n,r,e){function o(n){var t=0>n?~n:n;(s[t]||(s[t]=[])).push({i:n,g:a})}function i(n){n.forEach(o)}function u(n){n.forEach(i)}function f(n){"GeometryCollection"===n.type?n.geometries.forEach(f):n.type in l&&(a=n,l[n.type](n.arcs))}var c=[];if(arguments.length>1){var a,s=[],l={LineString:i,MultiLineString:u,Polygon:u,MultiPolygon:function(n){n.forEach(u)}};f(r),s.forEach(arguments.length<3?function(n){c.push(n[0].i)}:function(n){e(n[0].g,n[n.length-1].g)&&c.push(n[0].i)})}else for(var h=0,p=n.arcs.length;p>h;++h)c.push(h);return{type:"MultiLineString",arcs:t(n,c)}}function e(r,e){function o(n){n.forEach(function(t){t.forEach(function(t){(f[t=0>t?~t:t]||(f[t]=[])).push(n)})}),c.push(n)}function i(n){return l(u(r,{type:"Polygon",arcs:[n]}).coordinates[0])>0}var f={},c=[],a=[];return e.forEach(function(n){"Polygon"===n.type?o(n.arcs):"MultiPolygon"===n.type&&n.arcs.forEach(o)}),c.forEach(function(n){if(!n._){var t=[],r=[n];for(n._=1,a.push(t);n=r.pop();)t.push(n),n.forEach(function(n){n.forEach(function(n){f[0>n?~n:n].forEach(function(n){n._||(n._=1,r.push(n))})})})}}),c.forEach(function(n){delete n._}),{type:"MultiPolygon",arcs:a.map(function(e){var o=[];if(e.forEach(function(n){n.forEach(function(n){n.forEach(function(n){f[0>n?~n:n].length<2&&o.push(n)})})}),o=t(r,o),(n=o.length)>1)for(var u,c=i(e[0][0]),a=0;n>a;++a)if(c===i(o[a])){u=o[0],o[0]=o[a],o[a]=u;break}return o})}}function o(n,t){return"GeometryCollection"===t.type?{type:"FeatureCollection",features:t.geometries.map(function(t){return i(n,t)})}:i(n,t)}function i(n,t){var r={type:"Feature",id:t.id,properties:t.properties||{},geometry:u(n,t)};return null==t.id&&delete r.id,r}function u(n,t){function r(n,t){t.length&&t.pop();for(var r,e=s[0>n?~n:n],o=0,i=e.length;i>o;++o)t.push(r=e[o].slice()),a(r,o);0>n&&f(t,i)}function e(n){return n=n.slice(),a(n,0),n}function o(n){for(var t=[],e=0,o=n.length;o>e;++e)r(n[e],t);return t.length<2&&t.push(t[0].slice()),t}function i(n){for(var t=o(n);t.length<4;)t.push(t[0].slice());return t}function u(n){return n.map(i)}function c(n){var t=n.type;return"GeometryCollection"===t?{type:t,geometries:n.geometries.map(c)}:t in l?{type:t,coordinates:l[t](n)}:null}var a=v(n.transform),s=n.arcs,l={Point:function(n){return e(n.coordinates)},MultiPoint:function(n){return n.coordinates.map(e)},LineString:function(n){return o(n.arcs)},MultiLineString:function(n){return n.arcs.map(o)},Polygon:function(n){return u(n.arcs)},MultiPolygon:function(n){return n.arcs.map(u)}};return c(t)}function f(n,t){for(var r,e=n.length,o=e-t;o<--e;)r=n[o],n[o++]=n[e],n[e]=r}function c(n,t){for(var r=0,e=n.length;e>r;){var o=r+e>>>1;n[o]n&&(n=~n);var r=o[n];r?r.push(t):o[n]=[t]})}function r(n,r){n.forEach(function(n){t(n,r)})}function e(n,t){"GeometryCollection"===n.type?n.geometries.forEach(function(n){e(n,t)}):n.type in u&&u[n.type](n.arcs,t)}var o={},i=n.map(function(){return[]}),u={LineString:t,MultiLineString:r,Polygon:r,MultiPolygon:function(n,t){n.forEach(function(n){r(n,t)})}};n.forEach(e);for(var f in o)for(var a=o[f],s=a.length,l=0;s>l;++l)for(var h=l+1;s>h;++h){var p,g=a[l],v=a[h];(p=i[g])[f=c(p,v)]!==v&&p.splice(f,0,v),(p=i[v])[f=c(p,g)]!==g&&p.splice(f,0,g)}return i}function s(n,t){function r(n){i.remove(n),n[1][2]=t(n),i.push(n)}var e=v(n.transform),o=m(n.transform),i=g();return t||(t=h),n.arcs.forEach(function(n){for(var u,f,c=[],a=0,s=0,l=n.length;l>s;++s)f=n[s],e(n[s]=[f[0],f[1],1/0],s);for(var s=1,l=n.length-1;l>s;++s)u=n.slice(s-1,s+2),u[1][2]=t(u),c.push(u),i.push(u);for(var s=0,l=c.length;l>s;++s)u=c[s],u.previous=c[s-1],u.next=c[s+1];for(;u=i.pop();){var h=u.previous,p=u.next;u[1][2]0;){var r=(t+1>>1)-1,o=e[r];if(p(n,o)>=0)break;e[o._=t]=o,e[n._=t=r]=n}}function t(n,t){for(;;){var r=t+1<<1,i=r-1,u=t,f=e[u];if(o>i&&p(e[i],f)<0&&(f=e[u=i]),o>r&&p(e[r],f)<0&&(f=e[u=r]),u===t)break;e[f._=t]=f,e[n._=t=u]=n}}var r={},e=[],o=0;return r.push=function(t){return n(e[t._=o]=t,o++),o},r.pop=function(){if(!(0>=o)){var n,r=e[0];return--o>0&&(n=e[o],t(e[n._=0]=n,0)),r}},r.remove=function(r){var i,u=r._;if(e[u]===r)return u!==--o&&(i=e[o],(p(i,r)<0?n:t)(e[i._=u]=i,u)),u},r}function v(n){if(!n)return y;var t,r,e=n.scale[0],o=n.scale[1],i=n.translate[0],u=n.translate[1];return function(n,f){f||(t=r=0),n[0]=(t+=n[0])*e+i,n[1]=(r+=n[1])*o+u}}function m(n){if(!n)return y;var t,r,e=n.scale[0],o=n.scale[1],i=n.translate[0],u=n.translate[1];return function(n,f){f||(t=r=0);var c=(n[0]-i)/e|0,a=(n[1]-u)/o|0;n[0]=c-t,n[1]=a-r,t=c,r=a}}function y(){}var d={version:"1.6.18",mesh:function(n){return u(n,r.apply(this,arguments))},meshArcs:r,merge:function(n){return u(n,e.apply(this,arguments))},mergeArcs:e,feature:o,neighbors:a,presimplify:s};"function"==typeof define&&define.amd?define(d):"object"==typeof module&&module.exports?module.exports=d:this.topojson=d}(); -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Open Election Map 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 | 29 |
30 |

Open Election Map

31 |

DC 2014 General Election

32 |
33 |
34 | WAMU 88.5 American University Radio 35 | Code for DC 36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 |
Candidate
52 |
Party
53 |
Votes
54 |
%
55 |
56 |
    57 | Show All + 58 |
    59 |
    60 |
    61 |
    62 |
    63 |
    64 |
     
    65 |
    66 |
    67 |

    Legend

    68 |
    69 |
    70 |
    71 |

    View

    72 | 76 |
    77 |
    78 |

    Filter

    79 | 89 |
    90 |
    91 |
    92 | 93 | 94 | 98 | 99 | 100 |
    101 | 102 | 103 |
    104 | WAMU 88.5 American University Radio 105 | Code for DC 106 |
    107 | 108 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /data/results.csv: -------------------------------------------------------------------------------- 1 | vtd,bowser,catania,djonkam,faith,majors,schwartz,write_in,racine,smith,masters,zukerberg,williams,ag_write_in,71_yes,71_no 2 | vp1,1008,495,10,14,24,87,17,553,264,248,229,187,18,1119,371 3 | vp2,185,65,2,8,23,21,4,60,61,66,53,40,6,267,51 4 | vp3,385,432,1,9,19,98,11,279,119,176,187,45,5,580,317 5 | vp4,398,498,2,6,23,64,11,304,110,188,191,40,22,671,298 6 | vp5,554,775,3,8,17,91,9,499,137,286,267,19,12,884,500 7 | vp6,471,734,5,10,31,91,4,448,121,251,255,46,16,827,475 8 | vp7,338,426,1,8,13,70,3,233,86,227,155,25,4,520,287 9 | vp8,716,961,4,7,20,153,9,765,139,426,269,26,11,1097,703 10 | vp9,339,481,4,4,12,72,4,348,98,168,127,20,10,433,442 11 | vp10,534,656,0,11,15,119,7,547,80,263,238,43,8,779,513 12 | vp11,721,1056,7,19,25,115,17,534,239,481,362,68,25,1429,495 13 | vp12,122,222,0,1,3,35,1,136,29,103,64,5,3,210,162 14 | vp13,348,511,1,8,6,67,2,327,123,151,190,21,7,636,277 15 | vp14,605,858,2,12,28,94,12,454,229,309,287,86,13,1259,302 16 | vp15,677,1012,3,14,14,89,14,529,335,322,325,67,15,1407,323 17 | vp16,756,1187,4,11,16,90,8,701,334,321,365,84,10,1596,416 18 | vp17,1149,1042,4,26,35,141,15,692,371,398,481,146,24,1746,526 19 | vp18,1282,768,6,15,22,154,13,818,431,249,349,197,15,1514,643 20 | vp19,1078,682,2,20,12,110,20,566,578,215,263,152,13,1431,396 21 | vp20,304,129,1,3,2,19,3,155,119,56,41,64,1,342,94 22 | vp21,312,188,1,3,2,30,9,197,118,54,59,58,6,391,118 23 | vp22,904,1062,4,12,15,78,12,700,401,269,377,97,10,1580,415 24 | vp23,787,566,0,19,9,62,10,454,242,213,262,97,11,1141,237 25 | vp24,641,661,4,16,12,65,13,477,201,206,285,69,6,1123,249 26 | vp25,1011,1300,7,38,20,162,19,798,338,407,626,72,10,1969,490 27 | vp26,744,876,2,21,16,91,11,620,200,338,344,50,10,1229,480 28 | vp27,693,826,5,12,12,109,8,696,185,320,266,26,9,1079,507 29 | vp28,642,938,2,15,18,148,14,605,170,408,323,61,15,1067,613 30 | vp29,331,393,1,9,8,67,1,277,78,185,134,25,7,519,259 31 | vp30,388,549,1,4,6,81,7,468,94,238,128,8,6,604,387 32 | vp31,689,931,1,16,14,126,11,747,195,367,260,31,8,1111,619 33 | vp32,764,954,1,10,13,162,13,884,212,402,238,25,15,1284,589 34 | vp33,712,1128,3,28,18,133,12,801,247,370,372,38,15,1360,613 35 | vp34,976,1025,6,23,38,177,19,821,245,469,428,68,19,1600,607 36 | vp35,815,882,1,35,9,109,16,486,282,255,580,88,6,1473,317 37 | vp36,1030,711,7,26,10,112,24,564,370,240,344,179,9,1380,352 38 | vp37,727,404,1,23,4,78,14,407,241,149,218,136,8,962,224 39 | vp38,712,425,1,17,6,65,15,400,260,149,211,108,6,932,262 40 | vp39,934,876,4,48,6,83,25,575,379,312,460,89,12,1543,369 41 | vp40,1062,906,4,50,18,122,37,585,374,370,518,123,7,1706,390 42 | vp41,850,544,5,33,11,77,21,470,308,216,252,125,11,1119,270 43 | vp42,445,387,3,16,6,42,15,249,174,144,158,79,6,718,145 44 | vp43,498,261,5,12,4,60,13,290,176,82,122,111,10,589,189 45 | vp44,829,204,7,11,11,110,12,473,252,117,107,153,6,685,380 46 | vp45,689,369,3,19,5,80,17,427,230,149,165,127,4,830,276 47 | vp46,922,344,4,14,9,74,30,502,342,147,187,134,8,956,367 48 | vp47,856,549,3,19,8,81,26,556,287,190,253,119,13,1101,336 49 | vp48,885,494,4,14,2,81,16,672,298,157,188,81,3,976,391 50 | vp49,259,161,1,7,1,15,4,153,82,42,72,49,2,338,85 51 | vp50,695,643,4,13,13,105,12,607,166,385,177,41,3,959,462 52 | vp51,1310,1150,1,6,14,153,22,1145,314,636,318,36,11,1608,973 53 | vp52,550,408,0,3,2,61,2,430,109,243,129,8,5,637,337 54 | vp53,458,164,3,8,3,54,12,337,134,72,67,34,6,432,232 55 | vp54,799,325,7,10,8,75,22,499,274,162,138,101,2,847,331 56 | vp55,805,222,6,11,2,88,11,495,249,115,100,117,2,709,341 57 | vp56,993,298,4,18,6,106,11,601,294,118,156,164,6,878,391 58 | vp57,813,210,10,9,6,105,17,475,257,116,106,141,5,737,366 59 | vp58,751,171,2,5,2,99,16,556,205,89,73,99,1,622,356 60 | vp59,890,283,2,15,9,98,17,571,277,170,124,103,7,802,411 61 | vp60,647,128,5,4,5,72,4,387,240,82,54,58,2,456,322 62 | vp61,620,174,3,5,3,75,11,414,195,91,79,59,1,507,273 63 | vp62,1442,495,1,7,4,153,27,1278,384,192,133,76,8,1389,662 64 | vp63,1157,579,5,21,3,131,27,687,334,402,233,152,9,1293,532 65 | vp64,933,204,2,8,6,102,14,625,297,104,85,101,2,771,400 66 | vp65,1042,182,5,11,4,105,11,667,311,109,70,133,9,705,493 67 | vp66,1670,369,6,14,6,200,29,1124,579,157,137,215,5,1200,894 68 | vp67,1064,362,5,9,5,168,16,701,462,137,145,107,6,904,599 69 | vp68,586,386,3,13,5,120,17,397,289,134,159,71,10,698,390 70 | vp69,677,205,3,5,5,130,15,527,254,76,70,82,7,625,368 71 | vp70,516,190,4,14,4,83,4,325,214,67,88,73,3,542,237 72 | vp71,784,198,6,9,0,126,11,484,263,108,89,134,8,655,394 73 | vp72,1116,254,9,20,7,147,18,616,375,150,151,195,8,957,480 74 | vp73,573,384,3,13,7,97,20,417,246,145,137,77,6,726,317 75 | vp74,1072,479,3,17,9,117,31,667,333,186,200,209,20,1120,482 76 | vp75,943,454,1,21,14,72,17,462,451,158,191,156,6,1103,358 77 | vp76,278,48,1,2,2,14,3,124,77,35,29,55,1,225,75 78 | vp77,729,251,3,11,10,71,12,367,241,119,126,151,8,700,301 79 | vp78,661,168,4,5,3,82,8,344,191,93,77,151,3,567,282 80 | vp79,409,81,5,1,4,55,4,222,127,60,30,82,2,339,154 81 | vp80,449,132,1,3,4,59,6,196,216,54,66,78,8,435,188 82 | vp81,1199,860,7,18,17,119,16,733,428,317,303,188,25,1505,581 83 | vp82,718,605,5,11,15,72,15,490,257,216,207,108,8,994,355 84 | vp83,1071,996,5,16,23,123,22,757,413,305,367,174,27,1674,517 85 | vp84,609,712,3,15,25,58,18,520,230,262,198,59,13,997,384 86 | vp85,657,1115,2,10,25,78,12,673,274,303,323,53,23,1325,479 87 | vp86,526,666,4,7,19,69,9,469,197,205,215,60,9,864,379 88 | vp87,684,646,2,10,11,99,13,477,274,213,215,132,9,1004,369 89 | vp88,558,857,5,7,10,104,9,574,225,274,236,43,13,1085,397 90 | vp89,644,1194,1,17,30,111,23,713,228,338,382,44,31,1354,632 91 | vp90,359,647,3,7,12,58,9,396,138,188,167,46,16,745,302 92 | vp91,871,1042,6,15,8,109,11,649,360,356,355,122,18,1407,504 93 | vp92,424,56,8,2,1,66,8,204,148,52,35,98,4,335,179 94 | vp93,453,59,3,3,2,49,4,180,163,63,39,84,1,318,178 95 | vp94,491,90,1,3,1,64,3,227,164,55,34,124,3,353,211 96 | vp95,417,70,0,3,3,60,6,201,148,41,29,114,1,318,198 97 | vp96,582,82,2,0,1,76,9,222,274,63,43,122,3,424,238 98 | vp97,307,70,4,3,3,56,0,140,129,51,30,72,2,249,156 99 | vp98,528,90,1,4,3,82,3,292,187,43,38,114,1,403,251 100 | vp99,377,69,3,3,2,57,2,169,165,55,34,76,3,287,166 101 | vp100,504,55,1,2,1,49,3,173,153,61,38,144,1,323,204 102 | vp101,567,89,2,3,2,84,5,281,242,71,36,82,3,426,257 103 | vp102,660,93,3,5,2,88,7,318,245,66,48,136,1,476,271 104 | vp103,914,181,6,4,1,128,12,457,329,137,73,174,7,719,400 105 | vp104,778,127,3,11,9,65,10,365,258,97,62,180,2,634,291 106 | vp105,623,110,3,7,0,58,5,310,219,63,40,133,3,403,291 107 | vp106,826,179,5,6,1,120,9,445,317,109,66,148,5,594,428 108 | vp107,375,44,0,3,1,43,5,160,128,46,25,87,0,276,138 109 | vp108,284,86,1,1,1,68,7,195,139,30,25,44,2,226,194 110 | vp109,376,117,2,3,0,76,6,205,219,45,48,46,2,337,211 111 | vp110,1252,302,6,9,3,215,30,736,572,155,106,183,8,1011,661 112 | vp111,662,121,7,8,2,90,9,338,245,94,53,131,4,509,302 113 | vp112,580,122,2,4,2,76,5,274,257,52,44,140,2,427,281 114 | vp113,722,248,5,5,2,102,9,439,343,88,66,113,9,658,365 115 | vp114,643,118,3,6,3,64,12,266,268,80,52,131,4,537,231 116 | vp115,568,100,1,4,4,64,8,280,225,45,44,115,2,438,224 117 | vp116,701,110,4,6,3,61,8,284,289,69,50,154,3,524,244 118 | vp117,501,75,4,1,5,30,2,205,170,54,33,114,0,381,167 119 | vp118,544,82,2,7,2,48,3,287,187,52,26,103,4,430,196 120 | vp119,561,117,1,4,5,67,5,228,222,74,43,146,4,456,205 121 | vp120,477,86,5,6,1,46,8,246,177,49,34,85,4,383,180 122 | vp121,748,125,0,9,4,78,7,339,313,60,48,162,5,536,309 123 | vp122,502,67,2,3,1,58,6,236,218,47,21,95,1,336,228 124 | vp123,551,95,3,3,5,75,4,314,204,42,39,96,5,419,245 125 | vp124,679,153,2,7,2,77,4,349,314,61,44,118,2,517,309 126 | vp125,923,186,4,7,3,104,8,472,337,111,55,197,3,705,347 127 | vp126,742,144,3,10,8,76,9,377,252,72,67,173,2,587,303 128 | vp127,1160,758,12,16,19,162,12,715,451,275,308,200,10,1447,544 129 | vp128,646,478,1,8,14,69,12,396,169,224,202,107,8,869,296 130 | vp129,476,616,3,9,7,44,5,343,180,206,225,58,13,883,233 131 | vp130,238,355,2,2,23,31,9,242,75,111,99,17,9,394,245 132 | vp131,609,601,3,9,26,51,6,415,172,193,201,91,24,899,357 133 | vp132,521,76,3,4,2,60,6,244,205,65,22,94,2,381,215 134 | vp133,402,74,2,7,0,79,4,184,222,49,36,58,1,340,180 135 | vp134,545,95,2,6,1,65,12,226,211,47,53,131,5,403,211 136 | vp135,853,538,2,25,7,97,13,505,404,175,208,129,6,1111,356 137 | vp136,224,242,0,2,7,32,0,181,56,87,92,18,1,353,132 138 | vp137,249,261,0,7,7,18,5,194,123,64,71,32,5,422,101 139 | vp138,594,684,1,7,6,92,6,522,174,316,215,35,6,938,431 140 | vp139,883,146,2,3,1,100,7,522,263,90,68,138,2,608,396 141 | vp140,510,96,4,7,5,53,8,227,225,49,45,92,2,380,209 142 | vp141,534,804,2,6,11,53,12,461,217,227,288,47,10,1147,222 143 | vp142,440,377,3,5,7,69,8,373,134,111,141,66,10,595,259 144 | vp143,394,393,3,3,24,31,10,227,118,163,149,59,11,590,221 -------------------------------------------------------------------------------- /js/jquery.csv-0.71.min.js: -------------------------------------------------------------------------------- 1 | 2 | RegExp.escape=function(s){return s.replace(/[-\/\\^$*+?.()|[\]{}]/g,'\\$&');};(function($){'use strict' 3 | $.csv={defaults:{separator:',',delimiter:'"',headers:true},hooks:{castToScalar:function(value,state){var hasDot=/\./;if(isNaN(value)){return value;}else{if(hasDot.test(value)){return parseFloat(value);}else{var integer=parseInt(value);if(isNaN(integer)){return null;}else{return integer;}}}}},parsers:{parse:function(csv,options){var separator=options.separator;var delimiter=options.delimiter;if(!options.state.rowNum){options.state.rowNum=1;} 4 | if(!options.state.colNum){options.state.colNum=1;} 5 | var data=[];var entry=[];var state=0;var value='' 6 | var exit=false;function endOfEntry(){state=0;value='';if(options.start&&options.state.rowNum=options.end){exit=true;} 9 | options.state.rowNum++;options.state.colNum=1;} 10 | function endOfValue(){if(options.onParseValue===undefined){entry.push(value);}else{var hook=options.onParseValue(value,options.state);if(hook!==false){entry.push(hook);}} 11 | value='';state=0;options.state.colNum++;} 12 | var escSeparator=RegExp.escape(separator);var escDelimiter=RegExp.escape(delimiter);var match=/(D|S|\n|\r|[^DS\r\n]+)/;var matchSrc=match.source;matchSrc=matchSrc.replace(/S/g,escSeparator);matchSrc=matchSrc.replace(/D/g,escDelimiter);match=RegExp(matchSrc,'gm');csv.replace(match,function(m0){if(exit){return;} 13 | switch(state){case 0:if(m0===separator){value+='';endOfValue();break;} 14 | if(m0===delimiter){state=1;break;} 15 | if(m0==='\n'){endOfValue();endOfEntry();break;} 16 | if(/^\r$/.test(m0)){break;} 17 | value+=m0;state=3;break;case 1:if(m0===delimiter){state=2;break;} 18 | value+=m0;state=1;break;case 2:if(m0===delimiter){value+=m0;state=1;break;} 19 | if(m0===separator){endOfValue();break;} 20 | if(m0==='\n'){endOfValue();endOfEntry();break;} 21 | if(/^\r$/.test(m0)){break;} 22 | throw new Error('CSVDataError: Illegal State [Row:'+options.state.rowNum+'][Col:'+options.state.colNum+']');case 3:if(m0===separator){endOfValue();break;} 23 | if(m0==='\n'){endOfValue();endOfEntry();break;} 24 | if(/^\r$/.test(m0)){break;} 25 | if(m0===delimiter){throw new Error('CSVDataError: Illegal Quote [Row:'+options.state.rowNum+'][Col:'+options.state.colNum+']');} 26 | throw new Error('CSVDataError: Illegal Data [Row:'+options.state.rowNum+'][Col:'+options.state.colNum+']');default:throw new Error('CSVDataError: Unknown State [Row:'+options.state.rowNum+'][Col:'+options.state.colNum+']');}});if(entry.length!==0){endOfValue();endOfEntry();} 27 | return data;},splitLines:function(csv,options){var separator=options.separator;var delimiter=options.delimiter;if(!options.state.rowNum){options.state.rowNum=1;} 28 | var entries=[];var state=0;var entry='';var exit=false;function endOfLine(){state=0;if(options.start&&options.state.rowNum=options.end){exit=true;} 31 | options.state.rowNum++;} 32 | var escSeparator=RegExp.escape(separator);var escDelimiter=RegExp.escape(delimiter);var match=/(D|S|\n|\r|[^DS\r\n]+)/;var matchSrc=match.source;matchSrc=matchSrc.replace(/S/g,escSeparator);matchSrc=matchSrc.replace(/D/g,escDelimiter);match=RegExp(matchSrc,'gm');csv.replace(match,function(m0){if(exit){return;} 33 | switch(state){case 0:if(m0===separator){entry+=m0;state=0;break;} 34 | if(m0===delimiter){entry+=m0;state=1;break;} 35 | if(m0==='\n'){endOfLine();break;} 36 | if(/^\r$/.test(m0)){break;} 37 | entry+=m0;state=3;break;case 1:if(m0===delimiter){entry+=m0;state=2;break;} 38 | entry+=m0;state=1;break;case 2:var prevChar=entry.substr(entry.length-1);if(m0===delimiter&&prevChar===delimiter){entry+=m0;state=1;break;} 39 | if(m0===separator){entry+=m0;state=0;break;} 40 | if(m0==='\n'){endOfLine();break;} 41 | if(m0==='\r'){break;} 42 | throw new Error('CSVDataError: Illegal state [Row:'+options.state.rowNum+']');case 3:if(m0===separator){entry+=m0;state=0;break;} 43 | if(m0==='\n'){endOfLine();break;} 44 | if(m0==='\r'){break;} 45 | if(m0===delimiter){throw new Error('CSVDataError: Illegal quote [Row:'+options.state.rowNum+']');} 46 | throw new Error('CSVDataError: Illegal state [Row:'+options.state.rowNum+']');default:throw new Error('CSVDataError: Unknown state [Row:'+options.state.rowNum+']');}});if(entry!==''){endOfLine();} 47 | return entries;},parseEntry:function(csv,options){var separator=options.separator;var delimiter=options.delimiter;if(!options.state.rowNum){options.state.rowNum=1;} 48 | if(!options.state.colNum){options.state.colNum=1;} 49 | var entry=[];var state=0;var value='';function endOfValue(){if(options.onParseValue===undefined){entry.push(value);}else{var hook=options.onParseValue(value,options.state);if(hook!==false){entry.push(hook);}} 50 | value='';state=0;options.state.colNum++;} 51 | if(!options.match){var escSeparator=RegExp.escape(separator);var escDelimiter=RegExp.escape(delimiter);var match=/(D|S|\n|\r|[^DS\r\n]+)/;var matchSrc=match.source;matchSrc=matchSrc.replace(/S/g,escSeparator);matchSrc=matchSrc.replace(/D/g,escDelimiter);options.match=RegExp(matchSrc,'gm');} 52 | csv.replace(options.match,function(m0){switch(state){case 0:if(m0===separator){value+='';endOfValue();break;} 53 | if(m0===delimiter){state=1;break;} 54 | if(m0==='\n'||m0==='\r'){break;} 55 | value+=m0;state=3;break;case 1:if(m0===delimiter){state=2;break;} 56 | value+=m0;state=1;break;case 2:if(m0===delimiter){value+=m0;state=1;break;} 57 | if(m0===separator){endOfValue();break;} 58 | if(m0==='\n'||m0==='\r'){break;} 59 | throw new Error('CSVDataError: Illegal State [Row:'+options.state.rowNum+'][Col:'+options.state.colNum+']');case 3:if(m0===separator){endOfValue();break;} 60 | if(m0==='\n'||m0==='\r'){break;} 61 | if(m0===delimiter){throw new Error('CSVDataError: Illegal Quote [Row:'+options.state.rowNum+'][Col:'+options.state.colNum+']');} 62 | throw new Error('CSVDataError: Illegal Data [Row:'+options.state.rowNum+'][Col:'+options.state.colNum+']');default:throw new Error('CSVDataError: Unknown State [Row:'+options.state.rowNum+'][Col:'+options.state.colNum+']');}});endOfValue();return entry;}},toArray:function(csv,options,callback){var options=(options!==undefined?options:{});var config={};config.callback=((callback!==undefined&&typeof(callback)==='function')?callback:false);config.separator='separator'in options?options.separator:$.csv.defaults.separator;config.delimiter='delimiter'in options?options.delimiter:$.csv.defaults.delimiter;var state=(options.state!==undefined?options.state:{});var options={delimiter:config.delimiter,separator:config.separator,onParseEntry:options.onParseEntry,onParseValue:options.onParseValue,state:state} 63 | var entry=$.csv.parsers.parseEntry(csv,options);if(!config.callback){return entry;}else{config.callback('',entry);}},toArrays:function(csv,options,callback){var options=(options!==undefined?options:{});var config={};config.callback=((callback!==undefined&&typeof(callback)==='function')?callback:false);config.separator='separator'in options?options.separator:$.csv.defaults.separator;config.delimiter='delimiter'in options?options.delimiter:$.csv.defaults.delimiter;var data=[];var options={delimiter:config.delimiter,separator:config.separator,onParseEntry:options.onParseEntry,onParseValue:options.onParseValue,start:options.start,end:options.end,state:{rowNum:1,colNum:1}};data=$.csv.parsers.parse(csv,options);if(!config.callback){return data;}else{config.callback('',data);}},toObjects:function(csv,options,callback){var options=(options!==undefined?options:{});var config={};config.callback=((callback!==undefined&&typeof(callback)==='function')?callback:false);config.separator='separator'in options?options.separator:$.csv.defaults.separator;config.delimiter='delimiter'in options?options.delimiter:$.csv.defaults.delimiter;config.headers='headers'in options?options.headers:$.csv.defaults.headers;options.start='start'in options?options.start:1;if(config.headers){options.start++;} 64 | if(options.end&&config.headers){options.end++;} 65 | var lines=[];var data=[];var options={delimiter:config.delimiter,separator:config.separator,onParseEntry:options.onParseEntry,onParseValue:options.onParseValue,start:options.start,end:options.end,state:{rowNum:1,colNum:1},match:false};var headerOptions={delimiter:config.delimiter,separator:config.separator,start:1,end:1,state:{rowNum:1,colNum:1}} 66 | var headerLine=$.csv.parsers.splitLines(csv,headerOptions);var headers=$.csv.toArray(headerLine[0],options);var lines=$.csv.parsers.splitLines(csv,options);options.state.colNum=1;if(headers){options.state.rowNum=2;}else{options.state.rowNum=1;} 67 | for(var i=0,len=lines.length;i',a,""].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return J("flexWrap")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var L in s)C(s,L)&&(x=L.toLowerCase(),e[x]=s[L](),v.push((e[x]?"":"no-")+x));return e.input||K(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},D(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;fa||typeof i=="undefined")return 1;if(ie?0:e);++r=b&&i===n,l=[];if(f){var p=o(r);p?(i=t,r=p):f=false}for(;++ui(r,p)&&l.push(p);return f&&c(r),l}function ut(n,t,e,r){r=(r||0)-1;for(var u=n?n.length:0,o=[];++r=b&&f===n,h=u||v?a():s; 18 | for(v&&(h=o(h),f=t);++if(h,y))&&((u||v)&&h.push(y),s.push(g))}return v?(l(h.k),c(h)):u&&l(h),s}function lt(n){return function(t,e,r){var u={};e=J.createCallback(e,r,3),r=-1;var o=t?t.length:0;if(typeof o=="number")for(;++re?Ie(0,o+e):e)||0,Te(n)?i=-1o&&(o=a)}}else t=null==t&&kt(n)?r:J.createCallback(t,e,3),St(n,function(n,e,r){e=t(n,e,r),e>u&&(u=e,o=n)});return o}function Dt(n,t,e,r){if(!n)return e;var u=3>arguments.length;t=J.createCallback(t,r,4);var o=-1,i=n.length;if(typeof i=="number")for(u&&(e=n[++o]);++oarguments.length;return t=J.createCallback(t,r,4),Et(n,function(n,r,o){e=u?(u=false,n):t(e,n,r,o)}),e}function Tt(n){var t=-1,e=n?n.length:0,r=Xt(typeof e=="number"?e:0);return St(n,function(n){var e=at(0,++t);r[t]=r[e],r[e]=n}),r}function Ft(n,t,e){var r;t=J.createCallback(t,e,3),e=-1;var u=n?n.length:0;if(typeof u=="number")for(;++er?Ie(0,u+r):r||0}else if(r)return r=zt(t,e),t[r]===e?r:-1;return n(t,e,r)}function qt(n,t,e){if(typeof t!="number"&&null!=t){var r=0,u=-1,o=n?n.length:0;for(t=J.createCallback(t,e,3);++u>>1,e(n[r])e?0:e);++t=v; 29 | m?(i&&(i=ve(i)),s=f,a=n.apply(l,o)):i||(i=_e(r,v))}return m&&c?c=ve(c):c||t===h||(c=_e(u,t)),e&&(m=true,a=n.apply(l,o)),!m||c||i||(o=l=null),a}}function Ut(n){return n}function Gt(n,t,e){var r=true,u=t&&bt(t);t&&(e||u.length)||(null==e&&(e=t),o=Q,t=n,n=J,u=bt(t)),false===e?r=false:wt(e)&&"chain"in e&&(r=e.chain);var o=n,i=dt(o);St(u,function(e){var u=n[e]=t[e];i&&(o.prototype[e]=function(){var t=this.__chain__,e=this.__wrapped__,i=[e];if(be.apply(i,arguments),i=u.apply(n,i),r||t){if(e===i&&wt(i))return this; 30 | i=new o(i),i.__chain__=t}return i})})}function Ht(){}function Jt(n){return function(t){return t[n]}}function Qt(){return this.__wrapped__}e=e?Y.defaults(G.Object(),e,Y.pick(G,A)):G;var Xt=e.Array,Yt=e.Boolean,Zt=e.Date,ne=e.Function,te=e.Math,ee=e.Number,re=e.Object,ue=e.RegExp,oe=e.String,ie=e.TypeError,ae=[],fe=re.prototype,le=e._,ce=fe.toString,pe=ue("^"+oe(ce).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),se=te.ceil,ve=e.clearTimeout,he=te.floor,ge=ne.prototype.toString,ye=vt(ye=re.getPrototypeOf)&&ye,me=fe.hasOwnProperty,be=ae.push,_e=e.setTimeout,de=ae.splice,we=ae.unshift,je=function(){try{var n={},t=vt(t=re.defineProperty)&&t,e=t(n,n,n)&&t 31 | }catch(r){}return e}(),ke=vt(ke=re.create)&&ke,xe=vt(xe=Xt.isArray)&&xe,Ce=e.isFinite,Oe=e.isNaN,Ne=vt(Ne=re.keys)&&Ne,Ie=te.max,Se=te.min,Ee=e.parseInt,Re=te.random,Ae={};Ae[$]=Xt,Ae[T]=Yt,Ae[F]=Zt,Ae[B]=ne,Ae[q]=re,Ae[W]=ee,Ae[z]=ue,Ae[P]=oe,Q.prototype=J.prototype;var De=J.support={};De.funcDecomp=!vt(e.a)&&E.test(s),De.funcNames=typeof ne.name=="string",J.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:N,variable:"",imports:{_:J}},ke||(nt=function(){function n(){}return function(t){if(wt(t)){n.prototype=t; 32 | var r=new n;n.prototype=null}return r||e.Object()}}());var $e=je?function(n,t){M.value=t,je(n,"__bindData__",M)}:Ht,Te=xe||function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&ce.call(n)==$||false},Fe=Ne?function(n){return wt(n)?Ne(n):[]}:H,Be={"&":"&","<":"<",">":">",'"':""","'":"'"},We=_t(Be),qe=ue("("+Fe(We).join("|")+")","g"),ze=ue("["+Fe(Be).join("")+"]","g"),Pe=ye?function(n){if(!n||ce.call(n)!=q)return false;var t=n.valueOf,e=vt(t)&&(e=ye(t))&&ye(e);return e?n==e||ye(n)==e:ht(n) 33 | }:ht,Ke=lt(function(n,t,e){me.call(n,e)?n[e]++:n[e]=1}),Le=lt(function(n,t,e){(me.call(n,e)?n[e]:n[e]=[]).push(t)}),Me=lt(function(n,t,e){n[e]=t}),Ve=Rt,Ue=vt(Ue=Zt.now)&&Ue||function(){return(new Zt).getTime()},Ge=8==Ee(d+"08")?Ee:function(n,t){return Ee(kt(n)?n.replace(I,""):n,t||0)};return J.after=function(n,t){if(!dt(t))throw new ie;return function(){return 1>--n?t.apply(this,arguments):void 0}},J.assign=U,J.at=function(n){for(var t=arguments,e=-1,r=ut(t,true,false,1),t=t[2]&&t[2][t[1]]===n?1:r.length,u=Xt(t);++e=b&&o(r?e[r]:s)))}var p=e[0],h=-1,g=p?p.length:0,y=[];n:for(;++h(m?t(m,v):f(s,v))){for(r=u,(m||s).push(v);--r;)if(m=i[r],0>(m?t(m,v):f(e[r],v)))continue n;y.push(v)}}for(;u--;)(m=i[u])&&c(m);return l(i),l(s),y},J.invert=_t,J.invoke=function(n,t){var e=p(arguments,2),r=-1,u=typeof t=="function",o=n?n.length:0,i=Xt(typeof o=="number"?o:0);return St(n,function(n){i[++r]=(u?t:n[t]).apply(n,e)}),i},J.keys=Fe,J.map=Rt,J.mapValues=function(n,t,e){var r={}; 39 | return t=J.createCallback(t,e,3),h(n,function(n,e,u){r[e]=t(n,e,u)}),r},J.max=At,J.memoize=function(n,t){function e(){var r=e.cache,u=t?t.apply(this,arguments):m+arguments[0];return me.call(r,u)?r[u]:r[u]=n.apply(this,arguments)}if(!dt(n))throw new ie;return e.cache={},e},J.merge=function(n){var t=arguments,e=2;if(!wt(n))return n;if("number"!=typeof t[2]&&(e=t.length),3e?Ie(0,r+e):Se(e,r-1))+1);r--;)if(n[r]===t)return r;return-1},J.mixin=Gt,J.noConflict=function(){return e._=le,this},J.noop=Ht,J.now=Ue,J.parseInt=Ge,J.random=function(n,t,e){var r=null==n,u=null==t;return null==e&&(typeof n=="boolean"&&u?(e=n,n=1):u||typeof t!="boolean"||(e=t,u=true)),r&&u&&(t=1),n=+n||0,u?(t=n,n=0):t=+t||0,e||n%1||t%1?(e=Re(),Se(n+e*(t-n+parseFloat("1e-"+((e+"").length-1))),t)):at(n,t) 50 | },J.reduce=Dt,J.reduceRight=$t,J.result=function(n,t){if(n){var e=n[t];return dt(e)?n[t]():e}},J.runInContext=s,J.size=function(n){var t=n?n.length:0;return typeof t=="number"?t:Fe(n).length},J.some=Ft,J.sortedIndex=zt,J.template=function(n,t,e){var r=J.templateSettings;n=oe(n||""),e=_({},e,r);var u,o=_({},e.imports,r.imports),r=Fe(o),o=xt(o),a=0,f=e.interpolate||S,l="__p+='",f=ue((e.escape||S).source+"|"+f.source+"|"+(f===N?x:S).source+"|"+(e.evaluate||S).source+"|$","g");n.replace(f,function(t,e,r,o,f,c){return r||(r=o),l+=n.slice(a,c).replace(R,i),e&&(l+="'+__e("+e+")+'"),f&&(u=true,l+="';"+f+";\n__p+='"),r&&(l+="'+((__t=("+r+"))==null?'':__t)+'"),a=c+t.length,t 51 | }),l+="';",f=e=e.variable,f||(e="obj",l="with("+e+"){"+l+"}"),l=(u?l.replace(w,""):l).replace(j,"$1").replace(k,"$1;"),l="function("+e+"){"+(f?"":e+"||("+e+"={});")+"var __t,__p='',__e=_.escape"+(u?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}";try{var c=ne(r,"return "+l).apply(v,o)}catch(p){throw p.source=l,p}return t?c(t):(c.source=l,c)},J.unescape=function(n){return null==n?"":oe(n).replace(qe,gt)},J.uniqueId=function(n){var t=++y;return oe(null==n?"":n)+t 52 | },J.all=Ot,J.any=Ft,J.detect=It,J.findWhere=It,J.foldl=Dt,J.foldr=$t,J.include=Ct,J.inject=Dt,Gt(function(){var n={};return h(J,function(t,e){J.prototype[e]||(n[e]=t)}),n}(),false),J.first=Bt,J.last=function(n,t,e){var r=0,u=n?n.length:0;if(typeof t!="number"&&null!=t){var o=u;for(t=J.createCallback(t,e,3);o--&&t(n[o],o,n);)r++}else if(r=t,null==r||e)return n?n[u-1]:v;return p(n,Ie(0,u-r))},J.sample=function(n,t,e){return n&&typeof n.length!="number"&&(n=xt(n)),null==t||e?n?n[at(0,n.length-1)]:v:(n=Tt(n),n.length=Se(Ie(0,t),n.length),n) 53 | },J.take=Bt,J.head=Bt,h(J,function(n,t){var e="sample"!==t;J.prototype[t]||(J.prototype[t]=function(t,r){var u=this.__chain__,o=n(this.__wrapped__,t,r);return u||null!=t&&(!r||e&&typeof t=="function")?new Q(o,u):o})}),J.VERSION="2.4.1",J.prototype.chain=function(){return this.__chain__=true,this},J.prototype.toString=function(){return oe(this.__wrapped__)},J.prototype.value=Qt,J.prototype.valueOf=Qt,St(["join","pop","shift"],function(n){var t=ae[n];J.prototype[n]=function(){var n=this.__chain__,e=t.apply(this.__wrapped__,arguments); 54 | return n?new Q(e,n):e}}),St(["push","reverse","sort","unshift"],function(n){var t=ae[n];J.prototype[n]=function(){return t.apply(this.__wrapped__,arguments),this}}),St(["concat","slice","splice"],function(n){var t=ae[n];J.prototype[n]=function(){return new Q(t.apply(this.__wrapped__,arguments),this.__chain__)}}),J}var v,h=[],g=[],y=0,m=+new Date+"",b=75,_=40,d=" \t\x0B\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",w=/\b__p\+='';/g,j=/\b(__p\+=)''\+/g,k=/(__e\(.*?\)|\b__t\))\+'';/g,x=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,C=/\w*$/,O=/^\s*function[ \n\r\t]+\w/,N=/<%=([\s\S]+?)%>/g,I=RegExp("^["+d+"]*0+(?=.$)"),S=/($^)/,E=/\bthis\b/,R=/['\n\r\t\u2028\u2029\\]/g,A="Array Boolean Date Function Math Number Object RegExp String _ attachEvent clearTimeout isFinite isNaN parseInt setTimeout".split(" "),D="[object Arguments]",$="[object Array]",T="[object Boolean]",F="[object Date]",B="[object Function]",W="[object Number]",q="[object Object]",z="[object RegExp]",P="[object String]",K={}; 55 | K[B]=false,K[D]=K[$]=K[T]=K[F]=K[W]=K[q]=K[z]=K[P]=true;var L={leading:false,maxWait:0,trailing:false},M={configurable:false,enumerable:false,value:null,writable:false},V={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false},U={"\\":"\\","'":"'","\n":"n","\r":"r","\t":"t","\u2028":"u2028","\u2029":"u2029"},G=V[typeof window]&&window||this,H=V[typeof exports]&&exports&&!exports.nodeType&&exports,J=V[typeof module]&&module&&!module.nodeType&&module,Q=J&&J.exports===H&&H,X=V[typeof global]&&global;!X||X.global!==X&&X.window!==X||(G=X); 56 | var Y=s();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(G._=Y, define(function(){return Y})):H&&J?Q?(J.exports=Y)._=Y:H._=Y:G._=Y}).call(this); -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | /*jslint browser: true*/ 2 | /*jslint nomen: true*/ 3 | /*global $, _, L, topojson*/ 4 | 5 | (function () { 6 | 'use strict'; 7 | 8 | var app, 9 | data, 10 | 11 | Navigation, 12 | Status, 13 | Candidates, 14 | Map, 15 | Filter, 16 | Legend, 17 | 18 | // Default data file locations. CSVs are updated by update-data.rb. 19 | 20 | SHAPEFILE = 'data/precinct-boundaries.json', 21 | DATA_PATHS = { 22 | vtd: 'data/vtd.csv', 23 | contests: 'data/contests.csv', 24 | candidates: 'data/candidates.csv', 25 | results: 'data/results.csv' 26 | }, 27 | 28 | // Begin customizable constants. 29 | 30 | REFRESH_DELAY = 0, // Delay in seconds between checks for data. 0 to disable. 31 | 32 | TOOLTIP_DESCRIPTION = function (vtd) { 33 | return 'Ward ' + vtd.ward; 34 | }, 35 | 36 | AVAILABLE_FILTERS = { 37 | black: { 38 | name: 'Black Areas', // Labels the filter. 39 | column: 'PctBlackNonHispBridge_2010', // Must match a column in the vtd spreadsheet. 40 | description: "with a black population of" // Used for the descriptive text. 41 | }, 42 | white: { 43 | name: 'White Areas', 44 | column: 'PctWhiteNonHispBridge_2010', 45 | description: "with a white population of" 46 | }, 47 | hispanic: { 48 | name: 'Hispanic Areas', 49 | column: 'PctHisp_2010', 50 | description: "with an Hispanic population of", 51 | divider: 25 // Defaults to 50 52 | }, 53 | homeowners: { 54 | name: 'Homeowners', 55 | column: 'PctOwnerOccupiedHsgUnits_2007_11', 56 | description: "where the percentage of homes occupied by the owner is" 57 | }, 58 | income: { 59 | name: 'Avg Income', 60 | column: 'AvgFamilyIncAdj_2007_11', 61 | description: "where the average family income is", 62 | units: 'dollars', // Defaults to 'percent' 63 | min: 25000, // Defaults to 0 64 | max: 200000, // Defaults to 100 65 | step: 2500, // Defaults to 1 66 | divider: 60000, 67 | direction: 'lt' // Defaults to 'gt' for 'greater than' 68 | }, 69 | unemployment: { 70 | name: 'Unemployment', 71 | column: 'PctUnemployed_2007_11', 72 | description: "where the unemployment rate is", 73 | min: 0, 74 | max: 30, 75 | step: 0.5, 76 | divider: 7.5 77 | }, 78 | bowser: { 79 | name: 'Bowser Primary Vote', 80 | column: 'DemPrimary14_Bowser', 81 | description: "where Muriel Bowser's vote share in the Democratic primary was" 82 | }, 83 | gray: { 84 | name: 'Gray Primary Vote', 85 | column: 'DemPrimary14_Gray', 86 | description: "where Vincent Gray's vote share in the Democratic primary was" 87 | }, 88 | fenty: { 89 | name: 'Fenty 2010 Primary Vote', 90 | column: 'DemPrimary10_Fenty', 91 | description: "where Adrian Fenty's vote share in the 2010 Democratic primary was" 92 | } 93 | }; 94 | 95 | function interpolateHex(hex1, hex2, distance) { 96 | var r1 = parseInt(hex1.substr(1, 2), 16), 97 | g1 = parseInt(hex1.substr(3, 2), 16), 98 | b1 = parseInt(hex1.substr(5, 2), 16), 99 | r2 = parseInt(hex2.substr(1, 2), 16), 100 | g2 = parseInt(hex2.substr(3, 2), 16), 101 | b2 = parseInt(hex2.substr(5, 2), 16), 102 | r = Math.round(r2 + (r1 - r2) * distance).toString(16), 103 | g = Math.round(g2 + (g1 - g2) * distance).toString(16), 104 | b = Math.round(b2 + (b1 - b2) * distance).toString(16); 105 | 106 | return '#' + (r.length === 2 ? r : '0' + r) + (g.length === 2 ? g : '0' + g) + (b.length === 2 ? b : '0' + b); 107 | } 108 | 109 | $(function () { app.initialize(); }); 110 | 111 | app = { 112 | globals: { 113 | contest: '', 114 | initiative: false, 115 | view: 'winner', 116 | filteredVTDs: [] 117 | }, 118 | 119 | initialize: function () { 120 | var subscribeTo = data.subscribeTo; 121 | 122 | app.map = new Map('map', app); 123 | app.filter = new Filter('.options .filter'); 124 | 125 | data.updateAll(function (data) { 126 | app.globals.contest = data.contests[0].id; 127 | app.globals.filteredVTDs = app.filter.filteredVTDs(data.vtd); 128 | 129 | app.navigation = new Navigation('navigation', data.contests); 130 | app.status = new Status('#status', data.results); 131 | app.candidates = new Candidates('#candidates', data, app.globals); 132 | app.candidates.updateTally(data.results, app.globals); 133 | app.candidates.updateContest(app.globals); 134 | app.map.results = data.results; 135 | app.map.candidates = _.where(data.candidates, { contest: app.globals.contest }); 136 | app.map.vtdData = data.vtd; 137 | app.map.fireEvent('update', app.globals); 138 | app.legend = new Legend('#legend', data.candidates, app.globals.contest); 139 | 140 | 141 | $('.options .view a').click(function (e) { 142 | var target = $(e.target); 143 | 144 | if (!target.hasClass('selected')) { 145 | $('.options .view a.selected').removeClass('selected'); 146 | target.addClass('selected'); 147 | app.globals.view = target.data('view'); 148 | app.map.fireEvent('update', app.globals); 149 | } 150 | }); 151 | 152 | app.navigation.onChange = function (newContest) { 153 | app.globals.contest = newContest; 154 | app.globals.initiative = _.findWhere(data.contests, { id: newContest }).initiative; 155 | 156 | app.candidates.updateContest(app.globals); 157 | 158 | app.legend.updateContest(newContest); 159 | 160 | app.map.candidates = _.where(data.candidates, { contest: app.globals.contest }); 161 | app.map.fireEvent('update', app.globals); 162 | }; 163 | 164 | app.filter.onChange = function () { 165 | app.globals.filteredVTDs = app.filter.filteredVTDs(data.vtd); 166 | app.candidates.update(data.results, app.globals); 167 | app.map.fireEvent('update', app.globals); 168 | }; 169 | 170 | subscribeTo('results', app.status.update); 171 | subscribeTo('results', app.candidates.update); 172 | subscribeTo('results', function (results) { 173 | app.map.results = results; 174 | app.map.fireEvent('update', app.globals); 175 | }); 176 | }); 177 | 178 | if (REFRESH_DELAY > 0) { 179 | setInterval(function () { data.update(['results']); }, REFRESH_DELAY * 1000); 180 | } 181 | } 182 | }; 183 | 184 | Legend = function (el, candidates, contest) { 185 | this.$el = $(el); 186 | this.candidates = candidates; 187 | this.updateContest(contest); 188 | }; 189 | 190 | Legend.prototype.updateContest = function (contest) { 191 | var legend = this, 192 | candidates = _.filter(legend.candidates, function (candidate) { 193 | return !(candidate.other) && candidate.contest === contest; 194 | }), 195 | header = '
    60%
    70%
    80%
    '; 196 | 197 | legend.$el.empty(); 198 | 199 | legend.$el.append(header); 200 | 201 | _.each(candidates, function (candidate) { 202 | var div = $('
    '); 203 | 204 | div.append($('
    ').text(candidate.last_name)); 205 | div.append($('
    ').css('background', interpolateHex(candidate.color, '#D4D1D0', 0.25))); 206 | div.append($('
    ').css('background', interpolateHex(candidate.color, '#D4D1D0', 0.5))); 207 | div.append($('
    ').css('background', interpolateHex(candidate.color, '#D4D1D0', 0.75))); 208 | div.append($('
    ').css('background', candidate.color)); 209 | 210 | legend.$el.append(div); 211 | }); 212 | }; 213 | 214 | data = (function () { 215 | var addSubscriber, 216 | update, 217 | updateAll, 218 | asyncCallsForPath, 219 | pathIfNotPath, 220 | subscribers = {}, 221 | store = {}; 222 | 223 | _(DATA_PATHS).values().each(function (path) { 224 | subscribers[path] = []; 225 | store[path] = {}; 226 | }); 227 | 228 | addSubscriber = function (path, callback) { 229 | var wrappedCallback; 230 | path = pathIfNotPath(path); 231 | 232 | wrappedCallback = function () { callback(store[path]); }; 233 | subscribers[path].push(wrappedCallback); 234 | }; 235 | 236 | update = function (paths, callback) { 237 | var wrappedCallback, 238 | ajaxSet = _.map(paths, asyncCallsForPath), 239 | subscribedCallbacks = _.reduce(paths, function (memo, path) { 240 | return memo.concat(subscribers[pathIfNotPath(path)]); 241 | }, []); 242 | 243 | wrappedCallback = function () { 244 | var relabeled; 245 | if (callback) { 246 | relabeled = _.object(_.keys(DATA_PATHS), _.values(store)); 247 | callback(relabeled); 248 | } 249 | }; 250 | 251 | $.when.apply($, ajaxSet).done(subscribedCallbacks, wrappedCallback); 252 | }; 253 | 254 | updateAll = function (callback) { 255 | update(_.values(DATA_PATHS), callback); 256 | }; 257 | 258 | asyncCallsForPath = function (path) { 259 | path = pathIfNotPath(path); 260 | 261 | return $.ajax({ 262 | dataType: 'text', 263 | url: path, 264 | success: function (csv) { 265 | store[path] = $.csv.toObjects(csv); 266 | } 267 | }); 268 | }; 269 | 270 | pathIfNotPath = function (string) { 271 | if (string.match(/\./)) { return string; } 272 | 273 | return DATA_PATHS[string]; 274 | }; 275 | 276 | return { 277 | update: update, 278 | updateAll: updateAll, 279 | subscribeTo: addSubscriber 280 | }; 281 | }()); 282 | 283 | Map = function (el, app) { 284 | var map = this; 285 | 286 | this.results = this.results || {}; 287 | this.candidates = this.candidates || {}; 288 | this.vtdData = this.vtdData || {}; 289 | 290 | this.superclass(el, { 291 | dragging: false, 292 | touchZoom: false, 293 | scrollWheelZoom: false, 294 | doubleClickZoom: false, 295 | boxZoom: false, 296 | tap: false, 297 | keyboard: false, 298 | zoomControl: false, 299 | attributionControl: false 300 | }); 301 | 302 | this.marginCircles = L.layerGroup().addTo(map); 303 | 304 | function calculateMaxMargin(results, candidates) { 305 | var max = 0; 306 | 307 | _.each(results, function (vtd) { 308 | var totals = _(vtd).pick(candidates) 309 | .values() 310 | .map(function (str) { return parseInt(str, 10); }) 311 | .value().sort(function (a, b) { return b - a; }), 312 | margin = totals[0] - totals[1]; 313 | 314 | max = isNaN(margin) ? max : Math.max(max, margin); 315 | }); 316 | 317 | map.maxMargin = max; 318 | return max; 319 | } 320 | 321 | map.on({ update: function () { map.marginCircles.clearLayers(); map.maxMargin = undefined; }}); 322 | 323 | function initBoundaries(json) { 324 | map.vtds = L.geoJson(topojson.feature(json, json.objects.precincts), { 325 | style: { 326 | color: '#E8E6E5', 327 | opacity: 1, 328 | weight: 2, 329 | fillColor: '#D4D1D0', 330 | fillOpacity: 1 331 | }, 332 | onEachFeature: function (feature, layer) { 333 | var displayWinner, displayMargin, update, mouseover, mouseout; 334 | 335 | function cutscores(d) { 336 | /*jslint white: true */ 337 | return d > 0.8 ? 1 : 338 | d > 0.7 ? 0.75 : 339 | d > 0.6 ? 0.5 : 340 | 0.25 ; 341 | } 342 | 343 | displayWinner = function () { 344 | if (!_.isEmpty(map.candidates) && !_.isEmpty(map.results)) { 345 | var winnerColor, 346 | votesCast, 347 | vtdResults = _.findWhere(map.results, { vtd: feature.id }), 348 | contestResults = _(vtdResults).pick(_.pluck(map.candidates, 'id')) 349 | .map(function (v, k) { var pair = []; pair[0] = k; pair[1] = parseInt(v, 10); return pair; }) 350 | .sortBy(function (pair) { 351 | return -pair[1]; 352 | }).value(), 353 | winner = contestResults[0][0], 354 | winnerTally = contestResults[0][1], 355 | tie = contestResults[0][1] === contestResults[1][1]; 356 | 357 | if (winnerTally && !tie) { 358 | winnerColor = _.findWhere(map.candidates, { id: winner }).color; 359 | winnerColor = winnerColor === '' ? '#D4D1D0' : winnerColor; 360 | 361 | votesCast = _(contestResults).values().reduce(function (memo, pair) { return memo + pair[1]; }, 0); 362 | 363 | layer.setStyle({ fillColor: interpolateHex(winnerColor, '#D4D1D0', cutscores(winnerTally / votesCast)) }); 364 | } else { 365 | layer.setStyle({ fillColor: '#D4D1D0' }); 366 | } 367 | } 368 | 369 | layer.on({ 370 | mouseover: mouseover, 371 | mouseout: mouseout 372 | }); 373 | }; 374 | 375 | displayMargin = function () { 376 | if (!_.isEmpty(map.candidates) && !_.isEmpty(map.results)) { 377 | var winnerColor, 378 | vtdResults = _.findWhere(map.results, { vtd: feature.id }), 379 | contestResults = _(vtdResults).pick(_.pluck(map.candidates, 'id')) 380 | .map(function (v, k) { var pair = []; pair[0] = k; pair[1] = parseInt(v, 10); return pair; }) 381 | .sortBy(function (pair) { 382 | return -pair[1]; 383 | }).value(), 384 | winner = contestResults[0][0], 385 | margin = contestResults[0][1] - contestResults[1][1], 386 | maxMargin = map.maxMargin || calculateMaxMargin(map.results, _.pluck(map.candidates, 'id')); 387 | 388 | layer.setStyle({ fillColor: '#D4D1D0' }); 389 | 390 | if (margin > 0) { 391 | winnerColor = _.findWhere(map.candidates, { id: winner }).color; 392 | winnerColor = winnerColor === '' ? '#D4D1D0' : winnerColor; 393 | 394 | map.marginCircles.addLayer(L.circle(layer.getBounds().getCenter(), Math.sqrt(margin / maxMargin / 3.14) * 1000, { 395 | color: winnerColor, 396 | fillOpacity: 0.75, 397 | stroke: false 398 | }).on({ 399 | mouseover: function (e) { 400 | var vtd = _.findWhere(map.vtdData, { vtd: feature.id }); 401 | e.target.setStyle({ stroke: true }); 402 | app.candidates.update(map.results, { filteredVTDs: [feature.id] }); 403 | $('#hover-label .vtd-name').text(vtd.name); 404 | $('#hover-label .vtd-description').text(TOOLTIP_DESCRIPTION(vtd)); 405 | }, 406 | mouseout: function (e) { 407 | e.target.setStyle({ stroke: false }); 408 | app.candidates.update(map.results); 409 | $('#hover-label .vtd-name').empty(); 410 | $('#hover-label .vtd-description').empty(); 411 | } 412 | })); 413 | } 414 | } 415 | 416 | layer.off('mouseover'); 417 | layer.off('mouseout'); 418 | }; 419 | 420 | update = function (globals) { 421 | if (_.contains(globals.filteredVTDs, feature.id)) { 422 | switch (globals.view) { 423 | case 'winner': 424 | displayWinner(); 425 | break; 426 | case 'margin': 427 | displayMargin(); 428 | break; 429 | } 430 | } else { 431 | layer.setStyle({ fillColor: '#E4E1E0' }); 432 | } 433 | }; 434 | 435 | mouseover = function (e) { 436 | var vtd = _.findWhere(map.vtdData, { vtd: feature.id }); 437 | e.target.setStyle({ weight: 4 }); 438 | app.candidates.update(map.results, { filteredVTDs: [feature.id] }); 439 | $('#hover-label .vtd-name').text(vtd.name); 440 | $('#hover-label .vtd-description').text(TOOLTIP_DESCRIPTION(vtd)); 441 | }; 442 | 443 | mouseout = function (e) { 444 | e.target.setStyle({ weight: 2 }); 445 | app.candidates.update(map.results); 446 | $('#hover-label .vtd-name').empty(); 447 | $('#hover-label .vtd-description').empty(); 448 | }; 449 | 450 | map.on({ 451 | update: update 452 | }); 453 | 454 | displayWinner(); 455 | } 456 | }); 457 | 458 | map.fitBounds(map.vtds, { step: 0.1 }).addLayer(map.vtds); 459 | 460 | $(window).resize(function () { map.fitBounds(map.vtds, { step: 0.1 }); }); 461 | } 462 | 463 | $.ajax({ 464 | dataType: 'json', 465 | url: SHAPEFILE, 466 | success: initBoundaries 467 | }); 468 | }; 469 | 470 | Map.prototype = L.Map.prototype; 471 | Map.prototype.superclass = L.Map; 472 | 473 | Navigation = function (el, contests) { 474 | var navigation = this, 475 | $el = $(el), 476 | $ul = $('