├── webpack.config.prod.js ├── resources ├── styles │ ├── style.scss │ ├── map.sass │ ├── layout.sass │ ├── bootstrap.scss │ └── _variables.scss ├── images │ ├── heat-circle.png │ ├── heat-circle-dark.png │ └── heat-circle.svg └── files │ ├── line_colors_grouped.json │ └── line_colors.json ├── .gitignore ├── app ├── test.js ├── components │ ├── Mapbox.js │ ├── SubwayLines.js │ ├── MapControls.js │ ├── SubwayMap.js │ └── HeatMap.js ├── utils │ └── DomUtils.js ├── index.js └── ui │ ├── UiControls.js │ └── UiCallbacks.js ├── package.json ├── LICENSE ├── README.md ├── webpack.config.js └── public ├── index.html └── bundle.js /webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/styles/style.scss: -------------------------------------------------------------------------------- 1 | @import 'bootstrap'; 2 | @import './layout'; 3 | @import './map'; -------------------------------------------------------------------------------- /resources/images/heat-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piratefsh/mta-maps/HEAD/resources/images/heat-circle.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.orig 3 | /*.log 4 | /node_modules 5 | /dist 6 | /bower_components 7 | /*.sublime-workspace 8 | -------------------------------------------------------------------------------- /app/test.js: -------------------------------------------------------------------------------- 1 | export default function test(){ 2 | const a = [1,2,3,4,5,6] 3 | a.forEach(m => console.log(m)) 4 | } -------------------------------------------------------------------------------- /resources/images/heat-circle-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piratefsh/mta-maps/HEAD/resources/images/heat-circle-dark.png -------------------------------------------------------------------------------- /resources/files/line_colors_grouped.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors": { 3 | "123": "#EE352E", 4 | "456": "#00933C", 5 | "7": "#B933AD", 6 | "ACE": "#2850AD", 7 | "BDFM": "#FF6319", 8 | "G": "#6CBE45", 9 | "JZ": "#996633", 10 | "NQR": "#FCCC0A", 11 | "S": "#808183", 12 | "L": "#A7A9AC" 13 | } 14 | } -------------------------------------------------------------------------------- /app/components/Mapbox.js: -------------------------------------------------------------------------------- 1 | import 'mapbox.js' 2 | import 'mapbox.js/theme/style.css' 3 | import 'leaflet.label' 4 | import 'leaflet.label/dist/leaflet.label.css' 5 | import 'simpleheat' 6 | import 'leaflet.heat/src/HeatLayer' 7 | 8 | export default function Mapbox(){ 9 | L.mapbox.accessToken = 'pk.eyJ1IjoicGlyYXRlZnNoIiwiYSI6IjlNT2dMUGcifQ.cj4j9z29wjkXPAi7nK7ArA'; 10 | return L 11 | } 12 | 13 | -------------------------------------------------------------------------------- /resources/images/heat-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/files/line_colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors": { 3 | "1": "#EE352E", 4 | "3": "#EE352E", 5 | "2": "#EE352E", 6 | "5": "#00933C", 7 | "4": "#00933C", 8 | "7": "#B933AD", 9 | "6": "#00933C", 10 | "A": "#2850AD", 11 | "C": "#2850AD", 12 | "B": "#FF6319", 13 | "E": "#2850AD", 14 | "D": "#FF6319", 15 | "G": "#6CBE45", 16 | "F": "#FF6319", 17 | "J": "#996633", 18 | "M": "#FF6319", 19 | "L": "#A7A9AC", 20 | "N": "#FCCC0A", 21 | "Q": "#FCCC0A", 22 | "S": "#808183", 23 | "R": "#FCCC0A", 24 | "Z": "#996633" 25 | } 26 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mta-map", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "babel-core": "^5.8.25", 13 | "babel-loader": "^5.3.2", 14 | "babel-runtime": "^5.8.25", 15 | "bootstrap-sass": "^3.3.5", 16 | "css-loader": "^0.19.0", 17 | "extract-text-webpack-plugin": "^0.8.2", 18 | "file-loader": "^0.8.4", 19 | "json-loader": "^0.5.3", 20 | "sass-loader": "^3.0.0", 21 | "style-loader": "^0.12.4", 22 | "url-loader": "^0.5.6", 23 | "webpack": "^1.12.2" 24 | }, 25 | "dependencies": { 26 | "leaflet-transitionedicon": "git://github.com/piratefsh/leaflet-transitionedicon.git", 27 | "leaflet.heat": "^0.1.3", 28 | "leaflet.label": "git://github.com/Leaflet/Leaflet.label.git", 29 | "mapbox.js": "^2.2.2", 30 | "simpleheat": "^0.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/components/SubwayLines.js: -------------------------------------------------------------------------------- 1 | import LineColors from '!json!files/line_colors.json' 2 | import LineColorsGrouped from '!json!files/line_colors_grouped.json' 3 | 4 | export default class SubwayLines{ 5 | 6 | constructor(){ 7 | this.colors = LineColors.colors 8 | this.lines = LineColorsGrouped.colors 9 | } 10 | 11 | format(c, l){ 12 | return `${l}` 13 | } 14 | 15 | getIcon(singleLine){ 16 | const l = singleLine 17 | const c = this.colors[l] 18 | return this.format(c, l) 19 | } 20 | 21 | getAllIcons(){ 22 | const lines = [] 23 | const sortedKeys = Object.keys(this.lines).sort() 24 | for (let key of sortedKeys){ 25 | const indivLines = key.split("") 26 | const c = this.lines[key] 27 | const icons = indivLines.map(l => this.format(c, l)) 28 | const line = `
${icons.join('')}
` 29 | lines[key] = line 30 | } 31 | return lines 32 | } 33 | 34 | 35 | } -------------------------------------------------------------------------------- /app/utils/DomUtils.js: -------------------------------------------------------------------------------- 1 | export default { 2 | initToggle(elem, options){ 3 | /** Creates a toggle button 4 | options = { 5 | state: () => 'function to test if toggle is on. 6 | returns true if is on, false if otherwise', 7 | on: { 8 | text: 'innerHTML to show when toggle on' 9 | callback: 'thing to do when toggle on' 10 | }, 11 | off: { 12 | text: 'innerHTML to show when toggle off' 13 | callback: 'thing to do when toggle off' 14 | } 15 | } 16 | **/ 17 | elem.onclick = e => { 18 | //on 19 | if (options.state() == true) { 20 | options.on.callback(e) 21 | elem.innerHTML = options.on.text 22 | } 23 | //off 24 | else{ 25 | options.off.callback(e) 26 | elem.innerHTML = options.off.text 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sher Minn Chong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/components/MapControls.js: -------------------------------------------------------------------------------- 1 | export default class MapControls{ 2 | constructor(map){ 3 | this.map = map 4 | this.stationsVisible = true 5 | this.removeLayer = (linename, layer) => { 6 | this.map.removeLayer(layer) 7 | } 8 | } 9 | 10 | showLines(lines){ 11 | this.showStations(lines) 12 | this.showHeats(lines) 13 | } 14 | 15 | showHeats(lines){ 16 | const showL = (linename, layer) => { 17 | lines.forEach(l => { 18 | if(linename.indexOf(l) > -1){ 19 | layer.addTo(this.map).bringToFront() 20 | } 21 | }) 22 | } 23 | 24 | this.map.eachHeatLayer('entries', this.removeLayer) 25 | this.map.eachHeatLayer('exits', this.removeLayer) 26 | this.map.eachHeatLayer('entries', showL) 27 | this.map.eachHeatLayer('exits', showL) 28 | } 29 | 30 | showStations(lines){ 31 | if(this.stationsVisible) { 32 | this.map.eachLineLayer(this.removeLayer) 33 | this.map.eachLineLayer((linename, layer) => { 34 | lines.forEach(l => { 35 | if(linename.indexOf(l) > -1){ 36 | layer.addTo(this.map).bringToFront() 37 | } 38 | }) 39 | }) 40 | } 41 | } 42 | 43 | toggleStations(show, lines){ 44 | this.stationsVisible = show 45 | if(show){ 46 | this.showStations(lines) 47 | } 48 | else{ 49 | this.map.eachLineLayer((name, layer) => this.map.removeLayer(layer)) 50 | } 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NYC MTA Subway Traffic Visualizer 2 | 3 | A weeks worth of entrances and exits for every station on the NYC Subway system visualized on a map. Filter via lines and zoom in to see station names. 4 | 5 | ## Try it out 6 | [Over here!](http://piratefsh.github.io/mta-maps/public) 7 | 8 | ## Where is this data from? 9 | [Turnstile data](web.mta.info/developers/turnstile.html) taken from the MTA. Station location processed from [chriswhong/nycturnstiles](https://github.com/chriswhong/nycturnstiles), with the new added 34th St Hudson Yd station. 10 | 11 | 12 | ## Development 13 | 14 | All source code is in `app` and `public/index.html`. Data is located in `resources/files` in JSON format. 15 | 16 | JSON data generated with scripts [here](https://github.com/piratefsh/mta-turnstile-cruncher), which were scraped with some other [scripts](https://github.com/piratefsh/mta-turnstile-scraper). 17 | 18 | ### Serve 19 | This project runs on [Webpack](https://webpack.github.io/). To serve at http://localhost:8080/webpack-dev-server: 20 | 21 | ``` 22 | webpack-dev-server --inline --content-base public/ 23 | 24 | ``` 25 | 26 | ### Build 27 | To compile for production: 28 | 29 | ``` 30 | webpack --config webpack.config.js 31 | ``` 32 | 33 | ## To-do 34 | Currently only displays data for a week, although it can technicaly display data for any period of time, given the right data in JSON. Possible work: build a proper API for that purpose. 35 | 36 | 37 | ## Inspiration 38 | This project was built at Recurse Center, Fall 2015 and was inspired by fellow Recurser [Harry Truong](https://github.com/harrytruong) and this article: [Visualizing the MTA's Turnstile Data](chriswhong.com/open-data/visualizing-the-mtas-turnstile-data/) -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | webpack = require('webpack'), 3 | ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | module.exports = { 6 | entry: { 7 | app: ['./app/index.js'] 8 | }, 9 | output: { 10 | path: path.resolve(__dirname, 'public'), 11 | publicPath: '/', 12 | filename: 'bundle.js' 13 | }, 14 | devtool: 'eval', 15 | module: { 16 | loaders: [ 17 | // Babel loader 18 | { 19 | test: /\.jsx?$/, 20 | exclude: /(node_modules|bower_components)/, 21 | loader: 'babel?optional[]=runtime' 22 | }, 23 | // CSS loader 24 | { 25 | test: /\.css$/, 26 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader') 27 | }, 28 | // SASS/SCSS 29 | { 30 | test: /\.(sass|scss)$/, 31 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader!sass-loader') 32 | }, 33 | { 34 | test: /\.json$/, 35 | loader: 'json' 36 | }, 37 | { 38 | test: /\.jpe?g$|\.gif$|\.png$|\.svg$/i, 39 | loader: 'file' 40 | }, 41 | { 42 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, 43 | loader: "url-loader?limit=10000&minetype=application/font-woff" 44 | }, 45 | { 46 | test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, 47 | loader: "file-loader" 48 | } 49 | ] 50 | }, 51 | resolve: { 52 | // where to find modules 53 | modulesDirectories: [ 54 | 'node_modules', 55 | 'resources', 56 | 'app' 57 | ], 58 | extensions: ['.js', '.json', ''] 59 | }, 60 | plugins: [ 61 | new ExtractTextPlugin('styles.css') 62 | ] 63 | } -------------------------------------------------------------------------------- /resources/styles/map.sass: -------------------------------------------------------------------------------- 1 | #map-container, #map 2 | width: 100% 3 | height: 100vh 4 | background-color: #444444 5 | 6 | .map-popup 7 | min-width: 100px 8 | .title 9 | font-weight: bold 10 | display: block 11 | margin-bottom: 4px 12 | .unit 13 | color: #999999 14 | 15 | // round line alphanum icons 16 | .line-icon 17 | $size: 20px 18 | border-radius: 50% 19 | width: $size 20 | height: $size 21 | display: inline-block 22 | font-size: 1em 23 | font-weight: bold 24 | text-align: center 25 | color: white 26 | margin-right: 4px 27 | margin-bottom: 4px 28 | 29 | .heat-icon 30 | $size: 2px 31 | border-radius: 50% 32 | min-width: $size 33 | min-height: $size 34 | transition: all 2s 35 | &.heat-icon-entries 36 | background-color: rgba(white, 0.2) 37 | box-shadow: 0px 0px 5px rgba(white, 0.5) 38 | border: 1px solid rgba(white, 0.3) 39 | &.heat-icon-exits 40 | background-color: rgba(red, 0.2) 41 | box-shadow: 0px 0px 5px rgba(red, 0.5) 42 | border: 1px solid rgba(red, 0.3) 43 | 44 | .heat-transition-enter 45 | opacity: 0 46 | .heat-transition-enter-active 47 | opacity: 1 48 | .heat-transition-leave 49 | 50 | // labels 51 | .leaflet-label 52 | &.station-name 53 | $stroke-color: #333333 54 | $stroke-width: 1px 55 | background: none 56 | color: #eeeeee 57 | display: inline-block 58 | font-size: 0.8em 59 | font-weight: normal 60 | line-height: 1em 61 | text-shadow: 1px 1px 1px $stroke-color, 1px 1px 1px $stroke-color 62 | 63 | // overwrite leaflet styles 64 | .leaflet-label 65 | transition: opacity 0.5s 66 | background-color: transparent 67 | border: none 68 | padding: 4px 0px 69 | &:before, &:after 70 | border: none 71 | .leaflet-popup 72 | z-index: 9 73 | 74 | .control-fit-markers 75 | $size: 26px 76 | width: $size 77 | height: $size 78 | border: 1px solid #dddddd 79 | background-color: #fefefe 80 | cursor: auto 81 | border-radius: 3px 82 | margin-left: 10px 83 | vertical-align: middle 84 | text-align: center 85 | padding: 2px 86 | a 87 | color: #222222 88 | &:hover, &:focus, &:active 89 | color: #222222 90 | 91 | 92 | path.leaflet-clickable 93 | transition: d 0.2s 94 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NYC Subway Traffic Visualization 5 | 6 | 7 | 8 | 9 |
10 |
11 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 |
    51 |
  • 52 |
53 |
54 |
55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /resources/styles/layout.sass: -------------------------------------------------------------------------------- 1 | $bg-color: #2E2E2E 2 | $text-color: #eeeeee 3 | $space : 20px 4 | 5 | $sm-width: 768px 6 | 7 | .col-sidebar, .row-container 8 | background-color: $bg-color 9 | color: $text-color 10 | max-height: 100vh 11 | overflow-y: scroll 12 | 13 | .col-main 14 | padding-left: 0px 15 | padding-right: 0px 16 | position: relative 17 | 18 | .sidebar 19 | padding: $space 20 | width: 100% 21 | height: 100% 22 | .title 23 | margin-top: 0px 24 | #toggle-lines 25 | margin-right: $space/2 26 | #subway-lines 27 | padding-left: 0px 28 | margin-top: $space 29 | list-style: none 30 | fieldset 31 | display: block 32 | margin-right: 4px 33 | margin-bottom: 4px 34 | @media(max-width: $sm-width) 35 | display: inline-block 36 | margin-right: $space 37 | input, label 38 | opacity: 0.4 39 | label 40 | padding-left: $space/3 41 | input:checked + label, input:checked 42 | opacity: 1 43 | 44 | .hud 45 | position: absolute 46 | top: 0px 47 | right: 0px 48 | padding: $space/2 49 | // padding-right: $space*2 50 | z-index: 9 51 | background: radial-gradient(at top right,rgba(black,1) 0%,rgba(black,0) 80%); 52 | height: 100px 53 | .datetime-display 54 | max-width: 200px 55 | text-shadow: 1px 1px 1px black 56 | text-align: right 57 | margin-top: $space/2 58 | margin-right: $space 59 | font-size: 1em 60 | font-weight: bold 61 | font-family: 'Monaco', 'monospace' 62 | 63 | .timeline-container 64 | z-index: 9999 65 | position: absolute 66 | bottom: 0px 67 | width: 100% 68 | margin: $space*2 0px 69 | ul.timeline 70 | position: relative 71 | // background-color: rgba(#333333, 0.8) 72 | padding: 0px $space/2 73 | margin: 0px 74 | list-style: none 75 | display: flex 76 | font-size: 0.8em 77 | $text-color: #aaaaaa 78 | li 79 | position: relative 80 | z-index: 1 81 | color: $text-color 82 | flex: 1 83 | text-align: center 84 | &:not(:last-child) 85 | border-right: 1px solid $text-color 86 | &.weekend 87 | color: lighten($text-color, 30%) 88 | .timeline-cursor 89 | transition: width 2s 90 | position: absolute 91 | z-index: 0 92 | width: 0px 93 | height: 1.5em 94 | background-color: rgba(#eeeeee, 0.5) 95 | &.active 96 | transition-delay: 4s 97 | width: 100% 98 | 99 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | import test from './test' 2 | import HeatMap from './components/HeatMap' 3 | import MapControls from './components/MapControls' 4 | import SubwayLines from './components/SubwayLines' 5 | import ui from './ui/UiControls' 6 | import cb from './ui/UiCallbacks' 7 | import 'styles/style.scss' 8 | 9 | let map, mapControls 10 | 11 | function init(){ 12 | const startDate = '2015-09-28 00:00:00 GMT-04:00' 13 | // const endDate = '2015-09-29 23:59:59 GMT-04:00' 14 | const endDate = '2015-10-04 23:59:59 GMT-04:00' 15 | 16 | // must set size of map before initialization because .fitBounds() 17 | // does not like % or vh sizes 18 | ui.setMapSize() 19 | window.onresize = ui.setMapSize 20 | 21 | // init map and controls 22 | map = new HeatMap() 23 | mapControls = new MapControls(map) 24 | 25 | const sl = new SubwayLines() 26 | const subwayLines = sl.getAllIcons() 27 | const subwayListElem = document.querySelector('#subway-lines') 28 | 29 | // init subway line controls 30 | for(let lines in subwayLines){ 31 | // for each station 32 | const icons = subwayLines[lines] 33 | const fs = cb.setSubwayLineControl(mapControls, icons,lines) 34 | subwayListElem.appendChild(fs) 35 | } 36 | 37 | // for 'select all/none controls' 38 | cb.setSubwayLineControlBatch(true, mapControls) 39 | 40 | // for 'hide/show lines when in mobile mode' 41 | cb.setResponsiveCallbacks() 42 | 43 | // create timeline at bottom of map 44 | ui.drawTimeline(startDate, endDate) 45 | ui.setDates(startDate, endDate) 46 | 47 | initStartAnimationBtnCallbacks(startDate, endDate) 48 | } 49 | 50 | function initStartAnimationBtnCallbacks(startDate, endDate){ 51 | // this button makes the magic happen 52 | const startAnimationElem = document.querySelector('#start-animation') 53 | 54 | startAnimationElem.onclick = (e) => { 55 | if(e) e.preventDefault() 56 | 57 | // disable button until animation done 58 | startAnimationElem.disabled = true 59 | 60 | // start heating the map up, y'all 61 | let intervalPromises = map.heat({start: startDate, 62 | end: endDate}) 63 | 64 | intervalPromises = intervalPromises.map(p => { 65 | p.then(data => { 66 | ui.updateHUDTime(data.datetime) 67 | ui.updateTimeline(data.datetime, data.interval, data.whole) 68 | if( Date.parse(data.datetime) >= Date.parse(endDate) ){ 69 | startAnimationElem.disabled = false 70 | } 71 | }) 72 | }) 73 | 74 | Promise.all(intervalPromises).catch( e => { 75 | console.error('Something went wrong!', e) 76 | }) 77 | } 78 | 79 | // For development purposes 80 | startAnimationElem.onclick() 81 | } 82 | 83 | init() 84 | -------------------------------------------------------------------------------- /public/bundle.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) 10 | /******/ return installedModules[moduleId].exports; 11 | 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ exports: {}, 15 | /******/ id: moduleId, 16 | /******/ loaded: false 17 | /******/ }; 18 | 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | 22 | /******/ // Flag the module as loaded 23 | /******/ module.loaded = true; 24 | 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | 29 | 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | 36 | /******/ // __webpack_public_path__ 37 | /******/ __webpack_require__.p = "/"; 38 | 39 | /******/ // Load entry module and return exports 40 | /******/ return __webpack_require__(0); 41 | /******/ }) 42 | /************************************************************************/ 43 | /******/ ([ 44 | /* 0 */ 45 | /***/ function(module, exports, __webpack_require__) { 46 | 47 | module.exports = __webpack_require__(1); 48 | 49 | 50 | /***/ }, 51 | /* 1 */ 52 | /***/ function(module, exports, __webpack_require__) { 53 | 54 | 'use strict'; 55 | 56 | var _interopRequireDefault = __webpack_require__(2)['default']; 57 | 58 | var _test = __webpack_require__(3); 59 | 60 | var _test2 = _interopRequireDefault(_test); 61 | 62 | __webpack_require__(4); 63 | 64 | (0, _test2['default'])(); 65 | 66 | /***/ }, 67 | /* 2 */ 68 | /***/ function(module, exports) { 69 | 70 | "use strict"; 71 | 72 | exports["default"] = function (obj) { 73 | return obj && obj.__esModule ? obj : { 74 | "default": obj 75 | }; 76 | }; 77 | 78 | exports.__esModule = true; 79 | 80 | /***/ }, 81 | /* 3 */ 82 | /***/ function(module, exports) { 83 | 84 | "use strict"; 85 | 86 | Object.defineProperty(exports, "__esModule", { 87 | value: true 88 | }); 89 | exports["default"] = test; 90 | 91 | function test() { 92 | var a = [1, 2, 3, 4, 5, 6]; 93 | a.forEach(function (m) { 94 | return console.log(m); 95 | }); 96 | } 97 | 98 | module.exports = exports["default"]; 99 | 100 | /***/ }, 101 | /* 4 */ 102 | /***/ function(module, exports) { 103 | 104 | // removed by extract-text-webpack-plugin 105 | 106 | /***/ } 107 | /******/ ]); -------------------------------------------------------------------------------- /app/ui/UiControls.js: -------------------------------------------------------------------------------- 1 | export default { 2 | setDates(startDate, endDate){ 3 | // set dates in description 4 | const datesContainer = document.querySelector('.dates') 5 | datesContainer.innerHTML = `Week of ${startDate.split(' ')[0]} to ${endDate.split(' ')[0]}.` 6 | }, 7 | 8 | setMapSize(){ 9 | // set map size to fill map-container 10 | const mapElem = document.querySelector('#map') 11 | const mapContainerElem = document.querySelector('#map-container') 12 | const rect = mapContainerElem.getBoundingClientRect() 13 | 14 | mapElem.style.width = `${rect.width}px` 15 | mapElem.style.height = `${rect.height}px` 16 | }, 17 | 18 | updateHUDTime(datetime){ 19 | // update time at corner of map. for use to update at every time interval 20 | const datetimeDisplayElem = document.querySelector('.datetime-display') 21 | datetimeDisplayElem.innerHTML = datetime.toString() 22 | }, 23 | 24 | updateTimeline(datetime, interval, whole){ 25 | let id = this.getIdFromDate(datetime) 26 | const elem = document.getElementById(id) 27 | const elemWidth = elem.clientWidth 28 | 29 | const cursor = document.querySelector('.timeline-cursor') 30 | cursor.style.transition = `width ${interval/1000}s` 31 | 32 | let w = (cursor.clientWidth + elemWidth*(interval/whole)) 33 | 34 | cursor.style.width = w + 'px' 35 | 36 | }, 37 | 38 | drawTimeline(startDate, endDate){ 39 | // draw timeline at bottom of map 40 | const days = "Sun Mon Tue Wed Thu Fri Sat".split(' ') 41 | const months = "Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec".split(' ') 42 | const timelineElem = document.querySelector('.timeline') 43 | const start = new Date(Date.parse(startDate)) 44 | const end = new Date(Date.parse(endDate)) 45 | let curr = start 46 | while (curr <= end){ 47 | let d = document.createElement('li') 48 | let f = `${days[curr.getDay()]}, ${months[curr.getMonth()]} ${curr.getDate()}` 49 | d.innerHTML = f 50 | d.id = this.getIdFromDate(curr.toString()) 51 | 52 | // if weekend 53 | if(curr.getDay() == 0 || curr.getDay() == 6){ 54 | d.className = 'weekend' 55 | } 56 | timelineElem.appendChild(d) 57 | curr.setDate(curr.getDate() + 1) 58 | } 59 | }, 60 | 61 | getIdFromDate(dateString){ 62 | // given date format 'Thu Oct 01 2015 20:00:00 GMT-0400 (EDT)' 63 | // return id in format 'Thu-Oct-01-2015' 64 | const id = `${dateString}`.split(' ').slice(0, 4).join('-') 65 | return id 66 | }, 67 | 68 | getSelectedLines(){ 69 | // get lines checked in sidebar as array e.g. ['A', 'G' ... 'S'] 70 | let checked = document.querySelectorAll('.control-subway-line:checked') 71 | const selectedStations = Array.prototype.map.call(checked, line => line.value) 72 | const indivStations = selectedStations.reduce((prev, elem) => { 73 | prev.push(...elem.split("")) 74 | return prev 75 | }, []) 76 | return indivStations 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /resources/styles/bootstrap.scss: -------------------------------------------------------------------------------- 1 | // Core variables and mixins 2 | @import "variables"; 3 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/mixins"; 4 | 5 | // Reset and dependencies 6 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/normalize"; 7 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/print"; 8 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/glyphicons"; 9 | 10 | // Core CSS 11 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/scaffolding"; 12 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/type"; 13 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/code"; 14 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/grid"; 15 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/tables"; 16 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/forms"; 17 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/buttons"; 18 | 19 | // Components 20 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/component-animations"; 21 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/dropdowns"; 22 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/button-groups"; 23 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/input-groups"; 24 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/navs"; 25 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/navbar"; 26 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/breadcrumbs"; 27 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/pagination"; 28 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/pager"; 29 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/labels"; 30 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/badges"; 31 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/jumbotron"; 32 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/thumbnails"; 33 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/alerts"; 34 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/progress-bars"; 35 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/media"; 36 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/list-group"; 37 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/panels"; 38 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/responsive-embed"; 39 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/wells"; 40 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/close"; 41 | 42 | // Components w/ JavaScript 43 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/modals"; 44 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/tooltip"; 45 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/popovers"; 46 | // @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/carousel"; 47 | 48 | // Utility classes 49 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/utilities"; 50 | @import "../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities"; 51 | -------------------------------------------------------------------------------- /app/ui/UiCallbacks.js: -------------------------------------------------------------------------------- 1 | import dom from 'utils/DomUtils' 2 | import ui from 'ui/UiControls' 3 | 4 | export default { 5 | setResponsiveCallbacks(){ 6 | // responsive toggle stuff 7 | const toggleLines = document.querySelector('#toggle-lines') 8 | const collapseElem = document.querySelector('.collapse-sm') 9 | dom.initToggle(toggleLines, { 10 | state: () => { 11 | return collapseElem.className.split(' ').indexOf('hidden-xs') > -1 12 | }, 13 | on: { 14 | text: 'Hide Lines', 15 | callback: (e) => { 16 | e.preventDefault() 17 | collapseElem.className = collapseElem.className.replace('hidden-xs', '') 18 | } 19 | }, 20 | off: { 21 | text: 'Select Lines', 22 | callback: (e) => { 23 | e.preventDefault() 24 | collapseElem.className += ' hidden-xs' 25 | } 26 | } 27 | }) 28 | }, 29 | 30 | // show/hide subway lines 31 | setSubwayLineControlBatch(selectAll, mapControls){ 32 | const controls = document.querySelectorAll('.control-subway-line-batch') 33 | const batchClass = '.control-subway-line' 34 | const checkAllElem = document.querySelector(`input[value="check-all"]`) 35 | const checkNoneElem = document.querySelector(`input[value="check-none"]`) 36 | 37 | 38 | Array.prototype.forEach.call(controls, c => { 39 | c.onchange = e => { 40 | const val = e.target.value 41 | 42 | let checkboxes = [] 43 | let check = true 44 | 45 | if(val === 'check-all'){ 46 | checkboxes.push(...document.querySelectorAll(`${batchClass}:not(:checked)`)) 47 | checkNoneElem.checked = false 48 | check = true 49 | } 50 | else if(val === 'check-none'){ 51 | checkboxes.push(...document.querySelectorAll(`${batchClass}:checked`)) 52 | checkboxes.push(checkAllElem) 53 | check = false 54 | } 55 | 56 | if(checkboxes.length > 0){ 57 | checkboxes.forEach(cb => cb.checked = check) 58 | 59 | // trigger map update 60 | const event = new Event('change') 61 | checkboxes[0].dispatchEvent(event) 62 | } 63 | } 64 | }) 65 | 66 | // trigger initial check 67 | const triggerElem = selectAll? checkAllElem : checkNoneElem 68 | const event = new Event('change') 69 | triggerElem.checked = true 70 | triggerElem.dispatchEvent(event) 71 | 72 | // hide station controls 73 | const hideLinesInput = document.querySelector('#show-lines-control') 74 | dom.initToggle(hideLinesInput, { 75 | state: () => hideLinesInput.checked, 76 | on: { 77 | text: 'Show Stations', 78 | callback: () => { 79 | const lines = ui.getSelectedLines() 80 | mapControls.toggleStations(true, lines) 81 | } 82 | }, 83 | off: { 84 | text: 'Show Stations', 85 | callback: () => { 86 | const lines = ui.getSelectedLines() 87 | mapControls.toggleStations(false, lines) 88 | } 89 | } 90 | }) 91 | }, 92 | 93 | // create line checkboxes 94 | setSubwayLineControl(mapControls, icon, line){ 95 | const fs = document.createElement('fieldset') 96 | const cb = document.createElement('input') 97 | cb.setAttribute('type', 'checkbox') 98 | cb.setAttribute('id', `subway-line-${line}`) 99 | cb.setAttribute('class', 'control-subway-line') 100 | cb.value = `${line}` 101 | cb.onchange = () => {mapControls.showLines(ui.getSelectedLines())} 102 | 103 | const lbl = document.createElement('label') 104 | lbl.innerHTML = `${icon}` 105 | lbl.setAttribute('for', `subway-line-${line}`) 106 | fs.appendChild(cb) 107 | fs.appendChild(lbl) 108 | return fs 109 | } 110 | } -------------------------------------------------------------------------------- /app/components/SubwayMap.js: -------------------------------------------------------------------------------- 1 | import Mapbox from './Mapbox' 2 | import LMap from 'mapbox.js/src/map.js' 3 | import StationData from '!json!files/stations.json' 4 | import SubwayLines from './SubwayLines' 5 | import 'font-awesome/scss/font-awesome.scss' 6 | 7 | const L = Mapbox() 8 | 9 | export default class SubwayMap extends LMap.Map{ 10 | 11 | constructor() { 12 | super('map', 'piratefsh.nknilk08') 13 | // get leaflet L object 14 | this.L = Mapbox() 15 | this.subwayLines = new SubwayLines() 16 | this.stations = StationData.stations 17 | 18 | // layers and markers 19 | this.layers = { 20 | 'lines': {}, 21 | 'entries': {}, 22 | 'exits': {} 23 | } 24 | this.markers = [] 25 | this.stationNames = [] 26 | 27 | this.setCallbacks() 28 | this.markerBounds = this.addAllStationMarkers() 29 | 30 | this.addCustomControls() 31 | this.setCustomView() 32 | 33 | // when to show labels by default 34 | this.zoomThresholdForLabel = 13 35 | 36 | } 37 | 38 | setCustomView(){ 39 | // center view in manhattan 40 | const manhattanLatLng = [40.759123, -73.953266] 41 | const zoomLevel = 12 42 | this.fitBounds(this.markerBounds) 43 | this.setView(manhattanLatLng, zoomLevel) 44 | } 45 | 46 | addCustomControls(){ 47 | let SubwayControls = this.L.Control.extend({ 48 | options: { 49 | position: 'topleft' 50 | }, 51 | onAdd: (map) => { 52 | let container = this.L.DomUtil.create('div', 'control-fit-markers') 53 | container.innerHTML = '' 54 | L.DomEvent.addListener(container, 'click', L.DomEvent.stopPropagation) 55 | .addListener(container, 'dblclick', L.DomEvent.stopPropagation) 56 | .addListener(container, 'click', L.DomEvent.preventDefault) 57 | .addListener(container, 'dblclick', L.DomEvent.preventDefault) 58 | .addListener(container, 'click', () => { 59 | this.setCustomView()}) 60 | .addListener(container, 'dblclick', () => { 61 | this.setCustomView()}) 62 | return container 63 | } 64 | }) 65 | this.addControl(new SubwayControls()) 66 | } 67 | 68 | setCallbacks(){ 69 | this.on('zoomend', () => { 70 | if(this.getZoom() > this.zoomThresholdForLabel){ 71 | // make all labels noHide = true 72 | // i.e. always show labe; 73 | this.showMarkerLabel(true) 74 | } 75 | else{ 76 | this.showMarkerLabel(false) 77 | 78 | } 79 | }) 80 | } 81 | 82 | showMarkerLabel(val){ 83 | this.eachLineLayer((linename, l) => { 84 | l.eachLayer(m => m.setLabelNoHide(val)) 85 | }) 86 | } 87 | 88 | // for each layer in this 89 | eachLineLayer(f){ 90 | const lines = this.layers['lines'] 91 | for (let linename in lines){ 92 | const layer = lines[linename] 93 | f(linename, layer) 94 | } 95 | } 96 | 97 | createPopUp(latlng, content){ 98 | const popup = L.popup() 99 | .setLatLng(latlng) 100 | .setContent(content) 101 | return popup 102 | } 103 | 104 | lineToIcons(linename){ 105 | const lines = linename.split("") 106 | const icons = lines.map(l => this.subwayLines.getIcon(l)) 107 | return icons.join('') 108 | } 109 | 110 | createCircleMarker(title, latlng, color, popup) { 111 | if (latlng){ 112 | const marker = this.L.circleMarker(latlng, 113 | { 114 | title: title, 115 | riseOnHover: true, 116 | radius: 5, 117 | stroke: false, 118 | color: color, 119 | fillOpacity: 0.6, 120 | }) 121 | 122 | marker.bindLabel(title, { 123 | noHide: false, 124 | clickable: true, 125 | direction: 'auto', 126 | className: 'station-name' 127 | }) 128 | 129 | marker.bindPopup(popup) 130 | return marker 131 | 132 | } 133 | } 134 | 135 | createStationMarker(unit, s){ 136 | if(s.coords && s.coords.lat && s.coords.lng){ 137 | const latlng = [s.coords.lat, s.coords.lng] 138 | const lines = this.lineToIcons(s.line_name) 139 | 140 | const popupContent = `
${s.station_name} ${unit}${lines}
` 141 | const popup = this.createPopUp(latlng, popupContent) 142 | 143 | const station_name = `${s.station_name}` 144 | const marker = this.createCircleMarker(station_name, latlng, 145 | s.color, popup) 146 | marker.lineName = s.line_name 147 | marker.unit = unit 148 | return marker 149 | } 150 | } 151 | 152 | //add stations and returns their bounds 153 | addAllStationMarkers() { 154 | const stations = this.stations 155 | 156 | // const markers = stations.map(s => this.createStationMarker(s)) 157 | const markers = [] 158 | const lls = [] 159 | for (let unit in stations){ 160 | const data = stations[unit] 161 | const m = this.createStationMarker(unit, data) 162 | if(m){ 163 | markers.push(m) 164 | lls.push(m.getLatLng()) 165 | } 166 | } 167 | 168 | // for each marker, add to layer for their line 169 | markers.forEach(m => { 170 | if (m == null) return 171 | 172 | const lines = m.lineName.split("") 173 | lines.forEach(l => { 174 | if (l in this.layers['lines']){ 175 | this.layers['lines'][l].addLayer(m) 176 | } 177 | else{ 178 | this.layers['lines'][l] = this.L.featureGroup([m]) 179 | } 180 | }) 181 | }) 182 | 183 | this.eachLineLayer((ln, l) => l.addTo(this)) 184 | 185 | return this.L.latLngBounds(lls) 186 | } 187 | 188 | // debug tools 189 | _printLines(){ 190 | this.eachLineLayer((ln, l) => { 191 | const station = [] 192 | l.eachLayer(m => station.push(m.options.title)) 193 | console.log(ln, station) 194 | }) 195 | } 196 | 197 | } 198 | 199 | -------------------------------------------------------------------------------- /app/components/HeatMap.js: -------------------------------------------------------------------------------- 1 | import SubwayMap from './SubwayMap' 2 | import TurnstileData from 'files/turnstiles.json' 3 | import heatCircleSvg from 'file!images/heat-circle.svg' 4 | import heatCirclePng from 'images/heat-circle.png' 5 | 6 | export default class HeatMap extends SubwayMap{ 7 | constructor(...args){ 8 | super(...args) 9 | this.data = TurnstileData 10 | this.heatLayerRefs = {} 11 | 12 | const max = this.data.max.entries > this.data.max.exits ? this.data.max.entries : this.data.max.exits 13 | this.radiusRatio = 15/max 14 | this.minRadius = 2 15 | 16 | this.timeIntervals = [ 17 | '00:00:00', 18 | '04:00:00', 19 | '08:00:00', 20 | '12:00:00', 21 | '16:00:00', 22 | '20:00:00', 23 | '23:59:59'] 24 | 25 | this.timeFrame = 2000 26 | this.dayFrame = this.timeFrame * (this.timeIntervals.length-1) 27 | } 28 | 29 | heat(options, onDoneInterval){ 30 | const start = new Date(Date.parse(options.start)) 31 | const end = new Date(Date.parse(options.end)) 32 | const day = new Date(start) 33 | let timeout = 0 34 | let timeFrame = this.dayFrame 35 | let promises = [] 36 | while(day <= end){ 37 | const yr = `${day.getFullYear()}` 38 | let mo = `${day.getMonth() + 1}` 39 | let da = `${day.getDate()}` 40 | 41 | mo = mo.length == 1? `0${mo}` : mo 42 | da = da.length == 1? `0${da}` : da 43 | 44 | const formattedDate = `${yr}-${mo}-${da}` 45 | 46 | const pEntries = this.createHeatLayer(formattedDate, 'exits', timeout) 47 | const pExits = this.createHeatLayer(formattedDate, 'entries', timeout) 48 | 49 | Array.prototype.push.apply(promises, pEntries) 50 | Array.prototype.push.apply(promises, pExits) 51 | 52 | timeout += timeFrame 53 | day.setDate(day.getDate() + 1) 54 | } 55 | return promises 56 | } 57 | 58 | getRadius(volume){ 59 | return this.radiusRatio * volume + this.minRadius 60 | } 61 | 62 | generateHeatSizes(date, which){ 63 | const stations = this.data.stations 64 | 65 | // predefined time slots 66 | const heats = {} 67 | for(let time of this.timeIntervals){ 68 | heats[time] = [] 69 | } 70 | 71 | const intervals = Object.keys(heats).sort() 72 | 73 | for (let unit in stations){ 74 | // check unit exists 75 | if(!(unit in this.stations)){ 76 | // debug message 77 | // console.log('warning, missing station ', unit, stations[unit].station_name) 78 | continue 79 | } 80 | 81 | const s = stations[unit] 82 | const ll = this.stations[unit].coords 83 | const lines = this.stations[unit].line_name 84 | const stationName = this.stations[unit].station_name 85 | 86 | // check valid latlng 87 | if(!(ll && 'lat' in ll && 'lng' in ll)){ 88 | continue 89 | } 90 | 91 | if(date in s['dates']){ 92 | const times = s['dates'][date]['times'] 93 | times.forEach(t => { 94 | if(t.entries && t.exits){ 95 | const radius = this.getRadius(t[which]) 96 | const h = { 97 | latlng: ll, 98 | radius: radius, 99 | unit: unit, 100 | entries: t.entries, 101 | exits: t.exits, 102 | lineName: lines, 103 | stationName: stationName} 104 | 105 | // find time interval it belongs to 106 | const i = this.findTimeInterval(intervals, t.time) 107 | if (i > -1){ 108 | heats[intervals[i]].push(h) 109 | } 110 | } 111 | 112 | }) 113 | } 114 | } 115 | return heats 116 | } 117 | 118 | // find index of lower/upper bound time interval, whichever is closest where 119 | // list = ['00:00', '04:00', ... '20:00'] (sorted) 120 | // and time = '03:55' returns 1, '01:00' returns 0 121 | // uses bisection/binary search 122 | findTimeInterval(list, time){ 123 | if(list.length < 1 || time.length < 1){ 124 | return -1 125 | } 126 | let lo = 0; 127 | let hi = list.length 128 | let mid; 129 | while(lo < hi){ 130 | mid = Math.floor((lo + hi)/2) 131 | if (list[mid] < time){ 132 | lo = mid + 1 133 | } 134 | else{ 135 | hi = mid 136 | } 137 | } 138 | 139 | // find if lower bound is closer, if there is one. 140 | // have to add date so it's parsesable by Date lib 141 | if(lo > 0){ 142 | let actual = new Date('2015-09-09 ' + time) 143 | let upper = new Date('2015-09-09 ' + list[lo]) 144 | let lower = new Date('2015-09-09 ' + list[lo-1]) 145 | // diff between this and lower 146 | if(actual - lower < upper - actual){ 147 | return lo - 1 148 | } 149 | } 150 | 151 | return lo 152 | } 153 | 154 | // for each interval = for each day, for each time 155 | // make feature layers 156 | createHeatLayer(date, which, timeout){ 157 | const sizes = this.generateHeatSizes(date, which) 158 | 159 | let frameLen = this.timeFrame 160 | let counter = frameLen + timeout 161 | 162 | let promises = [] 163 | 164 | for(let time in sizes){ 165 | if(Object.keys(this.layers[which]).length < 1){ 166 | this.createHeatLayerInit(sizes[time], which) 167 | } 168 | else{ 169 | var p = new Promise((resolve, reject) => { 170 | setTimeout(()=>{ 171 | this.updateHeatLayer(sizes[time], counter, frameLen) 172 | const dt = new Date(Date.parse(`${date} ${time}`)) 173 | resolve({datetime: dt, interval: this.timeFrame, whole: this.dayFrame}) 174 | }, counter) 175 | }) 176 | 177 | promises.push(p) 178 | counter += frameLen 179 | } 180 | } 181 | return promises 182 | } 183 | 184 | createHeatLayerInit(sizes, which){ 185 | sizes.forEach(s => { this.initHeatLayer(s, which)} ) 186 | 187 | this.eachHeatLayer(which, (ln, l) => { 188 | l.addTo(this) 189 | }) 190 | } 191 | 192 | initHeatLayer(s, which){ 193 | const h = this.createHeatMarker(s.unit, s.latlng, s.radius, `${s.stationName}`, which) 194 | h.unit = s.unit 195 | this.heatLayerRefs[s.unit] = h 196 | 197 | const lines = s.lineName.split("") 198 | 199 | // add layer by lines 200 | lines.forEach(l => { 201 | const layers = this.layers[which] 202 | if (l in layers){ 203 | layers[l].addLayer(h) 204 | } 205 | else{ 206 | layers[l] = this.L.featureGroup([h]) 207 | } 208 | }); 209 | } 210 | 211 | // for each layer in this. which = entries|exits 212 | eachHeatLayer(which, f){ 213 | const lines = this.layers[which] 214 | for (let linename in lines){ 215 | const layer = lines[linename] 216 | f(linename, layer) 217 | } 218 | } 219 | 220 | updateHeatLayer(sizes, offset, timeout){ 221 | const delay = 10 222 | const frames = timeout/delay 223 | 224 | sizes.forEach(s => { 225 | if(s.unit in this.heatLayerRefs){ 226 | const h = this.heatLayerRefs[s.unit] 227 | const elemEnt = document.querySelector(`.heat-icon-entries-${s.unit}`) 228 | const elemExt = document.querySelector(`.heat-icon-exits-${s.unit}`) 229 | this.updateMarker(elemEnt, s, 'entries') 230 | this.updateMarker(elemExt, s, 'exits') 231 | 232 | } 233 | // if doesn't exist yet, create layer 234 | else{ 235 | this.initHeatLayer(s, 'entries') 236 | this.initHeatLayer(s, 'exits') 237 | } 238 | }) 239 | } 240 | 241 | updateMarker(elem, s, which){ 242 | if(elem){ 243 | const originalSizePx = elem.style.width 244 | const originalSize = parseInt(originalSizePx.slice(0, originalSizePx.length - 2)) 245 | 246 | const radius = this.getRadius(s[which]) 247 | const scale = radius/originalSize 248 | const rpx = Math.floor(radius) + 'px' 249 | const glowpx = Math.floor(radius)/2 + 'px' 250 | const marginpx = -1 * Math.floor(radius/2) + 'px' 251 | 252 | elem.style.width = rpx 253 | elem.style.height = rpx 254 | elem.style.marginLeft = marginpx 255 | elem.style.marginTop = marginpx 256 | 257 | let color = 'white' 258 | if(which == 'exits') color = 'red' 259 | // elem.style.boxShadow = `0px 0px ${glowpx} ${color}` 260 | } 261 | } 262 | 263 | createHeatMarker(id, ll, radius, title, which){ 264 | 265 | const whichClassName = which == 'entries' ? 'heat-icon-entries' : 'heat-icon-exits' 266 | radius = Math.floor(radius) 267 | 268 | const icon = new this.L.icon({ 269 | iconUrl: heatCirclePng, 270 | iconRetinaUrl: heatCirclePng, 271 | iconSize: [0, 0], 272 | iconAnchor: [10, 10], 273 | popupAnchor: [10, 0], 274 | shadowSize: [0, 0], 275 | className: `heat-icon ${whichClassName}`, 276 | }); 277 | 278 | icon.options.className += ` heat-icon-${which}-${id}` 279 | icon.options.iconSize = [radius, radius] 280 | icon.options.iconAnchor = [radius/2, radius/2] 281 | 282 | return this.L.marker(ll, { 283 | title: title, 284 | icon: icon 285 | }) 286 | } 287 | } -------------------------------------------------------------------------------- /resources/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | $bootstrap-sass-asset-helper: false !default; 2 | // 3 | // Variables 4 | // -------------------------------------------------- 5 | 6 | 7 | //== Colors 8 | // 9 | //## Gray and brand colors for use across Bootstrap. 10 | 11 | $gray-base: #000 !default; 12 | $gray-darker: lighten($gray-base, 13.5%) !default; // #222 13 | $gray-dark: lighten($gray-base, 20%) !default; // #333 14 | $gray: lighten($gray-base, 33.5%) !default; // #555 15 | $gray-light: lighten($gray-base, 46.7%) !default; // #777 16 | $gray-lighter: lighten($gray-base, 93.5%) !default; // #eee 17 | 18 | $brand-primary: darken(#428bca, 6.5%) !default; // #337ab7 19 | $brand-success: #5cb85c !default; 20 | $brand-info: #5bc0de !default; 21 | $brand-warning: #f0ad4e !default; 22 | $brand-danger: #d9534f !default; 23 | 24 | 25 | //== Scaffolding 26 | // 27 | //## Settings for some of the most global styles. 28 | 29 | //** Background color for ``. 30 | $body-bg: #fff !default; 31 | //** Global text color on ``. 32 | $text-color: $gray-dark !default; 33 | 34 | //** Global textual link color. 35 | $link-color: $gray-light !default; 36 | //** Link hover color set via `darken()` function. 37 | $link-hover-color: darken($link-color, 15%) !default; 38 | //** Link hover decoration. 39 | $link-hover-decoration: underline !default; 40 | 41 | 42 | //== Typography 43 | // 44 | //## Font, line-height, and color for body text, headings, and more. 45 | 46 | $font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; 47 | $font-family-serif: Georgia, "Times New Roman", Times, serif !default; 48 | //** Default monospace fonts for ``, ``, and `
`.
 49 | $font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace !default;
 50 | $font-family-base:        $font-family-sans-serif !default;
 51 | 
 52 | $font-size-base:          14px !default;
 53 | $font-size-large:         ceil(($font-size-base * 1.25)) !default; // ~18px
 54 | $font-size-small:         ceil(($font-size-base * 0.85)) !default; // ~12px
 55 | 
 56 | $font-size-h1:            floor(($font-size-base * 2.6)) !default; // ~36px
 57 | $font-size-h2:            floor(($font-size-base * 2.15)) !default; // ~30px
 58 | $font-size-h3:            ceil(($font-size-base * 1.7)) !default; // ~24px
 59 | $font-size-h4:            ceil(($font-size-base * 1.25)) !default; // ~18px
 60 | $font-size-h5:            $font-size-base !default;
 61 | $font-size-h6:            ceil(($font-size-base * 0.85)) !default; // ~12px
 62 | 
 63 | //** Unit-less `line-height` for use in components like buttons.
 64 | $line-height-base:        1.428571429 !default; // 20/14
 65 | //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
 66 | $line-height-computed:    floor(($font-size-base * $line-height-base)) !default; // ~20px
 67 | 
 68 | //** By default, this inherits from the ``.
 69 | $headings-font-family:    inherit !default;
 70 | $headings-font-weight:    500 !default;
 71 | $headings-line-height:    1.1 !default;
 72 | $headings-color:          inherit !default;
 73 | 
 74 | 
 75 | //== Iconography
 76 | //
 77 | //## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
 78 | 
 79 | //** Load fonts from this directory.
 80 | 
 81 | // [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path.
 82 | // [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths.
 83 | $icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") !default;
 84 | 
 85 | //** File name for all font files.
 86 | $icon-font-name:          "glyphicons-halflings-regular" !default;
 87 | //** Element ID within SVG icon file.
 88 | $icon-font-svg-id:        "glyphicons_halflingsregular" !default;
 89 | 
 90 | 
 91 | //== Components
 92 | //
 93 | //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
 94 | 
 95 | $padding-base-vertical:     6px !default;
 96 | $padding-base-horizontal:   12px !default;
 97 | 
 98 | $padding-large-vertical:    10px !default;
 99 | $padding-large-horizontal:  16px !default;
100 | 
101 | $padding-small-vertical:    5px !default;
102 | $padding-small-horizontal:  10px !default;
103 | 
104 | $padding-xs-vertical:       1px !default;
105 | $padding-xs-horizontal:     5px !default;
106 | 
107 | $line-height-large:         1.3333333 !default; // extra decimals for Win 8.1 Chrome
108 | $line-height-small:         1.5 !default;
109 | 
110 | $border-radius-base:        4px !default;
111 | $border-radius-large:       6px !default;
112 | $border-radius-small:       3px !default;
113 | 
114 | //** Global color for active items (e.g., navs or dropdowns).
115 | $component-active-color:    #fff !default;
116 | //** Global background color for active items (e.g., navs or dropdowns).
117 | $component-active-bg:       $brand-primary !default;
118 | 
119 | //** Width of the `border` for generating carets that indicator dropdowns.
120 | $caret-width-base:          4px !default;
121 | //** Carets increase slightly in size for larger components.
122 | $caret-width-large:         5px !default;
123 | 
124 | 
125 | //== Tables
126 | //
127 | //## Customizes the `.table` component with basic values, each used across all table variations.
128 | 
129 | //** Padding for ``s and ``s.
130 | $table-cell-padding:            8px !default;
131 | //** Padding for cells in `.table-condensed`.
132 | $table-condensed-cell-padding:  5px !default;
133 | 
134 | //** Default background color used for all tables.
135 | $table-bg:                      transparent !default;
136 | //** Background color used for `.table-striped`.
137 | $table-bg-accent:               #f9f9f9 !default;
138 | //** Background color used for `.table-hover`.
139 | $table-bg-hover:                #f5f5f5 !default;
140 | $table-bg-active:               $table-bg-hover !default;
141 | 
142 | //** Border color for table and cell borders.
143 | $table-border-color:            #ddd !default;
144 | 
145 | 
146 | //== Buttons
147 | //
148 | //## For each of Bootstrap's buttons, define text, background and border color.
149 | 
150 | $btn-font-weight:                normal !default;
151 | 
152 | $btn-default-color:              #333 !default;
153 | $btn-default-bg:                 #fff !default;
154 | $btn-default-border:             #ccc !default;
155 | 
156 | $btn-primary-color:              #fff !default;
157 | $btn-primary-bg:                 $brand-primary !default;
158 | $btn-primary-border:             darken($btn-primary-bg, 5%) !default;
159 | 
160 | $btn-success-color:              #fff !default;
161 | $btn-success-bg:                 $brand-success !default;
162 | $btn-success-border:             darken($btn-success-bg, 5%) !default;
163 | 
164 | $btn-info-color:                 #fff !default;
165 | $btn-info-bg:                    $brand-info !default;
166 | $btn-info-border:                darken($btn-info-bg, 5%) !default;
167 | 
168 | $btn-warning-color:              #fff !default;
169 | $btn-warning-bg:                 $brand-warning !default;
170 | $btn-warning-border:             darken($btn-warning-bg, 5%) !default;
171 | 
172 | $btn-danger-color:               #fff !default;
173 | $btn-danger-bg:                  $brand-danger !default;
174 | $btn-danger-border:              darken($btn-danger-bg, 5%) !default;
175 | 
176 | $btn-link-disabled-color:        $gray-light !default;
177 | 
178 | // Allows for customizing button radius independently from global border radius
179 | $btn-border-radius-base:         $border-radius-base !default;
180 | $btn-border-radius-large:        $border-radius-large !default;
181 | $btn-border-radius-small:        $border-radius-small !default;
182 | 
183 | 
184 | //== Forms
185 | //
186 | //##
187 | 
188 | //** `` background color
189 | $input-bg:                       #fff !default;
190 | //** `` background color
191 | $input-bg-disabled:              $gray-lighter !default;
192 | 
193 | //** Text color for ``s
194 | $input-color:                    $gray !default;
195 | //** `` border color
196 | $input-border:                   #ccc !default;
197 | 
198 | // TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
199 | //** Default `.form-control` border radius
200 | // This has no effect on ``s in CSS.
201 | $input-border-radius:            $border-radius-base !default;
202 | //** Large `.form-control` border radius
203 | $input-border-radius-large:      $border-radius-large !default;
204 | //** Small `.form-control` border radius
205 | $input-border-radius-small:      $border-radius-small !default;
206 | 
207 | //** Border color for inputs on focus
208 | $input-border-focus:             #66afe9 !default;
209 | 
210 | //** Placeholder text color
211 | $input-color-placeholder:        #999 !default;
212 | 
213 | //** Default `.form-control` height
214 | $input-height-base:              ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
215 | //** Large `.form-control` height
216 | $input-height-large:             (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
217 | //** Small `.form-control` height
218 | $input-height-small:             (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
219 | 
220 | //** `.form-group` margin
221 | $form-group-margin-bottom:       15px !default;
222 | 
223 | $legend-color:                   $gray-dark !default;
224 | $legend-border-color:            #e5e5e5 !default;
225 | 
226 | //** Background color for textual input addons
227 | $input-group-addon-bg:           $gray-lighter !default;
228 | //** Border color for textual input addons
229 | $input-group-addon-border-color: $input-border !default;
230 | 
231 | //** Disabled cursor for form controls and buttons.
232 | $cursor-disabled:                not-allowed !default;
233 | 
234 | 
235 | //== Dropdowns
236 | //
237 | //## Dropdown menu container and contents.
238 | 
239 | //** Background for the dropdown menu.
240 | $dropdown-bg:                    #fff !default;
241 | //** Dropdown menu `border-color`.
242 | $dropdown-border:                rgba(0,0,0,.15) !default;
243 | //** Dropdown menu `border-color` **for IE8**.
244 | $dropdown-fallback-border:       #ccc !default;
245 | //** Divider color for between dropdown items.
246 | $dropdown-divider-bg:            #e5e5e5 !default;
247 | 
248 | //** Dropdown link text color.
249 | $dropdown-link-color:            $gray-dark !default;
250 | //** Hover color for dropdown links.
251 | $dropdown-link-hover-color:      darken($gray-dark, 5%) !default;
252 | //** Hover background for dropdown links.
253 | $dropdown-link-hover-bg:         #f5f5f5 !default;
254 | 
255 | //** Active dropdown menu item text color.
256 | $dropdown-link-active-color:     $component-active-color !default;
257 | //** Active dropdown menu item background color.
258 | $dropdown-link-active-bg:        $component-active-bg !default;
259 | 
260 | //** Disabled dropdown menu item background color.
261 | $dropdown-link-disabled-color:   $gray-light !default;
262 | 
263 | //** Text color for headers within dropdown menus.
264 | $dropdown-header-color:          $gray-light !default;
265 | 
266 | //** Deprecated `$dropdown-caret-color` as of v3.1.0
267 | $dropdown-caret-color:           #000 !default;
268 | 
269 | 
270 | //-- Z-index master list
271 | //
272 | // Warning: Avoid customizing these values. They're used for a bird's eye view
273 | // of components dependent on the z-axis and are designed to all work together.
274 | //
275 | // Note: These variables are not generated into the Customizer.
276 | 
277 | $zindex-navbar:            1000 !default;
278 | $zindex-dropdown:          1000 !default;
279 | $zindex-popover:           1060 !default;
280 | $zindex-tooltip:           1070 !default;
281 | $zindex-navbar-fixed:      1030 !default;
282 | $zindex-modal-background:  1040 !default;
283 | $zindex-modal:             1050 !default;
284 | 
285 | 
286 | //== Media queries breakpoints
287 | //
288 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
289 | 
290 | // Extra small screen / phone
291 | //** Deprecated `$screen-xs` as of v3.0.1
292 | $screen-xs:                  480px !default;
293 | //** Deprecated `$screen-xs-min` as of v3.2.0
294 | $screen-xs-min:              $screen-xs !default;
295 | //** Deprecated `$screen-phone` as of v3.0.1
296 | $screen-phone:               $screen-xs-min !default;
297 | 
298 | // Small screen / tablet
299 | //** Deprecated `$screen-sm` as of v3.0.1
300 | $screen-sm:                  768px !default;
301 | $screen-sm-min:              $screen-sm !default;
302 | //** Deprecated `$screen-tablet` as of v3.0.1
303 | $screen-tablet:              $screen-sm-min !default;
304 | 
305 | // Medium screen / desktop
306 | //** Deprecated `$screen-md` as of v3.0.1
307 | $screen-md:                  992px !default;
308 | $screen-md-min:              $screen-md !default;
309 | //** Deprecated `$screen-desktop` as of v3.0.1
310 | $screen-desktop:             $screen-md-min !default;
311 | 
312 | // Large screen / wide desktop
313 | //** Deprecated `$screen-lg` as of v3.0.1
314 | $screen-lg:                  1200px !default;
315 | $screen-lg-min:              $screen-lg !default;
316 | //** Deprecated `$screen-lg-desktop` as of v3.0.1
317 | $screen-lg-desktop:          $screen-lg-min !default;
318 | 
319 | // So media queries don't overlap when required, provide a maximum
320 | $screen-xs-max:              ($screen-sm-min - 1) !default;
321 | $screen-sm-max:              ($screen-md-min - 1) !default;
322 | $screen-md-max:              ($screen-lg-min - 1) !default;
323 | 
324 | 
325 | //== Grid system
326 | //
327 | //## Define your custom responsive grid.
328 | 
329 | //** Number of columns in the grid.
330 | $grid-columns:              12 !default;
331 | //** Padding between columns. Gets divided in half for the left and right.
332 | $grid-gutter-width:         30px !default;
333 | // Navbar collapse
334 | //** Point at which the navbar becomes uncollapsed.
335 | $grid-float-breakpoint:     $screen-sm-min !default;
336 | //** Point at which the navbar begins collapsing.
337 | $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
338 | 
339 | 
340 | //== Container sizes
341 | //
342 | //## Define the maximum width of `.container` for different screen sizes.
343 | 
344 | // Small screen / tablet
345 | $container-tablet:             (720px + $grid-gutter-width) !default;
346 | //** For `$screen-sm-min` and up.
347 | $container-sm:                 $container-tablet !default;
348 | 
349 | // Medium screen / desktop
350 | $container-desktop:            (940px + $grid-gutter-width) !default;
351 | //** For `$screen-md-min` and up.
352 | $container-md:                 $container-desktop !default;
353 | 
354 | // Large screen / wide desktop
355 | $container-large-desktop:      (1140px + $grid-gutter-width) !default;
356 | //** For `$screen-lg-min` and up.
357 | $container-lg:                 $container-large-desktop !default;
358 | 
359 | 
360 | //== Navbar
361 | //
362 | //##
363 | 
364 | // Basics of a navbar
365 | $navbar-height:                    50px !default;
366 | $navbar-margin-bottom:             $line-height-computed !default;
367 | $navbar-border-radius:             $border-radius-base !default;
368 | $navbar-padding-horizontal:        floor(($grid-gutter-width / 2)) !default;
369 | $navbar-padding-vertical:          (($navbar-height - $line-height-computed) / 2) !default;
370 | $navbar-collapse-max-height:       340px !default;
371 | 
372 | $navbar-default-color:             #777 !default;
373 | $navbar-default-bg:                #f8f8f8 !default;
374 | $navbar-default-border:            darken($navbar-default-bg, 6.5%) !default;
375 | 
376 | // Navbar links
377 | $navbar-default-link-color:                #777 !default;
378 | $navbar-default-link-hover-color:          #333 !default;
379 | $navbar-default-link-hover-bg:             transparent !default;
380 | $navbar-default-link-active-color:         #555 !default;
381 | $navbar-default-link-active-bg:            darken($navbar-default-bg, 6.5%) !default;
382 | $navbar-default-link-disabled-color:       #ccc !default;
383 | $navbar-default-link-disabled-bg:          transparent !default;
384 | 
385 | // Navbar brand label
386 | $navbar-default-brand-color:               $navbar-default-link-color !default;
387 | $navbar-default-brand-hover-color:         darken($navbar-default-brand-color, 10%) !default;
388 | $navbar-default-brand-hover-bg:            transparent !default;
389 | 
390 | // Navbar toggle
391 | $navbar-default-toggle-hover-bg:           #ddd !default;
392 | $navbar-default-toggle-icon-bar-bg:        #888 !default;
393 | $navbar-default-toggle-border-color:       #ddd !default;
394 | 
395 | 
396 | //=== Inverted navbar
397 | // Reset inverted navbar basics
398 | $navbar-inverse-color:                      lighten($gray-light, 15%) !default;
399 | $navbar-inverse-bg:                         #222 !default;
400 | $navbar-inverse-border:                     darken($navbar-inverse-bg, 10%) !default;
401 | 
402 | // Inverted navbar links
403 | $navbar-inverse-link-color:                 lighten($gray-light, 15%) !default;
404 | $navbar-inverse-link-hover-color:           #fff !default;
405 | $navbar-inverse-link-hover-bg:              transparent !default;
406 | $navbar-inverse-link-active-color:          $navbar-inverse-link-hover-color !default;
407 | $navbar-inverse-link-active-bg:             darken($navbar-inverse-bg, 10%) !default;
408 | $navbar-inverse-link-disabled-color:        #444 !default;
409 | $navbar-inverse-link-disabled-bg:           transparent !default;
410 | 
411 | // Inverted navbar brand label
412 | $navbar-inverse-brand-color:                $navbar-inverse-link-color !default;
413 | $navbar-inverse-brand-hover-color:          #fff !default;
414 | $navbar-inverse-brand-hover-bg:             transparent !default;
415 | 
416 | // Inverted navbar toggle
417 | $navbar-inverse-toggle-hover-bg:            #333 !default;
418 | $navbar-inverse-toggle-icon-bar-bg:         #fff !default;
419 | $navbar-inverse-toggle-border-color:        #333 !default;
420 | 
421 | 
422 | //== Navs
423 | //
424 | //##
425 | 
426 | //=== Shared nav styles
427 | $nav-link-padding:                          10px 15px !default;
428 | $nav-link-hover-bg:                         $gray-lighter !default;
429 | 
430 | $nav-disabled-link-color:                   $gray-light !default;
431 | $nav-disabled-link-hover-color:             $gray-light !default;
432 | 
433 | //== Tabs
434 | $nav-tabs-border-color:                     #ddd !default;
435 | 
436 | $nav-tabs-link-hover-border-color:          $gray-lighter !default;
437 | 
438 | $nav-tabs-active-link-hover-bg:             $body-bg !default;
439 | $nav-tabs-active-link-hover-color:          $gray !default;
440 | $nav-tabs-active-link-hover-border-color:   #ddd !default;
441 | 
442 | $nav-tabs-justified-link-border-color:            #ddd !default;
443 | $nav-tabs-justified-active-link-border-color:     $body-bg !default;
444 | 
445 | //== Pills
446 | $nav-pills-border-radius:                   $border-radius-base !default;
447 | $nav-pills-active-link-hover-bg:            $component-active-bg !default;
448 | $nav-pills-active-link-hover-color:         $component-active-color !default;
449 | 
450 | 
451 | //== Pagination
452 | //
453 | //##
454 | 
455 | $pagination-color:                     $link-color !default;
456 | $pagination-bg:                        #fff !default;
457 | $pagination-border:                    #ddd !default;
458 | 
459 | $pagination-hover-color:               $link-hover-color !default;
460 | $pagination-hover-bg:                  $gray-lighter !default;
461 | $pagination-hover-border:              #ddd !default;
462 | 
463 | $pagination-active-color:              #fff !default;
464 | $pagination-active-bg:                 $brand-primary !default;
465 | $pagination-active-border:             $brand-primary !default;
466 | 
467 | $pagination-disabled-color:            $gray-light !default;
468 | $pagination-disabled-bg:               #fff !default;
469 | $pagination-disabled-border:           #ddd !default;
470 | 
471 | 
472 | //== Pager
473 | //
474 | //##
475 | 
476 | $pager-bg:                             $pagination-bg !default;
477 | $pager-border:                         $pagination-border !default;
478 | $pager-border-radius:                  15px !default;
479 | 
480 | $pager-hover-bg:                       $pagination-hover-bg !default;
481 | 
482 | $pager-active-bg:                      $pagination-active-bg !default;
483 | $pager-active-color:                   $pagination-active-color !default;
484 | 
485 | $pager-disabled-color:                 $pagination-disabled-color !default;
486 | 
487 | 
488 | //== Jumbotron
489 | //
490 | //##
491 | 
492 | $jumbotron-padding:              30px !default;
493 | $jumbotron-color:                inherit !default;
494 | $jumbotron-bg:                   $gray-lighter !default;
495 | $jumbotron-heading-color:        inherit !default;
496 | $jumbotron-font-size:            ceil(($font-size-base * 1.5)) !default;
497 | $jumbotron-heading-font-size:    ceil(($font-size-base * 4.5)) !default;
498 | 
499 | 
500 | //== Form states and alerts
501 | //
502 | //## Define colors for form feedback states and, by default, alerts.
503 | 
504 | $state-success-text:             #3c763d !default;
505 | $state-success-bg:               #dff0d8 !default;
506 | $state-success-border:           darken(adjust-hue($state-success-bg, -10), 5%) !default;
507 | 
508 | $state-info-text:                #31708f !default;
509 | $state-info-bg:                  #d9edf7 !default;
510 | $state-info-border:              darken(adjust-hue($state-info-bg, -10), 7%) !default;
511 | 
512 | $state-warning-text:             #8a6d3b !default;
513 | $state-warning-bg:               #fcf8e3 !default;
514 | $state-warning-border:           darken(adjust-hue($state-warning-bg, -10), 5%) !default;
515 | 
516 | $state-danger-text:              #a94442 !default;
517 | $state-danger-bg:                #f2dede !default;
518 | $state-danger-border:            darken(adjust-hue($state-danger-bg, -10), 5%) !default;
519 | 
520 | 
521 | //== Tooltips
522 | //
523 | //##
524 | 
525 | //** Tooltip max width
526 | $tooltip-max-width:           200px !default;
527 | //** Tooltip text color
528 | $tooltip-color:               #fff !default;
529 | //** Tooltip background color
530 | $tooltip-bg:                  #000 !default;
531 | $tooltip-opacity:             .9 !default;
532 | 
533 | //** Tooltip arrow width
534 | $tooltip-arrow-width:         5px !default;
535 | //** Tooltip arrow color
536 | $tooltip-arrow-color:         $tooltip-bg !default;
537 | 
538 | 
539 | //== Popovers
540 | //
541 | //##
542 | 
543 | //** Popover body background color
544 | $popover-bg:                          #fff !default;
545 | //** Popover maximum width
546 | $popover-max-width:                   276px !default;
547 | //** Popover border color
548 | $popover-border-color:                rgba(0,0,0,.2) !default;
549 | //** Popover fallback border color
550 | $popover-fallback-border-color:       #ccc !default;
551 | 
552 | //** Popover title background color
553 | $popover-title-bg:                    darken($popover-bg, 3%) !default;
554 | 
555 | //** Popover arrow width
556 | $popover-arrow-width:                 10px !default;
557 | //** Popover arrow color
558 | $popover-arrow-color:                 $popover-bg !default;
559 | 
560 | //** Popover outer arrow width
561 | $popover-arrow-outer-width:           ($popover-arrow-width + 1) !default;
562 | //** Popover outer arrow color
563 | $popover-arrow-outer-color:           fade_in($popover-border-color, 0.05) !default;
564 | //** Popover outer arrow fallback color
565 | $popover-arrow-outer-fallback-color:  darken($popover-fallback-border-color, 20%) !default;
566 | 
567 | 
568 | //== Labels
569 | //
570 | //##
571 | 
572 | //** Default label background color
573 | $label-default-bg:            $gray-light !default;
574 | //** Primary label background color
575 | $label-primary-bg:            $brand-primary !default;
576 | //** Success label background color
577 | $label-success-bg:            $brand-success !default;
578 | //** Info label background color
579 | $label-info-bg:               $brand-info !default;
580 | //** Warning label background color
581 | $label-warning-bg:            $brand-warning !default;
582 | //** Danger label background color
583 | $label-danger-bg:             $brand-danger !default;
584 | 
585 | //** Default label text color
586 | $label-color:                 #fff !default;
587 | //** Default text color of a linked label
588 | $label-link-hover-color:      #fff !default;
589 | 
590 | 
591 | //== Modals
592 | //
593 | //##
594 | 
595 | //** Padding applied to the modal body
596 | $modal-inner-padding:         15px !default;
597 | 
598 | //** Padding applied to the modal title
599 | $modal-title-padding:         15px !default;
600 | //** Modal title line-height
601 | $modal-title-line-height:     $line-height-base !default;
602 | 
603 | //** Background color of modal content area
604 | $modal-content-bg:                             #fff !default;
605 | //** Modal content border color
606 | $modal-content-border-color:                   rgba(0,0,0,.2) !default;
607 | //** Modal content border color **for IE8**
608 | $modal-content-fallback-border-color:          #999 !default;
609 | 
610 | //** Modal backdrop background color
611 | $modal-backdrop-bg:           #000 !default;
612 | //** Modal backdrop opacity
613 | $modal-backdrop-opacity:      .5 !default;
614 | //** Modal header border color
615 | $modal-header-border-color:   #e5e5e5 !default;
616 | //** Modal footer border color
617 | $modal-footer-border-color:   $modal-header-border-color !default;
618 | 
619 | $modal-lg:                    900px !default;
620 | $modal-md:                    600px !default;
621 | $modal-sm:                    300px !default;
622 | 
623 | 
624 | //== Alerts
625 | //
626 | //## Define alert colors, border radius, and padding.
627 | 
628 | $alert-padding:               15px !default;
629 | $alert-border-radius:         $border-radius-base !default;
630 | $alert-link-font-weight:      bold !default;
631 | 
632 | $alert-success-bg:            $state-success-bg !default;
633 | $alert-success-text:          $state-success-text !default;
634 | $alert-success-border:        $state-success-border !default;
635 | 
636 | $alert-info-bg:               $state-info-bg !default;
637 | $alert-info-text:             $state-info-text !default;
638 | $alert-info-border:           $state-info-border !default;
639 | 
640 | $alert-warning-bg:            $state-warning-bg !default;
641 | $alert-warning-text:          $state-warning-text !default;
642 | $alert-warning-border:        $state-warning-border !default;
643 | 
644 | $alert-danger-bg:             $state-danger-bg !default;
645 | $alert-danger-text:           $state-danger-text !default;
646 | $alert-danger-border:         $state-danger-border !default;
647 | 
648 | 
649 | //== Progress bars
650 | //
651 | //##
652 | 
653 | //** Background color of the whole progress component
654 | $progress-bg:                 #f5f5f5 !default;
655 | //** Progress bar text color
656 | $progress-bar-color:          #fff !default;
657 | //** Variable for setting rounded corners on progress bar.
658 | $progress-border-radius:      $border-radius-base !default;
659 | 
660 | //** Default progress bar color
661 | $progress-bar-bg:             $brand-primary !default;
662 | //** Success progress bar color
663 | $progress-bar-success-bg:     $brand-success !default;
664 | //** Warning progress bar color
665 | $progress-bar-warning-bg:     $brand-warning !default;
666 | //** Danger progress bar color
667 | $progress-bar-danger-bg:      $brand-danger !default;
668 | //** Info progress bar color
669 | $progress-bar-info-bg:        $brand-info !default;
670 | 
671 | 
672 | //== List group
673 | //
674 | //##
675 | 
676 | //** Background color on `.list-group-item`
677 | $list-group-bg:                 #fff !default;
678 | //** `.list-group-item` border color
679 | $list-group-border:             #ddd !default;
680 | //** List group border radius
681 | $list-group-border-radius:      $border-radius-base !default;
682 | 
683 | //** Background color of single list items on hover
684 | $list-group-hover-bg:           #f5f5f5 !default;
685 | //** Text color of active list items
686 | $list-group-active-color:       $component-active-color !default;
687 | //** Background color of active list items
688 | $list-group-active-bg:          $component-active-bg !default;
689 | //** Border color of active list elements
690 | $list-group-active-border:      $list-group-active-bg !default;
691 | //** Text color for content within active list items
692 | $list-group-active-text-color:  lighten($list-group-active-bg, 40%) !default;
693 | 
694 | //** Text color of disabled list items
695 | $list-group-disabled-color:      $gray-light !default;
696 | //** Background color of disabled list items
697 | $list-group-disabled-bg:         $gray-lighter !default;
698 | //** Text color for content within disabled list items
699 | $list-group-disabled-text-color: $list-group-disabled-color !default;
700 | 
701 | $list-group-link-color:         #555 !default;
702 | $list-group-link-hover-color:   $list-group-link-color !default;
703 | $list-group-link-heading-color: #333 !default;
704 | 
705 | 
706 | //== Panels
707 | //
708 | //##
709 | 
710 | $panel-bg:                    #fff !default;
711 | $panel-body-padding:          15px !default;
712 | $panel-heading-padding:       10px 15px !default;
713 | $panel-footer-padding:        $panel-heading-padding !default;
714 | $panel-border-radius:         $border-radius-base !default;
715 | 
716 | //** Border color for elements within panels
717 | $panel-inner-border:          #ddd !default;
718 | $panel-footer-bg:             #f5f5f5 !default;
719 | 
720 | $panel-default-text:          $gray-dark !default;
721 | $panel-default-border:        #ddd !default;
722 | $panel-default-heading-bg:    #f5f5f5 !default;
723 | 
724 | $panel-primary-text:          #fff !default;
725 | $panel-primary-border:        $brand-primary !default;
726 | $panel-primary-heading-bg:    $brand-primary !default;
727 | 
728 | $panel-success-text:          $state-success-text !default;
729 | $panel-success-border:        $state-success-border !default;
730 | $panel-success-heading-bg:    $state-success-bg !default;
731 | 
732 | $panel-info-text:             $state-info-text !default;
733 | $panel-info-border:           $state-info-border !default;
734 | $panel-info-heading-bg:       $state-info-bg !default;
735 | 
736 | $panel-warning-text:          $state-warning-text !default;
737 | $panel-warning-border:        $state-warning-border !default;
738 | $panel-warning-heading-bg:    $state-warning-bg !default;
739 | 
740 | $panel-danger-text:           $state-danger-text !default;
741 | $panel-danger-border:         $state-danger-border !default;
742 | $panel-danger-heading-bg:     $state-danger-bg !default;
743 | 
744 | 
745 | //== Thumbnails
746 | //
747 | //##
748 | 
749 | //** Padding around the thumbnail image
750 | $thumbnail-padding:           4px !default;
751 | //** Thumbnail background color
752 | $thumbnail-bg:                $body-bg !default;
753 | //** Thumbnail border color
754 | $thumbnail-border:            #ddd !default;
755 | //** Thumbnail border radius
756 | $thumbnail-border-radius:     $border-radius-base !default;
757 | 
758 | //** Custom text color for thumbnail captions
759 | $thumbnail-caption-color:     $text-color !default;
760 | //** Padding around the thumbnail caption
761 | $thumbnail-caption-padding:   9px !default;
762 | 
763 | 
764 | //== Wells
765 | //
766 | //##
767 | 
768 | $well-bg:                     #f5f5f5 !default;
769 | $well-border:                 darken($well-bg, 7%) !default;
770 | 
771 | 
772 | //== Badges
773 | //
774 | //##
775 | 
776 | $badge-color:                 #fff !default;
777 | //** Linked badge text color on hover
778 | $badge-link-hover-color:      #fff !default;
779 | $badge-bg:                    $gray-light !default;
780 | 
781 | //** Badge text color in active nav link
782 | $badge-active-color:          $link-color !default;
783 | //** Badge background color in active nav link
784 | $badge-active-bg:             #fff !default;
785 | 
786 | $badge-font-weight:           bold !default;
787 | $badge-line-height:           1 !default;
788 | $badge-border-radius:         10px !default;
789 | 
790 | 
791 | //== Breadcrumbs
792 | //
793 | //##
794 | 
795 | $breadcrumb-padding-vertical:   8px !default;
796 | $breadcrumb-padding-horizontal: 15px !default;
797 | //** Breadcrumb background color
798 | $breadcrumb-bg:                 #f5f5f5 !default;
799 | //** Breadcrumb text color
800 | $breadcrumb-color:              #ccc !default;
801 | //** Text color of current page in the breadcrumb
802 | $breadcrumb-active-color:       $gray-light !default;
803 | //** Textual separator for between breadcrumb elements
804 | $breadcrumb-separator:          "/" !default;
805 | 
806 | 
807 | //== Carousel
808 | //
809 | //##
810 | 
811 | $carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6) !default;
812 | 
813 | $carousel-control-color:                      #fff !default;
814 | $carousel-control-width:                      15% !default;
815 | $carousel-control-opacity:                    .5 !default;
816 | $carousel-control-font-size:                  20px !default;
817 | 
818 | $carousel-indicator-active-bg:                #fff !default;
819 | $carousel-indicator-border-color:             #fff !default;
820 | 
821 | $carousel-caption-color:                      #fff !default;
822 | 
823 | 
824 | //== Close
825 | //
826 | //##
827 | 
828 | $close-font-weight:           bold !default;
829 | $close-color:                 #000 !default;
830 | $close-text-shadow:           0 1px 0 #fff !default;
831 | 
832 | 
833 | //== Code
834 | //
835 | //##
836 | 
837 | $code-color:                  #c7254e !default;
838 | $code-bg:                     #f9f2f4 !default;
839 | 
840 | $kbd-color:                   #fff !default;
841 | $kbd-bg:                      #333 !default;
842 | 
843 | $pre-bg:                      #f5f5f5 !default;
844 | $pre-color:                   $gray-dark !default;
845 | $pre-border-color:            #ccc !default;
846 | $pre-scrollable-max-height:   340px !default;
847 | 
848 | 
849 | //== Type
850 | //
851 | //##
852 | 
853 | //** Horizontal offset for forms and lists.
854 | $component-offset-horizontal: 180px !default;
855 | //** Text muted color
856 | $text-muted:                  $gray-light !default;
857 | //** Abbreviations and acronyms border color
858 | $abbr-border-color:           $gray-light !default;
859 | //** Headings small color
860 | $headings-small-color:        $gray-light !default;
861 | //** Blockquote small color
862 | $blockquote-small-color:      $gray-light !default;
863 | //** Blockquote font size
864 | $blockquote-font-size:        ($font-size-base * 1.25) !default;
865 | //** Blockquote border color
866 | $blockquote-border-color:     $gray-lighter !default;
867 | //** Page header border color
868 | $page-header-border-color:    $gray-lighter !default;
869 | //** Width of horizontal description list titles
870 | $dl-horizontal-offset:        $component-offset-horizontal !default;
871 | //** Horizontal line color.
872 | $hr-border:                   $gray-lighter !default;
873 | 


--------------------------------------------------------------------------------