├── .gitattributes ├── .gitignore ├── README.md ├── app.py ├── s1.png ├── 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 /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interactive Dashboard Using Python Flask,MongoDB, and Javascript 2 | 3 | **COMPANY MASTER DATA (Source: [https://data.gov.in/])** 4 | 5 | I have used MongoDB for storing and querying the data, Python for building a web server that interacts with MongoDB and serving html pages, Javascript libraries d3.js, dc.js and crossfilter.js for building interactive charts. 6 | 7 | For building the charts, I have used 3 Javascript libraries: crossfilter.js, d3.js and dc.js. 8 | 9 | *crossfilter.js is a Javascript library for grouping, filtering, and aggregating large datasets. 10 | *d3.js is a Javascript library for controlling the data and building charts. 11 | *dc.js is a Javascript charting library that leverages both crossfilter.js and d3.js, and makes the creation of highly interactive data visualization simple. 12 | 13 | Mongodb Connection 14 | ``` 15 | app = Flask(__name__) 16 | 17 | MONGODB_HOST = 'localhost' 18 | MONGODB_PORT = 27017 19 | DBS_NAME = 'delhi' 20 | COLLECTION_NAME = 'projects' 21 | FIELDS = {'AUTHORIZED_CAPITAL':True,'PAIDUP_CAPITAL':True,'COMPANY_STATUS':True,'PRINCIPAL_BUSINESS_ACTIVITY':True,'COMPANY_NAME':True,'COMPANY_CLASS':True,'_id':False} 22 | ``` 23 | 24 | ``` 25 | if __name__ == "__main__": 26 | app.run(host='0.0.0.0',port=5000,debug=True) 27 | ``` 28 | 29 | .defer is for reading the projects data 30 | ``` 31 | d3.json("delhi/projects", function (projectsJson) 32 | ``` 33 | 34 | Creating a Crossfilter instance: 35 | ``` 36 | var ndx = crossfilter(projectsJson); 37 | ``` 38 | Defining 5 data dimensions, namely Authorized Capital, Company Class, Principal Business Activity, Company Status, and Company Name 39 | ``` 40 | var pieTypeDim = ndx.dimension(function(d) { return d.AUTHORIZED_CAPITAL; }); 41 | var pieDim = ndx.dimension(function(d) { return d.COMPANY_CLASS; }); 42 | var rowLevelDim = ndx.dimension(function(d) { return d.PRINCIPAL_BUSINESS_ACTIVITY; }); 43 | var bubbleDim = ndx.dimension(function(d) { return d.COMPANY_STATUS; }); 44 | var DataDim = ndx.dimension(function(d) { return d.COMPANY_NAME; }); 45 | ``` 46 | Defining Data groups: 47 | ``` 48 | var numProjectsByResourceType = pieTypeDim.group() 49 | .reduceCount(function(d){return d.AUTHORIZED_CAPITAL;}); 50 | var pieDimType = pieDim.group() 51 | .reduceCount(function(d){return d.COMPANY_CLASS;}); 52 | var numProjectsByPovertyLevel = rowLevelDim.group() 53 | .reduceCount(function(d){return d.PRINCIPAL_BUSINESS_ACTIVITY;}); 54 | var bubbleDimgroup = bubbleDim.group() 55 | .reduceCount(function(d){return d.COMPANY_NAME;}); 56 | var DataDimgroup = DataDim.group() 57 | .reduceCount(function(d){return d.COMPANY_STATUS;}); 58 | ``` 59 | Defining DC Charts: 60 | ``` 61 | var rowChart = dc.pieChart("#pie-chart"); 62 | var row1Chart = dc.rowChart("#row-chart"); 63 | var bubbleChart = dc.rowChart("#bubble-chart"); 64 | var dataTable = dc.dataTable("#data-chart"); 65 | ``` 66 | 67 | #Screenshot 68 | 69 | ![ScreenShot](https://github.com/praritlamba/Dashboard-Python-Flask-MongoDB-and-JavaScript/blob/master/s1.png) 70 | -------------------------------------------------------------------------------- /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 = 'delhi' 13 | COLLECTION_NAME = 'projects' 14 | FIELDS = {'AUTHORIZED_CAPITAL':True,'PAIDUP_CAPITAL':True,'COMPANY_STATUS':True,'PRINCIPAL_BUSINESS_ACTIVITY':True,'COMPANY_NAME':True,'COMPANY_CLASS':True,'_id':False} 15 | 16 | @app.route("/") 17 | def index(): 18 | return render_template("index.html") 19 | 20 | @app.route("/delhi/projects") 21 | def donorschoose_projects(): 22 | connection = MongoClient(MONGODB_HOST, MONGODB_PORT) 23 | collection = connection[DBS_NAME][COLLECTION_NAME] 24 | projects = collection.find(projection=FIELDS, limit=10000) 25 | json_projects = [] 26 | for project in projects: 27 | json_projects.append(project) 28 | json_projects = json.dumps(json_projects, default=json_util.default) 29 | connection.close() 30 | return json_projects 31 | 32 | if __name__ == "__main__": 33 | app.run(host='0.0.0.0',port=5000,debug=True) -------------------------------------------------------------------------------- /s1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/praritlamba/Dashboard-Python-Flask-MongoDB-and-JavaScript/28db8b58795f65b1344ec9bc45ef27d383450214/s1.png -------------------------------------------------------------------------------- /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 | 2 | d3.json("delhi/projects", function (projectsJson) { 3 | 4 | 5 | 6 | var ndx = crossfilter(projectsJson); 7 | 8 | 9 | 10 | 11 | var pieTypeDim = ndx.dimension(function(d) { return d.AUTHORIZED_CAPITAL; }); 12 | var pieDim = ndx.dimension(function(d) { return d.COMPANY_CLASS; }); 13 | var rowLevelDim = ndx.dimension(function(d) { return d.PRINCIPAL_BUSINESS_ACTIVITY; }); 14 | var bubbleDim = ndx.dimension(function(d) { return d.COMPANY_STATUS; }); 15 | var DataDim = ndx.dimension(function(d) { return d.COMPANY_NAME; }); 16 | 17 | 18 | var numProjectsByResourceType = pieTypeDim.group() 19 | .reduceCount(function(d){return d.AUTHORIZED_CAPITAL;}); 20 | var pieDimType = pieDim.group() 21 | .reduceCount(function(d){return d.COMPANY_CLASS;}); 22 | var numProjectsByPovertyLevel = rowLevelDim.group() 23 | .reduceCount(function(d){return d.PRINCIPAL_BUSINESS_ACTIVITY;}); 24 | var bubbleDimgroup = bubbleDim.group() 25 | .reduceCount(function(d){return d.COMPANY_NAME;}); 26 | 27 | 28 | 29 | var DataDimgroup = DataDim.group() 30 | .reduceCount(function(d){return d.COMPANY_STATUS;}); 31 | 32 | var rowChart = dc.pieChart("#pie-chart"); 33 | var row1Chart = dc.rowChart("#row-chart"); 34 | var bubbleChart = dc.rowChart("#bubble-chart"); 35 | var dataTable = dc.dataTable("#data-chart"); 36 | 37 | rowChart 38 | .width(400) 39 | .height(400) 40 | .dimension(pieTypeDim) 41 | .group(numProjectsByResourceType) 42 | .radius(200) 43 | .slicesCap(5) 44 | .legend(dc.legend().gap(3)); 45 | 46 | 47 | dataTable 48 | .width(960) 49 | .height(800) 50 | 51 | .dimension(DataDim) 52 | .group(function(d) { return d.COMPANY_NAME; 53 | }) 54 | 55 | .columns([ 56 | function(d) { return d.COMPANY_STATUS; }, 57 | function(d) { return d.COMPANY_CLASS; }, 58 | 59 | function(d) { return d.PAIDUP_CAPITAL; }, 60 | function(d) { return d.AUTHORIZED_CAPITAL; }, 61 | 62 | function(d) { return d.PRINCIPAL_BUSINESS_ACTIVITY; }, 63 | ]) 64 | .sortBy(function(d){ return d.PRINCIPAL_BUSINESS_ACTIVITY; }) 65 | .order(d3.ascending); 66 | 67 | 68 | bubbleChart 69 | .width(700) 70 | .height(150) 71 | .dimension(pieDim) 72 | .group(pieDimType) 73 | .xAxis().tickFormat(); 74 | 75 | row1Chart 76 | .width(600) 77 | .height(600) 78 | .dimension(rowLevelDim) 79 | .group(numProjectsByPovertyLevel) 80 | .xAxis().tickFormat(50); 81 | 82 | 83 | 84 | dc.renderAll(); 85 | 86 | }); -------------------------------------------------------------------------------- /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('