├── README.md ├── Vagrantfile ├── app.py ├── requirements.txt ├── static ├── css │ └── custom.css ├── geojson │ └── us-states.json ├── js │ └── graphs.js └── lib │ ├── css │ ├── bootstrap.min.css │ ├── dc.css │ └── keen-dashboards.css │ └── js │ ├── bootstrap.min.js │ ├── crossfilter.js │ ├── d3.js │ ├── dc.js │ ├── jquery.min.js │ ├── keen.min.js │ └── queue.js └── templates └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # DonorsChoose_Visualization 2 | * Source Code for my blog post: [Interactive Data Visualization with D3.js, DC.js, Python, and MongoDB](http://adilmoujahid.com/posts/2015/01/interactive-data-visualization-d3-dc-python-mongodb/) 3 | 4 | #Visit my Blog : http://adilmoujahid.com 5 | 6 | ## Getting started 7 | 8 | The dependencies for the project can be installed using 9 | 10 | $ pip install -r requirements.txt 11 | 12 | You can use ``Vagrant`` to start a machine with a MongoDB instance running 13 | 14 | $ vagrant up 15 | 16 | To initialize the database you need to download the data 17 | 18 | $ wget https://s3.amazonaws.com/open_data/csv/opendata_projects.zip && unzip opendata_projects.zip 19 | 20 | and import it 21 | 22 | $ mongoimport -d donorschoose -c projects --type csv --file /vagrant/opendata_projects.csv -headerline 23 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | # All Vagrant configuration is done here. The most common configuration 9 | # options are documented and commented below. For a complete reference, 10 | # please see the online documentation at vagrantup.com. 11 | 12 | # Every Vagrant virtual environment requires a box to build off of. 13 | config.vm.box = "puphpet/debian75-x64" 14 | 15 | # Disable automatic box update checking. If you disable this, then 16 | # boxes will only be checked for updates when the user runs 17 | # `vagrant box outdated`. This is not recommended. 18 | # config.vm.box_check_update = false 19 | 20 | # config.vm.network "public_network", ip: "192.168.2.17" 21 | # Create a forwarded port mapping which allows access to a specific port 22 | # within the machine from a port on the host machine. In the example below, 23 | # accessing "localhost:8080" will access port 80 on the guest machine. 24 | config.vm.network "forwarded_port", guest: 27017, host: 27017 25 | 26 | # install MongoDB (bind to all the interface, SECURITY ISSUES HERE!!!!) 27 | config.vm.provision "shell", inline: "apt-get update && apt-get install -y mongodb && sed -i 's/bind_ip = 127.0.0.1/bind_ip = 0.0.0.0/' /etc/mongodb.conf && service mongodb restart" 28 | end 29 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import render_template 3 | from pymongo import MongoClient 4 | import json 5 | from bson import json_util 6 | from bson.json_util import dumps 7 | 8 | app = Flask(__name__) 9 | 10 | MONGODB_HOST = 'localhost' 11 | MONGODB_PORT = 27017 12 | DBS_NAME = 'donorschoose' 13 | COLLECTION_NAME = 'projects' 14 | FIELDS = {'school_state': True, 'resource_type': True, 'poverty_level': True, 'date_posted': True, 'total_donations': True, '_id': False} 15 | 16 | 17 | @app.route("/") 18 | def index(): 19 | return render_template("index.html") 20 | 21 | 22 | @app.route("/donorschoose/projects") 23 | def donorschoose_projects(): 24 | connection = MongoClient(MONGODB_HOST, MONGODB_PORT) 25 | collection = connection[DBS_NAME][COLLECTION_NAME] 26 | projects = collection.find(projection=FIELDS, limit=100000) 27 | #projects = collection.find(projection=FIELDS) 28 | json_projects = [] 29 | for project in projects: 30 | json_projects.append(project) 31 | json_projects = json.dumps(json_projects, default=json_util.default) 32 | connection.close() 33 | return json_projects 34 | 35 | if __name__ == "__main__": 36 | app.run(host='0.0.0.0',port=5000,debug=True) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | Jinja2==2.7.3 3 | MarkupSafe==0.23 4 | Werkzeug==0.10 5 | itsdangerous==0.24 6 | pymongo==2.8 7 | -------------------------------------------------------------------------------- /static/css/custom.css: -------------------------------------------------------------------------------- 1 | #number-projects-nd { 2 | font-size: 60px; 3 | } 4 | 5 | #total-donations-nd { 6 | font-size: 60px; 7 | } 8 | 9 | .dc-chart g.row text { 10 | fill: black; 11 | font-size: 12px; 12 | } -------------------------------------------------------------------------------- /static/js/graphs.js: -------------------------------------------------------------------------------- 1 | queue() 2 | .defer(d3.json, "/donorschoose/projects") 3 | .defer(d3.json, "static/geojson/us-states.json") 4 | .await(makeGraphs); 5 | 6 | function makeGraphs(error, projectsJson, statesJson) { 7 | 8 | //Clean projectsJson data 9 | var donorschooseProjects = projectsJson; 10 | var dateFormat = d3.time.format("%Y-%m-%d"); 11 | donorschooseProjects.forEach(function(d) { 12 | d["date_posted"] = dateFormat.parse(d["date_posted"]); 13 | d["date_posted"].setDate(1); 14 | d["total_donations"] = +d["total_donations"]; 15 | }); 16 | 17 | //Create a Crossfilter instance 18 | var ndx = crossfilter(donorschooseProjects); 19 | 20 | //Define Dimensions 21 | var dateDim = ndx.dimension(function(d) { return d["date_posted"]; }); 22 | var resourceTypeDim = ndx.dimension(function(d) { return d["resource_type"]; }); 23 | var povertyLevelDim = ndx.dimension(function(d) { return d["poverty_level"]; }); 24 | var stateDim = ndx.dimension(function(d) { return d["school_state"]; }); 25 | var totalDonationsDim = ndx.dimension(function(d) { return d["total_donations"]; }); 26 | 27 | 28 | //Calculate metrics 29 | var numProjectsByDate = dateDim.group(); 30 | var numProjectsByResourceType = resourceTypeDim.group(); 31 | var numProjectsByPovertyLevel = povertyLevelDim.group(); 32 | var totalDonationsByState = stateDim.group().reduceSum(function(d) { 33 | return d["total_donations"]; 34 | }); 35 | 36 | var all = ndx.groupAll(); 37 | var totalDonations = ndx.groupAll().reduceSum(function(d) {return d["total_donations"];}); 38 | 39 | var max_state = totalDonationsByState.top(1)[0].value; 40 | 41 | //Define values (to be used in charts) 42 | var minDate = dateDim.bottom(1)[0]["date_posted"]; 43 | var maxDate = dateDim.top(1)[0]["date_posted"]; 44 | 45 | //Charts 46 | var timeChart = dc.barChart("#time-chart"); 47 | var resourceTypeChart = dc.rowChart("#resource-type-row-chart"); 48 | var povertyLevelChart = dc.rowChart("#poverty-level-row-chart"); 49 | var usChart = dc.geoChoroplethChart("#us-chart"); 50 | var numberProjectsND = dc.numberDisplay("#number-projects-nd"); 51 | var totalDonationsND = dc.numberDisplay("#total-donations-nd"); 52 | 53 | numberProjectsND 54 | .formatNumber(d3.format("d")) 55 | .valueAccessor(function(d){return d; }) 56 | .group(all); 57 | 58 | totalDonationsND 59 | .formatNumber(d3.format("d")) 60 | .valueAccessor(function(d){return d; }) 61 | .group(totalDonations) 62 | .formatNumber(d3.format(".3s")); 63 | 64 | timeChart 65 | .width(600) 66 | .height(160) 67 | .margins({top: 10, right: 50, bottom: 30, left: 50}) 68 | .dimension(dateDim) 69 | .group(numProjectsByDate) 70 | .transitionDuration(500) 71 | .x(d3.time.scale().domain([minDate, maxDate])) 72 | .elasticY(true) 73 | .xAxisLabel("Year") 74 | .yAxis().ticks(4); 75 | 76 | resourceTypeChart 77 | .width(300) 78 | .height(250) 79 | .dimension(resourceTypeDim) 80 | .group(numProjectsByResourceType) 81 | .xAxis().ticks(4); 82 | 83 | povertyLevelChart 84 | .width(300) 85 | .height(250) 86 | .dimension(povertyLevelDim) 87 | .group(numProjectsByPovertyLevel) 88 | .xAxis().ticks(4); 89 | 90 | 91 | usChart.width(1000) 92 | .height(330) 93 | .dimension(stateDim) 94 | .group(totalDonationsByState) 95 | .colors(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]) 96 | .colorDomain([0, max_state]) 97 | .overlayGeoJson(statesJson["features"], "state", function (d) { 98 | return d.properties.name; 99 | }) 100 | .projection(d3.geo.albersUsa() 101 | .scale(600) 102 | .translate([340, 150])) 103 | .title(function (p) { 104 | return "State: " + p["key"] 105 | + "\n" 106 | + "Total Donations: " + Math.round(p["value"]) + " $"; 107 | }) 108 | 109 | dc.renderAll(); 110 | 111 | }; -------------------------------------------------------------------------------- /static/lib/css/dc.css: -------------------------------------------------------------------------------- 1 | div.dc-chart { 2 | float: left; 3 | } 4 | 5 | .dc-chart rect.bar { 6 | stroke: none; 7 | cursor: pointer; 8 | } 9 | 10 | .dc-chart rect.bar:hover { 11 | fill-opacity: .5; 12 | } 13 | 14 | .dc-chart rect.stack1 { 15 | stroke: none; 16 | fill: red; 17 | } 18 | 19 | .dc-chart rect.stack2 { 20 | stroke: none; 21 | fill: green; 22 | } 23 | 24 | .dc-chart rect.deselected { 25 | stroke: none; 26 | fill: #ccc; 27 | } 28 | 29 | .dc-chart .pie-slice { 30 | fill: white; 31 | font-size: 12px; 32 | cursor: pointer; 33 | } 34 | 35 | .dc-chart .pie-slice :hover { 36 | fill-opacity: .8; 37 | } 38 | 39 | .dc-chart .selected path { 40 | stroke-width: 3; 41 | stroke: #ccc; 42 | fill-opacity: 1; 43 | } 44 | 45 | .dc-chart .deselected path { 46 | stroke: none; 47 | fill-opacity: .5; 48 | fill: #ccc; 49 | } 50 | 51 | .dc-chart .axis path, .axis line { 52 | fill: none; 53 | stroke: #000; 54 | shape-rendering: crispEdges; 55 | } 56 | 57 | .dc-chart .axis text { 58 | font: 10px sans-serif; 59 | } 60 | 61 | .dc-chart .grid-line { 62 | fill: none; 63 | stroke: #ccc; 64 | opacity: .5; 65 | shape-rendering: crispEdges; 66 | } 67 | 68 | .dc-chart .grid-line line { 69 | fill: none; 70 | stroke: #ccc; 71 | opacity: .5; 72 | shape-rendering: crispEdges; 73 | } 74 | 75 | .dc-chart .brush rect.background { 76 | z-index: -999; 77 | } 78 | 79 | .dc-chart .brush rect.extent { 80 | fill: steelblue; 81 | fill-opacity: .125; 82 | } 83 | 84 | .dc-chart .brush .resize path { 85 | fill: #eee; 86 | stroke: #666; 87 | } 88 | 89 | .dc-chart path.line { 90 | fill: none; 91 | stroke-width: 1.5px; 92 | } 93 | 94 | .dc-chart circle.dot { 95 | stroke: none; 96 | } 97 | 98 | .dc-chart g.dc-tooltip path { 99 | fill: none; 100 | stroke: grey; 101 | stroke-opacity: .8; 102 | } 103 | 104 | .dc-chart path.area { 105 | fill-opacity: .3; 106 | stroke: none; 107 | } 108 | 109 | .dc-chart .node { 110 | font-size: 0.7em; 111 | cursor: pointer; 112 | } 113 | 114 | .dc-chart .node :hover { 115 | fill-opacity: .8; 116 | } 117 | 118 | .dc-chart .selected circle { 119 | stroke-width: 3; 120 | stroke: #ccc; 121 | fill-opacity: 1; 122 | } 123 | 124 | .dc-chart .deselected circle { 125 | stroke: none; 126 | fill-opacity: .5; 127 | fill: #ccc; 128 | } 129 | 130 | .dc-chart .bubble { 131 | stroke: none; 132 | fill-opacity: 0.6; 133 | } 134 | 135 | .dc-data-count { 136 | float: right; 137 | margin-top: 15px; 138 | margin-right: 15px; 139 | } 140 | 141 | .dc-data-count .filter-count { 142 | color: #3182bd; 143 | font-weight: bold; 144 | } 145 | 146 | .dc-data-count .total-count { 147 | color: #3182bd; 148 | font-weight: bold; 149 | } 150 | 151 | .dc-data-table { 152 | } 153 | 154 | .dc-chart g.state { 155 | cursor: pointer; 156 | } 157 | 158 | .dc-chart g.state :hover { 159 | fill-opacity: .8; 160 | } 161 | 162 | .dc-chart g.state path { 163 | stroke: white; 164 | } 165 | 166 | .dc-chart g.selected path { 167 | } 168 | 169 | .dc-chart g.deselected path { 170 | fill: grey; 171 | } 172 | 173 | .dc-chart g.selected text { 174 | } 175 | 176 | .dc-chart g.deselected text { 177 | display: none; 178 | } 179 | 180 | .dc-chart g.county path { 181 | stroke: white; 182 | fill: none; 183 | } 184 | 185 | .dc-chart g.debug rect { 186 | fill: blue; 187 | fill-opacity: .2; 188 | } 189 | 190 | .dc-chart g.row rect { 191 | fill-opacity: 0.8; 192 | cursor: pointer; 193 | } 194 | 195 | .dc-chart g.row rect:hover { 196 | fill-opacity: 0.6; 197 | } 198 | 199 | .dc-chart g.row text { 200 | fill: white; 201 | font-size: 12px; 202 | cursor: pointer; 203 | } 204 | 205 | .dc-legend { 206 | font-size: 11px; 207 | } 208 | 209 | .dc-legend-item { 210 | cursor: pointer; 211 | } 212 | 213 | .dc-chart g.axis text { 214 | /* Makes it so the user can't accidentally click and select text that is meant as a label only */ 215 | -webkit-user-select: none; /* Chrome/Safari */ 216 | -moz-user-select: none; /* Firefox */ 217 | -ms-user-select: none; /* IE10 */ 218 | -o-user-select: none; 219 | user-select: none; 220 | pointer-events: none; 221 | } 222 | 223 | .dc-chart path.highlight { 224 | stroke-width: 3; 225 | fill-opacity: 1; 226 | stroke-opacity: 1; 227 | } 228 | 229 | .dc-chart .highlight { 230 | fill-opacity: 1; 231 | stroke-opacity: 1; 232 | } 233 | 234 | .dc-chart .fadeout { 235 | fill-opacity: 0.2; 236 | stroke-opacity: 0.2; 237 | } 238 | 239 | .dc-chart path.dc-symbol { 240 | fill-opacity: 0.5; 241 | stroke-opacity: 0.5; 242 | } 243 | 244 | .dc-hard .number-display { 245 | float: none; 246 | } 247 | -------------------------------------------------------------------------------- /static/lib/css/keen-dashboards.css: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Full-page application style 4 | */ 5 | 6 | body.application { 7 | background: #f2f2f2; 8 | padding: 60px 20px 0; 9 | } 10 | body.application > .container-fluid { 11 | padding-left: 0px; 12 | padding-right: 0px; 13 | } 14 | body.application div[class^="col-"] { 15 | padding-left: 5px; 16 | padding-right: 5px; 17 | } 18 | body.application div[class^="col-"] div[class^="col-"] { 19 | padding-left: 15px; 20 | padding-right: 15px; 21 | } 22 | 23 | body.application hr { 24 | border-color: #d7d7d7; 25 | margin: 10px 0; 26 | } 27 | 28 | 29 | 30 | .navbar-inverse { 31 | background-color: #3d4a57; 32 | border-color: #333; 33 | } 34 | .navbar-inverse .navbar-nav > li > a, 35 | .navbar a.navbar-brand { 36 | color: #fbfbfb; 37 | text-decoration: none; 38 | } 39 | 40 | .chart-wrapper { 41 | background: #fff; 42 | border: 1px solid #e2e2e2; 43 | border-radius: 3px; 44 | margin-bottom: 10px; 45 | } 46 | .chart-wrapper .chart-title { 47 | border-bottom: 1px solid #d7d7d7; 48 | color: #666; 49 | font-size: 14px; 50 | font-weight: 200; 51 | padding: 7px 10px 4px; 52 | } 53 | 54 | .chart-wrapper .chart-stage { 55 | /*min-height: 240px;*/ 56 | overflow: hidden; 57 | padding: 5px 10px; 58 | position: relative; 59 | } 60 | 61 | .chart-wrapper .chart-notes { 62 | background: #fbfbfb; 63 | border-top: 1px solid #e2e2e2; 64 | color: #808080; 65 | font-size: 12px; 66 | padding: 8px 10px 5px; 67 | } 68 | -------------------------------------------------------------------------------- /static/lib/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.to=function(b){var c=this,d=this.getItemIndex(this.$active=this.$element.find(".item.active"));return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=e[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:g});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,f&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(e)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:g});return a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one("bsTransitionEnd",function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger(m)),f&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(b=!b),e||d.data("bs.collapse",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};c.VERSION="3.2.0",c.DEFAULTS={toggle:!0},c.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},c.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var c=a.Event("show.bs.collapse");if(this.$element.trigger(c),!c.isDefaultPrevented()){var d=this.$parent&&this.$parent.find("> .panel > .in");if(d&&d.length){var e=d.data("bs.collapse");if(e&&e.transitioning)return;b.call(d,"hide"),e||d.data("bs.collapse",null)}var f=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[f](0),this.transitioning=1;var g=function(){this.$element.removeClass("collapsing").addClass("collapse in")[f](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return g.call(this);var h=a.camelCase(["scroll",f].join("-"));this.$element.one("bsTransitionEnd",a.proxy(g,this)).emulateTransitionEnd(350)[f](this.$element[0][h])}}},c.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},c.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var d=a.fn.collapse;a.fn.collapse=b,a.fn.collapse.Constructor=c,a.fn.collapse.noConflict=function(){return a.fn.collapse=d,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(c){var d,e=a(this),f=e.attr("data-target")||c.preventDefault()||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),g=a(f),h=g.data("bs.collapse"),i=h?"toggle":e.data(),j=e.attr("data-parent"),k=j&&a(j);h&&h.transitioning||(k&&k.find('[data-toggle="collapse"][data-parent="'+j+'"]').not(e).addClass("collapsed"),e[g.hasClass("in")?"addClass":"removeClass"]("collapsed")),b.call(g,i)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.2.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('