├── .gitattributes ├── app ├── .buildignore ├── robots.txt ├── styles │ ├── nv.d3.less │ ├── widgets.less │ ├── main.less │ ├── bootstrap.less │ └── themes │ │ ├── cyborg.less │ │ ├── default.less │ │ ├── simplex.less │ │ └── amelia.less ├── favicon.ico ├── images │ ├── main_banner.png │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── template │ ├── percentage.html │ ├── topics.html │ └── widgetOptions.html ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── views │ ├── main.html │ ├── simple.html │ └── apps.html ├── settings.js ├── scripts │ ├── controllers │ │ ├── simple.js │ │ ├── meteor.js │ │ ├── topics.js │ │ ├── widgetOptions.js │ │ ├── apps.js │ │ ├── serverdata.js │ │ ├── rest.js │ │ ├── main.js │ │ └── discovery.js │ ├── app.js │ ├── services │ │ ├── gateway.js │ │ ├── widgets.js │ │ ├── service.js │ │ └── datamodel.js │ └── vendor │ │ ├── visibly.js │ │ └── meteor-ddp.js ├── index.html └── 404.html ├── start_dist.sh ├── start_dev.sh ├── dist ├── robots.txt ├── favicon.ico ├── images │ ├── 570fe1a1.main_banner.png │ ├── 5aa9bc32.glyphicons-halflings.png │ └── 0220d123.glyphicons-halflings-white.png ├── template │ ├── percentage.html │ ├── topics.html │ └── widgetOptions.html ├── views │ ├── main.html │ └── apps.html ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── settings.js ├── index.html └── 404.html ├── .bowerrc ├── forever_dev.js ├── .gitignore ├── docs ├── AngularJSDashboard.png └── ProjectDependencies.png ├── dev_start.sh ├── .travis.yml ├── test ├── runner.html ├── spec │ └── controllers │ │ └── main.js └── .jshintrc ├── .editorconfig ├── .jshintrc ├── config.js ├── app.js ├── bower.json ├── CONTRIBUTING.md ├── karma-e2e.conf.js ├── package.json ├── karma.conf.js ├── README.md ├── Gruntfile.js └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /app/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /start_dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | node app.js -------------------------------------------------------------------------------- /start_dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | STATIC_DIR=/app node app.js -------------------------------------------------------------------------------- /app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /dist/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /forever_dev.js: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export STATIC_DIR=/app 3 | forever start app.js -------------------------------------------------------------------------------- /app/styles/nv.d3.less: -------------------------------------------------------------------------------- 1 | svg { 2 | width: auto; 3 | height: auto; 4 | } 5 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .tmp 3 | .sass-cache 4 | .idea 5 | .DS_Store 6 | app/bower_components 7 | -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/dist/favicon.ico -------------------------------------------------------------------------------- /app/images/main_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/app/images/main_banner.png -------------------------------------------------------------------------------- /docs/AngularJSDashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/docs/AngularJSDashboard.png -------------------------------------------------------------------------------- /docs/ProjectDependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/docs/ProjectDependencies.png -------------------------------------------------------------------------------- /app/images/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/app/images/glyphicons-halflings.png -------------------------------------------------------------------------------- /app/template/percentage.html: -------------------------------------------------------------------------------- 1 |
2 | Template from templateUrl 3 |
{{percentage}}
4 |
-------------------------------------------------------------------------------- /dev_start.sh: -------------------------------------------------------------------------------- 1 | export PORT=3005 2 | export STATIC_DIR=/app 3 | export GATEWAY_HOST=localhost:3390 4 | 5 | #forever start app.js 6 | node app.js -------------------------------------------------------------------------------- /dist/images/570fe1a1.main_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/dist/images/570fe1a1.main_banner.png -------------------------------------------------------------------------------- /dist/template/percentage.html: -------------------------------------------------------------------------------- 1 |
2 | Template from templateUrl 3 |
{{percentage}}
4 |
-------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_script: 5 | - 'npm install -g bower grunt-cli' 6 | - 'bower install' 7 | -------------------------------------------------------------------------------- /dist/views/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
-------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/app/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/app/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/images/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/app/images/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /app/views/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | -------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/app/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /dist/images/5aa9bc32.glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/dist/images/5aa9bc32.glyphicons-halflings.png -------------------------------------------------------------------------------- /dist/images/0220d123.glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtpublic/malhar-dashboard-webapp/HEAD/dist/images/0220d123.glyphicons-halflings-white.png -------------------------------------------------------------------------------- /app/styles/widgets.less: -------------------------------------------------------------------------------- 1 | .widget { 2 | .alert { 3 | margin: 0; 4 | } 5 | 6 | .top-n .grid { 7 | width: 100%; 8 | height: 350px; 9 | border: 1px solid rgb(212, 212, 212); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/views/simple.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | Option Selected: {{value}}

5 |
6 |
7 | -------------------------------------------------------------------------------- /app/views/apps.html: -------------------------------------------------------------------------------- 1 |
2 |

Select Application to Load

3 |
Loading Applications...
4 | 5 |
6 |
-------------------------------------------------------------------------------- /dist/views/apps.html: -------------------------------------------------------------------------------- 1 |
2 |

Select Application to Load

3 |
Loading Applications...
4 | 5 |
6 |
-------------------------------------------------------------------------------- /test/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | End2end Test Runner 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "angular": false, 23 | "jQuery": false, 24 | "_": false, 25 | "d3": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/spec/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MainCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('app')); 7 | 8 | var MainCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | MainCtrl = $controller('MainCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should be defined', function () { 20 | expect(MainCtrl).toBeDefined(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "after": false, 23 | "afterEach": false, 24 | "angular": false, 25 | "before": false, 26 | "beforeEach": false, 27 | "browser": false, 28 | "describe": false, 29 | "expect": false, 30 | "inject": false, 31 | "it": false, 32 | "spyOn": false 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /app/settings.js: -------------------------------------------------------------------------------- 1 | // client-side settings (for dev only) 2 | window.settings = {}; 3 | 4 | settings.gatewayHost = 'localhost:9090'; 5 | settings.meteorHost = 'localhost:5000'; 6 | 7 | settings.webSocketURL = 'ws://' + settings.gatewayHost + '/pubsub'; 8 | settings.restBaseURL = 'http://' + settings.gatewayHost + '/ws/v1/'; 9 | settings.meteorURL = 'ws://' + settings.meteorHost + '/websocket'; 10 | 11 | settings.topic = {}; 12 | settings.topic.visualdata = {}; 13 | 14 | settings.topic.visualdata.piValue = 'piValue'; 15 | settings.topic.visualdata.percentage = 'percentage'; 16 | settings.topic.visualdata.progress = 'progress'; 17 | settings.topic.visualdata.chartValue = 'chartValue'; 18 | settings.topic.visualdata.chartValue2 = 'chartValue2'; 19 | settings.topic.visualdata.topn = 'topn'; 20 | settings.topic.visualdata.pieChart = 'piechart'; -------------------------------------------------------------------------------- /dist/settings.js: -------------------------------------------------------------------------------- 1 | // client-side settings (for dev only) 2 | window.settings = {}; 3 | 4 | settings.gatewayHost = 'localhost:9090'; 5 | settings.meteorHost = 'localhost:5000'; 6 | 7 | settings.webSocketURL = 'ws://' + settings.gatewayHost + '/pubsub'; 8 | settings.restBaseURL = 'http://' + settings.gatewayHost + '/ws/v1/'; 9 | settings.meteorURL = 'ws://' + settings.meteorHost + '/websocket'; 10 | 11 | settings.topic = {}; 12 | settings.topic.visualdata = {}; 13 | 14 | settings.topic.visualdata.piValue = 'piValue'; 15 | settings.topic.visualdata.percentage = 'percentage'; 16 | settings.topic.visualdata.progress = 'progress'; 17 | settings.topic.visualdata.chartValue = 'chartValue'; 18 | settings.topic.visualdata.chartValue2 = 'chartValue2'; 19 | settings.topic.visualdata.topn = 'topn'; 20 | settings.topic.visualdata.pieChart = 'piechart'; -------------------------------------------------------------------------------- /app/scripts/controllers/simple.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | angular.module('app') 20 | .controller('SimpleCtrl', function ($scope) { 21 | $scope.values = ['option1', 'option2', 'option3']; 22 | $scope.value = $scope.values[0]; 23 | }); -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | var config = {}; 2 | 3 | config.port = process.env.PORT || 3000; 4 | config.staticDir = process.env.STATIC_DIR || '/dist'; 5 | 6 | // client settings (passed to the browser) 7 | config.settings = {}; 8 | var settings = config.settings; 9 | 10 | settings.gatewayHost = process.env.GATEWAY_HOST || 'localhost:9090'; 11 | settings.meteorHost = process.env.METEOR_HOST || 'localhost:5000'; 12 | 13 | settings.webSocketURL = 'ws://' + settings.gatewayHost + '/pubsub'; 14 | settings.restBaseURL = 'http://' + settings.gatewayHost + '/ws/v1/'; 15 | settings.meteorURL = 'ws://' + settings.meteorHost + '/websocket'; 16 | 17 | settings.topic = {}; 18 | settings.topic.visualdata = {}; 19 | 20 | settings.topic.visualdata.piValue = 'piValue'; 21 | settings.topic.visualdata.percentage = 'percentage'; 22 | settings.topic.visualdata.progress = 'progress'; 23 | settings.topic.visualdata.chartValue = 'chartValue'; 24 | settings.topic.visualdata.chartValue2 = 'chartValue2'; 25 | settings.topic.visualdata.topn = 'topn'; 26 | settings.topic.visualdata.pieChart = 'piechart'; 27 | 28 | module.exports = config; 29 | -------------------------------------------------------------------------------- /app/template/topics.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 |

5 | 8 | 9 | 12 | 15 |
16 |

17 |

18 | 19 | 20 |

21 |
{{topicSchema}}
22 |
23 |

24 |

25 | 26 | 27 |

28 |
{{topicData}}
29 |
30 |
31 |

-------------------------------------------------------------------------------- /dist/template/topics.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 |

5 | 8 | 9 | 12 | 15 |
16 |

17 |

18 | 19 | 20 |

21 |
{{topicSchema}}
22 |
23 |

24 |

25 | 26 | 27 |

28 |
{{topicData}}
29 |
30 |
31 |

-------------------------------------------------------------------------------- /app/styles/main.less: -------------------------------------------------------------------------------- 1 | // path to fonts 2 | @icon-font-path: "../fonts/"; 3 | @import "bootstrap"; 4 | @import "../bower_components/pines-notify/pnotify.core.css"; 5 | @import "../bower_components/nvd3/nv.d3.css"; 6 | @import "../bower_components/ng-grid/ng-grid.css"; 7 | @import "nv.d3"; 8 | @import "../bower_components/malhar-angular-dashboard/dist/angular-ui-dashboard.css"; 9 | 10 | // app-specific styles 11 | @import "widgets"; 12 | 13 | body { 14 | padding-top: 50px; 15 | } 16 | 17 | .main-view { 18 | margin: 15px; 19 | } 20 | 21 | .navbar-inverse { 22 | background: #11506d; 23 | 24 | .navbar-brand { 25 | color: #ffffff; 26 | 27 | sup { 28 | vertical-align: middle; 29 | font-size: 50%; 30 | font-weight: normal; 31 | } 32 | } 33 | } 34 | 35 | .ngGrid { 36 | border: 1px solid rgb(212,212,212); 37 | } 38 | 39 | .applist { 40 | .ngGrid { 41 | width: 800px; 42 | height: 500px; 43 | } 44 | 45 | .alert { 46 | display: inline-block; 47 | } 48 | } 49 | 50 | fieldset.overview-group { 51 | border: 1px solid black; 52 | padding: 10px; 53 | 54 | legend { 55 | width: auto; 56 | border: 0; 57 | } 58 | } -------------------------------------------------------------------------------- /app/scripts/controllers/meteor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | .controller('MeteorCtrl', function ($scope, MeteorDataModel, MeteorTimeSeriesDataModel) { 5 | var widgetDefs = [ 6 | { 7 | name: 'Line Chart', 8 | directive: 'wt-line-chart', 9 | dataAttrName: 'chart', 10 | dataModelType: MeteorTimeSeriesDataModel, 11 | dataModelOptions: { 12 | collection: 'history' 13 | }, 14 | style: { 15 | width: '50%' 16 | } 17 | }, 18 | { 19 | name: 'JSON', 20 | directive: 'wt-json', 21 | dataAttrName: 'value', 22 | dataModelType: MeteorDataModel, 23 | dataModelOptions: { 24 | collection: 'history' 25 | } 26 | } 27 | ]; 28 | 29 | var defaultWidgets = [ 30 | { 31 | name: 'Line Chart', 32 | title: 'Meteor MongoDB Historical Data' 33 | }, 34 | { 35 | name: 'JSON', 36 | title: 'Collection Changes' 37 | } 38 | ]; 39 | 40 | $scope.dashboardOptions = { 41 | widgetButtons: true, 42 | widgetDefinitions: widgetDefs, 43 | defaultWidgets: defaultWidgets, 44 | optionsTemplateUrl: 'template/widgetOptions.html' 45 | }; 46 | }); 47 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var http = require('http'); 3 | var config = require('./config'); 4 | 5 | var app = express(); 6 | 7 | var allowCrossDomain = function(req, res, next) { 8 | res.header('Access-Control-Allow-Origin', '*'); 9 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); 10 | res.header('Access-Control-Allow-Headers', 'Content-Type'); 11 | next(); 12 | }; 13 | 14 | // all environments 15 | app.use(express.favicon()); 16 | app.use(express.logger('dev')); 17 | app.use(express.bodyParser()); 18 | app.use(express.methodOverride()); 19 | app.use(allowCrossDomain); 20 | app.use(app.router); 21 | 22 | console.log('environment: ' + app.get('env')); 23 | 24 | app.use(express.static(__dirname + config.staticDir)); 25 | 26 | if ('development' === app.get('env')) { 27 | app.use(express.errorHandler()); 28 | } 29 | 30 | app.get('/settings.js', function(req, res) { 31 | res.setHeader('Content-Type', 'application/javascript'); 32 | res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); 33 | res.setHeader('Pragma', 'no-cache'); 34 | res.setHeader('Expires', 0); 35 | 36 | res.send('window.settings = ' + JSON.stringify(config.settings) + ';'); 37 | }); 38 | 39 | http.createServer(app).listen(config.port, function(){ 40 | console.log('Express server listening on port ' + config.port); 41 | }); -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard-webapp", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "angular": "~1.3", 6 | "json3": "~3.2.4", 7 | "jquery": "~1.9.1", 8 | "es5-shim": "~2.0.8", 9 | "angular-resource": "~1.3", 10 | "angular-cookies": "~1.3", 11 | "angular-sanitize": "~1.3", 12 | "gridster": "~0.2.1", 13 | "bootstrap": "~3.0.3", 14 | "angular-bootstrap": "0.9.0", 15 | "angular-route": "~1.3", 16 | "jquery-ui": "~1.10.3", 17 | "underscore": "~1.5.2", 18 | "ng-grid": "2.0.11", 19 | "d3": "3.4.4", 20 | "nvd3": "~1.1.15-beta", 21 | "angularjs-nvd3-directives": "~0.0.7", 22 | "angular-pines-notify": "~0.1", 23 | "pines-notify": "~1.3", 24 | "angular-ui-sortable": "0.12.5", 25 | "malhar-angular-widgets": "https://github.com/DataTorrent/malhar-angular-widgets.git#master", 26 | "malhar-angular-dashboard": "https://github.com/DataTorrent/malhar-angular-dashboard.git#master" 27 | }, 28 | "devDependencies": { 29 | "angular-mocks": "~1.3", 30 | "angular-scenario": "~1.3" 31 | }, 32 | "resolutions": { 33 | "angular-ui-sortable": "0.12.5", 34 | "d3": "3.4.4", 35 | "jquery": "~2.0.3", 36 | "angular-bootstrap": "~0.11.0", 37 | "jquery-ui": "~1.11.0", 38 | "pines-notify": "~2.0.1", 39 | "angular": "~1.2.22", 40 | "angular-pines-notify": "~1.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing Guidelines 2 | ======================= 3 | This project welcomes new contributors. 4 | 5 | Licensing 6 | --------- 7 | You acknowledge that your submissions to DataTorrent on this repository are made pursuant the terms of the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html) and constitute "Contributions," as defined therein, and you represent and warrant that you have the right and authority to do so. 8 | 9 | When adding **new javascript files**, please include the following Apache v2.0 license header at the top of the file, with the fields enclosed by brackets "[]" replaced with your own identifying information. **(Don't include the brackets!)**: 10 | 11 | ```JavaScript 12 | /* 13 | * Copyright (c) [XXXX] [NAME OF COPYRIGHT OWNER] 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | ``` -------------------------------------------------------------------------------- /app/scripts/controllers/topics.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | .controller('TopicCtrl', function ($scope, webSocket, Gateway) { 5 | $scope.prevTopic = function () { 6 | var index = $scope.topics.indexOf($scope.topic); 7 | var prev = ($scope.topics.length + index - 1) % $scope.topics.length; 8 | $scope.topic = $scope.topics[prev]; 9 | }; 10 | 11 | $scope.nextTopic = function () { 12 | var index = $scope.topics.indexOf($scope.topic); 13 | var next = (index + 1) % $scope.topics.length; 14 | $scope.topic = $scope.topics[next]; 15 | }; 16 | 17 | $scope.selectTopic = function (topic) { 18 | $scope.topic = topic; 19 | }; 20 | 21 | var callback = function (message) { 22 | $scope.topicData = JSON.stringify(message, null, ' '); 23 | $scope.$apply(); 24 | }; 25 | 26 | $scope.$watch('topic', function (newTopic, oldTopic) { 27 | if (oldTopic && callback) { 28 | webSocket.unsubscribe(oldTopic.topic, callback); 29 | } 30 | 31 | if (newTopic) { 32 | $scope.topicData = 'Loading...'; 33 | $scope.topicSchema = JSON.stringify(newTopic.schema, null, ' '); 34 | webSocket.subscribe(newTopic.topic, callback, $scope); 35 | } 36 | }); 37 | 38 | $scope.topics = []; 39 | 40 | Gateway.getTopics().then(function (topics) { 41 | $scope.topics = topics; 42 | if (topics.length) { 43 | $scope.topic = $scope.topics[0]; 44 | } 45 | }); 46 | }); -------------------------------------------------------------------------------- /karma-e2e.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | 9 | // testing framework to use (jasmine/mocha/qunit/...) 10 | frameworks: ['ng-scenario'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | 'test/e2e/**/*.js' 15 | ], 16 | 17 | // list of files / patterns to exclude 18 | exclude: [], 19 | 20 | // web server port 21 | port: 8080, 22 | 23 | // level of logging 24 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 25 | logLevel: config.LOG_INFO, 26 | 27 | 28 | // enable / disable watching file and executing tests whenever any file changes 29 | autoWatch: false, 30 | 31 | 32 | // Start these browsers, currently available: 33 | // - Chrome 34 | // - ChromeCanary 35 | // - Firefox 36 | // - Opera 37 | // - Safari (only Mac) 38 | // - PhantomJS 39 | // - IE (only Windows) 40 | browsers: ['Chrome'], 41 | 42 | 43 | // Continuous Integration mode 44 | // if true, it capture browsers, run tests and exit 45 | singleRun: false 46 | 47 | // Uncomment the following lines if you are using grunt's server to run the tests 48 | // proxies: { 49 | // '/': 'http://localhost:9000/' 50 | // }, 51 | // URL root prevent conflicts with the site root 52 | // urlRoot: '_karma_' 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /app/scripts/controllers/widgetOptions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | .controller('WidgetOptionsCtrl', function ($scope, webSocket, Gateway, settings) { 5 | $scope.dataSourceName = 'Gateway (' + settings.gatewayHost + ')'; //TODO 6 | 7 | $scope.prevTopic = function () { 8 | var index = $scope.topics.indexOf($scope.topic); 9 | var prev = ($scope.topics.length + index - 1) % $scope.topics.length; 10 | $scope.topic = $scope.topics[prev]; 11 | }; 12 | 13 | $scope.nextTopic = function () { 14 | var index = $scope.topics.indexOf($scope.topic); 15 | var next = (index + 1) % $scope.topics.length; 16 | $scope.topic = $scope.topics[next]; 17 | }; 18 | 19 | var widget = $scope.widget; 20 | 21 | if (widget && widget.dataModel) { 22 | $scope.topics = []; 23 | if (widget.dataTypes) { 24 | $scope.dataTypes = widget.dataTypes.join(', '); 25 | } 26 | 27 | Gateway.getTopics().then(function (topics) { 28 | if (widget.dataTypes) { 29 | topics = _.reject(topics, function (topic) { 30 | return !topic.schema || !_.contains(widget.dataTypes, topic.schema.type); 31 | }); 32 | } 33 | 34 | $scope.topics = topics; 35 | $scope.topic = _.findWhere($scope.topics, {topic: widget.dataModel.topic}); 36 | }); 37 | 38 | $scope.$watch('topic', function (newTopic) { 39 | if (newTopic && (newTopic.topic !== widget.dataModel.topic)) { 40 | widget.dataModel.update(newTopic.topic); 41 | } 42 | }); 43 | 44 | $scope.selectTopic = function (topic) { 45 | $scope.topic = topic; 46 | }; 47 | } 48 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-dashboard", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "express": "~3.4.8", 6 | "mongodb": "^1.4.0", 7 | "lodash": "^2.4.1" 8 | }, 9 | "devDependencies": { 10 | "grunt": "~0.4.1", 11 | "grunt-contrib-copy": "~0.4.1", 12 | "grunt-contrib-concat": "~0.3.0", 13 | "grunt-contrib-coffee": "~0.7.0", 14 | "grunt-contrib-uglify": "~0.2.0", 15 | "grunt-contrib-compass": "~0.5.0", 16 | "grunt-contrib-jshint": "~0.6.0", 17 | "grunt-contrib-cssmin": "~0.6.0", 18 | "grunt-contrib-connect": "~0.5.0", 19 | "grunt-contrib-clean": "~0.5.0", 20 | "grunt-contrib-htmlmin": "~0.1.3", 21 | "grunt-contrib-watch": "~0.5.2", 22 | "grunt-autoprefixer": "~0.2.0", 23 | "grunt-usemin": "~0.1.11", 24 | "grunt-svgmin": "~0.2.0", 25 | "grunt-rev": "~0.1.0", 26 | "grunt-concurrent": "~0.3.0", 27 | "load-grunt-tasks": "~0.1.0", 28 | "grunt-google-cdn": "~0.2.0", 29 | "grunt-ngmin": "~0.0.2", 30 | "time-grunt": "~0.1.0", 31 | "karma-ng-scenario": "~0.1.0", 32 | "grunt-karma": "~0.6.2", 33 | "karma-script-launcher": "~0.1.0", 34 | "karma-chrome-launcher": "~0.1.2", 35 | "karma-firefox-launcher": "~0.1.3", 36 | "karma-html2js-preprocessor": "~0.1.0", 37 | "karma-jasmine": "~0.1.5", 38 | "karma-coffee-preprocessor": "~0.1.2", 39 | "requirejs": "~2.1.9", 40 | "karma-requirejs": "~0.2.1", 41 | "karma-phantomjs-launcher": "~0.1.1", 42 | "karma": "~0.10.9", 43 | "karma-ng-html2js-preprocessor": "~0.1.0", 44 | "grunt-contrib-less": "~0.9.0" 45 | }, 46 | "engines": { 47 | "node": ">=0.8.0" 48 | }, 49 | "scripts": { 50 | "test": "grunt test" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/scripts/controllers/apps.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | .controller('AppsCtrl', function ($scope, Gateway) { 5 | $scope.showLoading = true; 6 | 7 | var topicsPromise = Gateway.getTopics(); 8 | 9 | topicsPromise.then(function (topics) { 10 | var appIdMap = {}; 11 | 12 | _.each(topics, function (topic) { 13 | var appId = topic.appId; 14 | 15 | if (appIdMap.hasOwnProperty(appId)) { 16 | var app = appIdMap[appId]; 17 | app.topicCount++; 18 | } else { 19 | var newApp = { 20 | id: appId, 21 | name: topic.appName, 22 | startedTime: topic.appStartedTime, 23 | topicCount: 1 24 | }; 25 | appIdMap[appId] = newApp; 26 | } 27 | }); 28 | 29 | $scope.apps = _.sortBy(_.values(appIdMap), function (app) { 30 | return (-app.startedTime); 31 | }); 32 | $scope.showLoading = false; 33 | }, function () { 34 | $scope.showLoading = false; 35 | }); 36 | 37 | var linkTemplate = '
{{COL_FIELD}}
'; 38 | 39 | $scope.gridOptions = { 40 | data: 'apps', 41 | enableRowSelection: false, 42 | enableColumnResize: true, 43 | showFilter: true, 44 | columnDefs: [ 45 | { field: 'id', displayName: 'Id', cellTemplate: linkTemplate, width: 250 }, 46 | { field: 'name', displayName: 'Name' }, 47 | { field: 'startedTime', displayName: 'Start Time', cellFilter: 'date:\'yyyy-MM-dd HH:mm:ss\'', width: 200 }, 48 | { field: 'topicCount', displayName: 'Topic Count', width: 150 } 49 | ] 50 | }; 51 | }); -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | -------------------------------------------------------------------------------- /app/template/widgetOptions.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
6 | 7 |
8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 | 20 | 21 |
22 |
23 | 26 | 29 | 32 |
33 |
34 |
35 | 36 | 50 |
-------------------------------------------------------------------------------- /dist/template/widgetOptions.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
6 | 7 |
8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 | 20 | 21 |
22 |
23 | 26 | 29 | 32 |
33 |
34 |
35 | 36 | 50 |
-------------------------------------------------------------------------------- /app/scripts/controllers/serverdata.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | .controller('ServerDataCtrl', function ($scope, webSocket, Gateway, settings, widgetDefs) { 5 | var defaultWidgets = [ 6 | { 7 | name: 'Value', 8 | title: 'Value 1' 9 | }, 10 | { 11 | name: 'Value', 12 | title: 'Value 2', 13 | dataModelOptions: { 14 | defaultTopic: settings.topic.visualdata.percentage 15 | } 16 | }, 17 | { 18 | name: 'Progressbar', 19 | title: 'Progressbar' 20 | }, 21 | { 22 | name: 'Line Chart', 23 | title: 'Line Chart 1' 24 | }, 25 | { 26 | name: 'Line Chart', 27 | title: 'Line Chart 2', 28 | dataModelOptions: { 29 | defaultTopic: settings.topic.visualdata.chartValue2 30 | } 31 | }, 32 | { 33 | name: 'TopN', 34 | title: 'Top N' 35 | }, 36 | { 37 | name: 'Pie Chart', 38 | title: 'Pie Chart' 39 | }, 40 | { 41 | name: 'JSON', 42 | title: 'JSON' 43 | }, 44 | { 45 | name: 'WebSocket Debugger', 46 | title: 'WebSocket Debugger' 47 | } 48 | ]; 49 | 50 | $scope.dashboardOptions = { 51 | //useLocalStorage: true, //TODO enable by default 52 | widgetButtons: true, 53 | widgetDefinitions: widgetDefs, 54 | defaultWidgets: defaultWidgets, 55 | optionsTemplateUrl: 'template/widgetOptions.html' 56 | }; 57 | 58 | // initialize widgets with default topics 59 | var topicsPromise = Gateway.getTopics(); 60 | 61 | $scope.$on('widgetAdded', function (event, widget) { 62 | event.stopPropagation(); 63 | 64 | if (widget.dataModel && widget.dataModelOptions && widget.dataModelOptions.defaultTopic) { 65 | topicsPromise.then(function (topics) { 66 | var defaultTopic = widget.dataModelOptions.defaultTopic; 67 | 68 | var topic = _.find(topics, function (topic) { 69 | return topic.name.indexOf(defaultTopic) >= 0; 70 | }); 71 | 72 | if (topic) { 73 | widget.dataModel.update(topic.topic); 74 | } 75 | }); 76 | } 77 | }); 78 | }); -------------------------------------------------------------------------------- /app/scripts/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | angular.module('app', [ 20 | 'app.service', 21 | 'ngRoute', 22 | 'ngCookies', 23 | 'ngResource', 24 | 'ngSanitize', 25 | 'nvd3ChartDirectives', 26 | 'ui.dashboard', 27 | 'ui.widgets', 28 | 'ui.models' 29 | ]) 30 | .constant('settings', window.settings) 31 | .config(function ($routeProvider, webSocketProvider, settings) { 32 | if (settings) { 33 | webSocketProvider.setWebSocketURL(settings.webSocketURL); 34 | } 35 | 36 | $routeProvider 37 | .when('/', { 38 | templateUrl: 'views/main.html', 39 | controller: 'MainCtrl' 40 | }) 41 | .when('/simple', { 42 | templateUrl: 'views/simple.html', 43 | controller: 'SimpleCtrl' 44 | }) 45 | .when('/rest', { 46 | templateUrl: 'views/main.html', 47 | controller: 'RestDataCtrl' 48 | }) 49 | .when('/meteor', { 50 | templateUrl: 'views/main.html', 51 | controller: 'MeteorCtrl' 52 | }) 53 | .when('/discovery', { 54 | templateUrl: 'views/main.html', 55 | controller: 'DiscoveryCtrl' 56 | }) 57 | .when('/apps', { 58 | templateUrl: 'views/apps.html', 59 | controller: 'AppsCtrl' 60 | }) 61 | .when('/apps/:appId', { 62 | templateUrl: 'views/main.html', 63 | controller: 'DiscoveryCtrl' 64 | }) 65 | .when('/clientdata', { 66 | templateUrl: 'views/main.html', 67 | controller: 'MainCtrl' 68 | }) 69 | .when('/serverdata', { 70 | templateUrl: 'views/main.html', 71 | controller: 'ServerDataCtrl' 72 | }) 73 | .otherwise({ 74 | redirectTo: '/' 75 | }); 76 | }); -------------------------------------------------------------------------------- /app/styles/bootstrap.less: -------------------------------------------------------------------------------- 1 | @import "../bower_components/bootstrap/less/mixins.less"; 2 | 3 | // Reset 4 | @import "../bower_components/bootstrap/less/normalize.less"; 5 | @import "../bower_components/bootstrap/less/print.less"; 6 | 7 | // Core CSS 8 | @import "../bower_components/bootstrap/less/scaffolding.less"; 9 | @import "../bower_components/bootstrap/less/type.less"; 10 | @import "../bower_components/bootstrap/less/code.less"; 11 | @import "../bower_components/bootstrap/less/grid.less"; 12 | @import "../bower_components/bootstrap/less/tables.less"; 13 | @import "../bower_components/bootstrap/less/forms.less"; 14 | @import "../bower_components/bootstrap/less/buttons.less"; 15 | 16 | // Components 17 | @import "../bower_components/bootstrap/less/component-animations.less"; 18 | @import "../bower_components/bootstrap/less/glyphicons.less"; 19 | @import "../bower_components/bootstrap/less/dropdowns.less"; 20 | @import "../bower_components/bootstrap/less/button-groups.less"; 21 | @import "../bower_components/bootstrap/less/input-groups.less"; 22 | @import "../bower_components/bootstrap/less/navs.less"; 23 | @import "../bower_components/bootstrap/less/navbar.less"; 24 | @import "../bower_components/bootstrap/less/breadcrumbs.less"; 25 | @import "../bower_components/bootstrap/less/pagination.less"; 26 | @import "../bower_components/bootstrap/less/pager.less"; 27 | @import "../bower_components/bootstrap/less/labels.less"; 28 | @import "../bower_components/bootstrap/less/badges.less"; 29 | @import "../bower_components/bootstrap/less/jumbotron.less"; 30 | @import "../bower_components/bootstrap/less/thumbnails.less"; 31 | @import "../bower_components/bootstrap/less/alerts.less"; 32 | @import "../bower_components/bootstrap/less/progress-bars.less"; 33 | @import "../bower_components/bootstrap/less/media.less"; 34 | @import "../bower_components/bootstrap/less/list-group.less"; 35 | @import "../bower_components/bootstrap/less/panels.less"; 36 | @import "../bower_components/bootstrap/less/wells.less"; 37 | @import "../bower_components/bootstrap/less/close.less"; 38 | 39 | // Components w/ JavaScript 40 | @import "../bower_components/bootstrap/less/modals.less"; 41 | @import "../bower_components/bootstrap/less/tooltip.less"; 42 | @import "../bower_components/bootstrap/less/popovers.less"; 43 | @import "../bower_components/bootstrap/less/carousel.less"; 44 | 45 | // Utility classes 46 | @import "../bower_components/bootstrap/less/utilities.less"; 47 | @import "../bower_components/bootstrap/less/responsive-utilities.less"; -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | 9 | // testing framework to use (jasmine/mocha/qunit/...) 10 | frameworks: ['jasmine'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | 'app/bower_components/jquery/jquery.js', 15 | 'app/bower_components/underscore/underscore.js', 16 | 'app/bower_components/angular/angular.js', 17 | 'app/bower_components/angular-route/angular-route.js', 18 | 'app/bower_components/angular-mocks/angular-mocks.js', 19 | 'app/bower_components/angular-resource/angular-resource.js', 20 | 'app/bower_components/angular-cookies/angular-cookies.js', 21 | 'app/bower_components/angular-sanitize/angular-sanitize.js', 22 | 'app/bower_components/angular-ui-sortable/sortable.js', 23 | 'app/bower_components/angular-bootstrap/ui-bootstrap-tpls.js', 24 | 'app/bower_components/malhar-angular-dashboard/dist/angular-ui-dashboard.js', 25 | 'app/bower_components/malhar-angular-table/dist/mlhr-table.js', 26 | 'app/bower_components/malhar-angular-widgets/dist/malhar-angular-widgets.js', 27 | 'app/bower_components/ng-grid/ng-grid-2.0.11.debug.js', 28 | 'app/bower_components/angularjs-nvd3-directives/dist/angularjs-nvd3-directives.js', 29 | 'app/bower_components/pines-notify/jquery.pnotify.js', 30 | 'app/bower_components/angular-pines-notify/src/pnotify.js', 31 | 'app/scripts/*.js', 32 | 'app/scripts/**/*.js', 33 | 'test/mock/**/*.js', 34 | 'test/spec/**/*.js' 35 | ], 36 | 37 | // list of files / patterns to exclude 38 | exclude: [], 39 | 40 | // web server port 41 | port: 8080, 42 | 43 | // level of logging 44 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 45 | logLevel: config.LOG_INFO, 46 | 47 | 48 | // enable / disable watching file and executing tests whenever any file changes 49 | autoWatch: false, 50 | 51 | 52 | // Start these browsers, currently available: 53 | // - Chrome 54 | // - ChromeCanary 55 | // - Firefox 56 | // - Opera 57 | // - Safari (only Mac) 58 | // - PhantomJS 59 | // - IE (only Windows) 60 | browsers: ['PhantomJS'], 61 | 62 | 63 | // Continuous Integration mode 64 | // if true, it capture browsers, run tests and exit 65 | singleRun: false 66 | }); 67 | }; 68 | -------------------------------------------------------------------------------- /app/scripts/controllers/rest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | .controller('RestDataCtrl', function ($scope, settings, RestTimeSeriesDataModel, RestTopNDataModel) { 5 | var widgetDefs = [ 6 | { 7 | name: 'Line Chart Visits', 8 | directive: 'wt-historical-chart', 9 | dataAttrName: 'chart', 10 | dataModelType: RestTimeSeriesDataModel, 11 | dataModelOptions: { 12 | metric: 'count' 13 | }, 14 | style: { 15 | width: '50%' 16 | } 17 | }, 18 | { 19 | name: 'Line Chart Bandwidth', 20 | directive: 'wt-historical-chart', 21 | dataAttrName: 'chart', 22 | dataModelType: RestTimeSeriesDataModel, 23 | dataModelOptions: { 24 | metric: 'bandwidth' 25 | }, 26 | style: { 27 | width: '50%' 28 | } 29 | }, 30 | { 31 | name: 'Countries', 32 | directive: 'wt-top-n', 33 | dataAttrName: 'data', 34 | dataModelType: RestTopNDataModel, 35 | dataModelOptions: { 36 | dimension: 'geoip_country_name', 37 | limit: 20 38 | }, 39 | style: { 40 | width: '33%' 41 | } 42 | }, 43 | { 44 | name: 'Cities', 45 | directive: 'wt-top-n', 46 | dataAttrName: 'data', 47 | dataModelType: RestTopNDataModel, 48 | dataModelOptions: { 49 | dimension: 'geoip_city_name', 50 | limit: 20 51 | }, 52 | style: { 53 | width: '33%' 54 | } 55 | }, 56 | { 57 | name: 'Browsers', 58 | directive: 'wt-top-n', 59 | dataAttrName: 'data', 60 | dataModelType: RestTopNDataModel, 61 | dataModelOptions: { 62 | dimension: 'agentinfo_name', 63 | limit: 20 64 | }, 65 | style: { 66 | width: '33%' 67 | } 68 | } 69 | ]; 70 | 71 | var defaultWidgets = [ 72 | { 73 | name: 'Line Chart Visits', 74 | title: 'Visits' 75 | }, 76 | { 77 | name: 'Line Chart Bandwidth', 78 | title: 'Bandwidth' 79 | }, 80 | { 81 | name: 'Countries', 82 | title: 'Countries' 83 | }, 84 | { 85 | name: 'Cities', 86 | title: 'Cities' 87 | }, 88 | { 89 | name: 'Browsers', 90 | title: 'Browsers' 91 | } 92 | ]; 93 | 94 | $scope.dashboardOptions = { 95 | widgetButtons: true, 96 | widgetDefinitions: widgetDefs, 97 | defaultWidgets: defaultWidgets 98 | }; 99 | }); -------------------------------------------------------------------------------- /app/scripts/services/gateway.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app.service') 4 | .factory('Gateway', function ($rootScope, $q, $http, webSocket, settings) { 5 | return { 6 | getDataTopics: function () { 7 | var deferred = $q.defer(); 8 | 9 | webSocket.subscribe('_latestTopics', function (message) { 10 | var list = _.reject(message, function (topic) { 11 | return topic.indexOf('AppData{\"schema\"') !== 0; 12 | }); 13 | 14 | var topics = _.map(list, function (topic) { 15 | var jsonInd = topic.indexOf('{'); 16 | 17 | var jsonString = topic.substr(jsonInd); 18 | 19 | var topicData = JSON.parse(jsonString); 20 | 21 | return { 22 | topic: topic, 23 | appId: topicData.appId, 24 | name: topicData.topicName, 25 | schema: topicData.schema, 26 | type: topicData.schema.type 27 | }; 28 | }); 29 | 30 | deferred.resolve(topics); 31 | //$rootScope.$apply(); 32 | }, $rootScope); 33 | webSocket.send({ type: 'getLatestTopics' }); 34 | //TODO unsubscribe 35 | 36 | return deferred.promise; 37 | }, 38 | 39 | getRunningApps: function () { 40 | var deferred = $q.defer(); 41 | 42 | var url = settings.restBaseURL + 'applications?states=running&jsonp=JSON_CALLBACK'; 43 | $http.jsonp(url) 44 | .success(function (data) { 45 | if (data && data.apps && data.apps.length > 0) { 46 | var apps = _.reject(data.apps, function (app) { 47 | return app.state !== 'RUNNING'; 48 | }); 49 | apps = _.sortBy(apps, function (app) { 50 | return (-app.startedTime); 51 | }); 52 | 53 | deferred.resolve(apps); 54 | } 55 | }); 56 | 57 | return deferred.promise; 58 | }, 59 | 60 | getTopics: function () { 61 | var deferred = $q.defer(); 62 | 63 | var topicsPromise = this.getDataTopics(); 64 | var appsPromise = this.getRunningApps(); 65 | 66 | $q.all({ topics: topicsPromise, apps: appsPromise }).then(function (resolutions) { 67 | var topics = resolutions.topics; 68 | var apps = resolutions.apps; 69 | 70 | var appIdMap = {}; 71 | _.each(apps, function (app) { 72 | appIdMap[app.id] = app; 73 | }); 74 | 75 | topics = _.reject(topics, function (topic) { 76 | return !appIdMap.hasOwnProperty(topic.appId); 77 | }); 78 | 79 | _.each(topics, function (topic) { 80 | var app = appIdMap[topic.appId]; 81 | topic.appName = app.name; 82 | topic.appStartedTime = app.startedTime; 83 | }); 84 | 85 | topics = _.sortBy(topics, function (topic) { 86 | return topic.name; 87 | }); 88 | 89 | deferred.resolve(topics); 90 | }); 91 | 92 | return deferred.promise; 93 | } 94 | }; 95 | }); 96 | -------------------------------------------------------------------------------- /app/scripts/services/widgets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app.service') 4 | .factory('widgetDefs', function (settings, WebSocketWidgetDataModel, TimeSeriesDataModel, PieChartDataModel) { 5 | return [ 6 | { 7 | name: 'Value', 8 | directive: 'wt-scope-watch', 9 | dataAttrName: 'value', 10 | attrs: { 11 | 'value-class': 'alert-info' 12 | }, 13 | dataTypes: ['percentage', 'simple'], 14 | dataModelType: WebSocketWidgetDataModel, 15 | dataModelOptions: { 16 | defaultTopic: settings.topic.visualdata.piValue 17 | } 18 | }, 19 | { 20 | name: 'Progressbar', 21 | directive: 'progressbar', 22 | attrs: { 23 | class: 'progress-striped', 24 | type: 'success' 25 | }, 26 | dataAttrName: 'value', 27 | dataTypes: ['percentage', 'simple'], 28 | dataModelType: WebSocketWidgetDataModel, 29 | dataModelOptions: { 30 | defaultTopic: settings.topic.visualdata.progress 31 | } 32 | }, 33 | { 34 | name: 'Line Chart', 35 | directive: 'wt-line-chart', 36 | dataAttrName: 'chart', 37 | dataTypes: ['timeseries'], 38 | dataModelType: TimeSeriesDataModel, 39 | dataModelOptions: { 40 | defaultTopic: settings.topic.visualdata.chartValue 41 | }, 42 | style: { 43 | width: '50%' 44 | } 45 | }, 46 | { 47 | name: 'TopN', 48 | directive: 'wt-top-n', 49 | attrs: { 50 | data: 'serverTopTen' 51 | }, 52 | dataAttrName: 'data', 53 | dataTypes: ['topN'], 54 | dataModelType: WebSocketWidgetDataModel, 55 | dataModelOptions: { 56 | defaultTopic: settings.topic.visualdata.topn 57 | } 58 | }, 59 | { 60 | name: 'Pie Chart', 61 | directive: 'wt-pie-chart', 62 | style: { 63 | width: '350px', 64 | height: '350px' 65 | }, 66 | dataAttrName: 'data', 67 | dataTypes: ['piechart'], 68 | dataModelType: PieChartDataModel, 69 | dataModelOptions: { 70 | defaultTopic: settings.topic.visualdata.pieChart 71 | } 72 | }, 73 | { 74 | name: 'Gauge', 75 | directive: 'wt-gauge', 76 | dataAttrName: 'value', 77 | dataTypes: ['percentage', 'simple'], 78 | dataModelType: WebSocketWidgetDataModel, 79 | dataModelOptions: { 80 | defaultTopic: settings.topic.visualdata.percentage 81 | }, 82 | style: { 83 | width: '250px' 84 | } 85 | }, 86 | { 87 | name: 'JSON', 88 | directive: 'wt-json', 89 | dataAttrName: 'value', 90 | dataModelType: WebSocketWidgetDataModel, 91 | dataModelOptions: { 92 | defaultTopic: settings.topic.visualdata.topn 93 | } 94 | }, 95 | { 96 | name: 'WebSocket Debugger', 97 | templateUrl: 'template/topics.html' 98 | } 99 | ]; 100 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | malhar-dashboard-webapp 2 | ================= 3 | 4 | [![Build Status](https://travis-ci.org/DataTorrent/malhar-dashboard-webapp.svg)](https://travis-ci.org/DataTorrent/malhar-dashboard-webapp) 5 | 6 | Dashboard Web Application 7 | 8 | [Online Demo](http://datatorrent.github.io/malhar-dashboard-webapp/#/) 9 | 10 | ![AngularJS Dashboard](docs/AngularJSDashboard.png "AngularJS Dashboard") 11 | 12 | ## Dependencies 13 | 14 | [Dashboard Directive](https://github.com/DataTorrent/malhar-angular-dashboard) 15 | 16 | [Widget Library](https://github.com/DataTorrent/malhar-angular-widgets) 17 | 18 | ![Dependencies](docs/ProjectDependencies.png "Dependencies") 19 | 20 | ## Running Application (minimum dependencies) 21 | 22 | 1. Node.js way 23 | 24 | Install express 25 | 26 | ``` bash 27 | $ npm install express 28 | ``` 29 | Run Node.js server 30 | 31 | ``` bash 32 | $ node app.js 33 | ``` 34 | Application will be available at http://localhost:3000. 35 | 36 | 2. Simple web server way 37 | 38 | Start any web server in "dist" directory, e.g. with Python 39 | ``` bash 40 | $ python -m SimpleHTTPServer 8080 41 | ``` 42 | Application will be available at http://localhost:8080 43 | 44 | In both cases static files (including bundled JS/CSS) will be served from "dist" directory. 45 | 46 | ## Running Application (development mode) 47 | Install dependencies: 48 | 49 | ``` bash 50 | $ npm install 51 | ``` 52 | 53 | Install Bower dependencies: 54 | 55 | ``` bash 56 | $ bower install 57 | ``` 58 | 59 | Run Grunt server task: 60 | 61 | ``` bash 62 | $ grunt server 63 | ``` 64 | 65 | Application will be available at http://localhost:9000 66 | 67 | ## Building Application 68 | 69 | Application is built with Grunt. 70 | 71 | ``` bash 72 | $ npm install -g grunt-cli 73 | $ grunt 74 | ``` 75 | 76 | ## Contributing 77 | 78 | This project welcomes new contributors. 79 | 80 | You acknowledge that your submissions to DataTorrent on this repository are made pursuant the terms of the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html) and constitute "Contributions," as defined therein, and you represent and warrant that you have the right and authority to do so. 81 | 82 | When **adding new javascript files**, please prepend the Apache v2.0 license header, which can be found in [CONTRIBUTING.md file](https://github.com/DataTorrent/malhar-dashboard-webapp/blob/master/CONTRIBUTING.md). 83 | 84 | 85 | ## Links 86 | 87 | [malhar-angular-dashboard](https://github.com/DataTorrent/malhar-angular-dashboard) AngularJS Dashboard directive. 88 | 89 | [malhar-angular-widgets](https://github.com/DataTorrent/malhar-angular-widgets) Widget library. 90 | 91 | [Node.js](http://nodejs.org/) Software platform built on JavaScript runtime 92 | 93 | [AngularJS](http://angularjs.org/) JavaScript framework 94 | 95 | [ui-sortable](https://github.com/angular-ui/ui-sortable) AngularJS UI Sortable 96 | 97 | [jQuery UI Sortable](http://jqueryui.com/sortable/) jQuery UI Sortable plugin (reordering with drag and drop) 98 | 99 | [Bower](http://bower.io/) Package manager for the web 100 | 101 | [Grunt](http://gruntjs.com/) JavaScript Task Runner 102 | 103 | [Yeoman](http://yeoman.io/) Webapp generator 104 | 105 | [DDP](https://github.com/meteor/meteor/blob/master/packages/livedata/DDP.md) Meteor Distributed Data Protocol 106 | 107 | [Meteor-DDP](https://github.com/eddflrs/meteor-ddp) Meteor DDP Javascript client -------------------------------------------------------------------------------- /app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | .controller('MainCtrl', function ($scope, $interval, stackedAreaChartSampleData, pieChartSampleData, RandomTimeSeriesDataModel, RandomTopNDataModel) { 5 | var widgetDefinitions = [ 6 | { 7 | name: 'wt-time', 8 | style: { 9 | width: '33%' 10 | } 11 | }, 12 | { 13 | name: 'wt-random', 14 | style: { 15 | width: '33%' 16 | } 17 | }, 18 | { 19 | name: 'wt-scope-watch', 20 | attrs: { 21 | value: 'randomValue' 22 | }, 23 | style: { 24 | width: '34%' 25 | } 26 | }, 27 | { 28 | name: 'wt-line-chart', 29 | dataAttrName: 'chart', 30 | dataModelType: RandomTimeSeriesDataModel, 31 | style: { 32 | width: '50%' 33 | } 34 | }, 35 | { 36 | name: 'wt-gauge', 37 | attrs: { 38 | value: 'percentage' 39 | }, 40 | style: { 41 | width: '250px' 42 | } 43 | }, 44 | { 45 | name: 'wt-top-n', 46 | dataAttrName: 'data', 47 | dataModelType: RandomTopNDataModel, 48 | style: { 49 | width: '30%' 50 | } 51 | }, 52 | { 53 | name: 'progressbar', 54 | attrs: { 55 | class: 'progress-striped', 56 | type: 'success', 57 | value: 'percentage' 58 | }, 59 | style: { 60 | width: '30%' 61 | } 62 | }, 63 | { 64 | name: 'progressbar2', 65 | template: '
{{percentage}}%
', 66 | style: { 67 | width: '30%' 68 | } 69 | }, 70 | { 71 | name: 'URLtemplate', 72 | templateUrl: 'template/percentage.html' 73 | }, 74 | { 75 | name: 'wt-pie-chart', 76 | style: { 77 | width: '350px', 78 | height: '350px' 79 | }, 80 | attrs: { 81 | data: 'pieChartData' 82 | } 83 | } 84 | ]; 85 | 86 | 87 | var defaultWidgets = _.map(widgetDefinitions, function (widgetDef) { 88 | return { 89 | name: widgetDef.name 90 | }; 91 | }); 92 | 93 | $scope.dashboardOptions = { 94 | widgetButtons: true, 95 | widgetDefinitions: widgetDefinitions, 96 | defaultWidgets: defaultWidgets 97 | }; 98 | 99 | // random scope value (scope-watch widget) 100 | $interval(function () { 101 | $scope.randomValue = Math.random(); 102 | }, 500); 103 | 104 | // percentage (gauge widget, progressbar widget) 105 | $scope.percentage = 5; 106 | $interval(function () { 107 | $scope.percentage = ($scope.percentage + 10) % 100; 108 | }, 1000); 109 | 110 | // nvd3-stacked-area-chart 111 | $scope.stackedAreaChartData = stackedAreaChartSampleData; 112 | 113 | $scope.xAxisTickFormat = function () { 114 | return function (d) { 115 | return d3.time.format('%x')(new Date(d)); 116 | }; 117 | }; 118 | 119 | // pie chart 120 | $scope.pieChartData = pieChartSampleData; 121 | 122 | /* 123 | var pieChart = angular.copy(pieChartSampleData); 124 | 125 | $interval(function () { //TODO 126 | var a = pieChart[0]; 127 | var b = pieChart[1]; 128 | var sum = a.y + b.y; 129 | a.y = (a.y + 1) % sum; 130 | b.y = sum - a.y; 131 | $scope.pieChartData = angular.copy(pieChart); 132 | }, 500); 133 | */ 134 | 135 | // external controls 136 | $scope.addWidget = function (directive) { 137 | $scope.dashboardOptions.addWidget({ 138 | name: directive 139 | }); 140 | }; 141 | 142 | $scope.addWidgetScopeWatch = function () { 143 | $scope.dashboardOptions.addWidget({ 144 | name: 'scope-watch', 145 | attrs: { 146 | value: 'randomValue' 147 | } 148 | }); 149 | }; 150 | }); -------------------------------------------------------------------------------- /app/scripts/controllers/discovery.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | .controller('DiscoveryCtrl', function ($scope, $routeParams, webSocket, Gateway, settings, 5 | widgetDefs, notificationService) { 6 | 7 | $scope.dashboardOptions = { 8 | useLocalStorage: false, //TODO enable by default 9 | widgetButtons: true, 10 | widgetDefinitions: widgetDefs, 11 | settingsModalOptions: { 12 | optionsTemplateUrl: 'template/widgetOptions.html' 13 | } 14 | }; 15 | 16 | Gateway.getTopics().then(function (topics) { 17 | if (!topics.length) { //TODO 18 | return; 19 | } 20 | 21 | topics = _.sortBy(topics, function (topic) { 22 | return (-topic.appStartedTime); 23 | }); 24 | 25 | var appId; 26 | 27 | if ($routeParams.appId) { 28 | appId = $routeParams.appId; 29 | } else { 30 | appId = topics[0].appId; 31 | } 32 | 33 | var selTopic = _.findWhere(topics, { appId: appId }); 34 | 35 | var appTopics = _.where(topics, { appId: appId }); 36 | 37 | var widgets = []; 38 | function addWidget(widget) { 39 | widgets.push(widget); 40 | } 41 | 42 | var gaugeUsed = false; 43 | 44 | _.each(appTopics, function (topic) { 45 | var type = topic.schema.type; 46 | 47 | if (type === 'timeseries') { 48 | addWidget({ 49 | name: 'Line Chart', 50 | title: 'Line Chart', 51 | dataModelOptions: { 52 | topic: topic.topic 53 | } 54 | }); 55 | } else if (type === 'topN') { 56 | addWidget({ 57 | name: 'TopN', 58 | title: 'Top N', 59 | dataModelOptions: { 60 | topic: topic.topic 61 | } 62 | }); 63 | } else if (type === 'percentage') { 64 | if (!gaugeUsed) { 65 | addWidget({ 66 | name: 'Gauge', 67 | title: 'Gauge', 68 | dataModelOptions: { 69 | topic: topic.topic 70 | } 71 | }); 72 | gaugeUsed = true; 73 | } else { 74 | addWidget({ 75 | name: 'Progressbar', 76 | title: 'Progressbar', 77 | dataModelOptions: { 78 | topic: topic.topic 79 | } 80 | }); 81 | } 82 | } else if (type === 'piechart') { 83 | addWidget({ 84 | name: 'Pie Chart', 85 | title: 'Pie Chart', 86 | dataModelOptions: { 87 | topic: topic.topic 88 | } 89 | }); 90 | } 91 | }); 92 | 93 | $scope.dashboardOptions.loadWidgets(widgets); 94 | 95 | notificationService.notify({ 96 | title: 'Dashboard Loaded', 97 | text: 'Dashboard for application {name} ({id}) has been loaded.' 98 | .replace('{name}', selTopic.appName) 99 | .replace('{id}', selTopic.appId), 100 | type: 'success', 101 | delay: 5000, 102 | icon: false, 103 | history: false 104 | }); 105 | }); 106 | 107 | // initialize widgets with default topics 108 | var topicsPromise = Gateway.getTopics(); 109 | 110 | $scope.$on('widgetAdded', function (event, widget) { 111 | event.stopPropagation(); 112 | 113 | if (widget.dataModel && widget.dataModelOptions && widget.dataModelOptions.topic) { 114 | topicsPromise.then(function (topics) { 115 | var selTopic = widget.dataModelOptions.topic; 116 | 117 | var topic = null; 118 | 119 | if (selTopic) { 120 | topic = _.find(topics, function (topic) { 121 | return topic.topic === selTopic; 122 | }); 123 | } else { 124 | var defaultTopic = widget.dataModelOptions.defaultTopic; 125 | 126 | topic = _.find(topics, function (topic) { 127 | return topic.name.indexOf(defaultTopic) >= 0; 128 | }); 129 | } 130 | 131 | if (topic) { 132 | widget.dataModel.update(topic.topic); 133 | } 134 | }); 135 | } 136 | }); 137 | }); -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /app/scripts/vendor/visibly.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * visibly - v0.6 Aug 2011 - Page Visibility API Polyfill 3 | * http://github.com/addyosmani 4 | * Copyright (c) 2011 Addy Osmani 5 | * Dual licensed under the MIT and GPL licenses. 6 | * 7 | * Methods supported: 8 | * visibly.onVisible(callback) 9 | * visibly.onHidden(callback) 10 | * visibly.hidden() 11 | * visibly.visibilityState() 12 | * visibly.visibilitychange(callback(state)); 13 | */ 14 | 15 | ;(function () { 16 | 17 | window.visibly = { 18 | q: document, 19 | p: undefined, 20 | prefixes: ['webkit', 'ms','o','moz','khtml'], 21 | props: ['VisibilityState', 'visibilitychange', 'Hidden'], 22 | m: ['focus', 'blur'], 23 | visibleCallbacks: [], 24 | hiddenCallbacks: [], 25 | genericCallbacks:[], 26 | _callbacks: [], 27 | cachedPrefix:"", 28 | fn:null, 29 | 30 | onVisible: function (_callback) { 31 | if(typeof _callback == 'function' ){ 32 | this.visibleCallbacks.push(_callback); 33 | } 34 | }, 35 | onHidden: function (_callback) { 36 | if(typeof _callback == 'function' ){ 37 | this.hiddenCallbacks.push(_callback); 38 | } 39 | }, 40 | getPrefix:function(){ 41 | if(!this.cachedPrefix){ 42 | for(var l=0;b=this.prefixes[l++];){ 43 | if(b + this.props[2] in this.q){ 44 | this.cachedPrefix = b; 45 | return this.cachedPrefix; 46 | } 47 | } 48 | } 49 | }, 50 | 51 | visibilityState:function(){ 52 | return this._getProp(0); 53 | }, 54 | hidden:function(){ 55 | return this._getProp(2); 56 | }, 57 | visibilitychange:function(fn){ 58 | if(typeof fn == 'function' ){ 59 | this.genericCallbacks.push(fn); 60 | } 61 | 62 | var n = this.genericCallbacks.length; 63 | if(n){ 64 | if(this.cachedPrefix){ 65 | while(n--){ 66 | this.genericCallbacks[n].call(this, this.visibilityState()); 67 | } 68 | }else{ 69 | while(n--){ 70 | this.genericCallbacks[n].call(this, arguments[0]); 71 | } 72 | } 73 | } 74 | 75 | }, 76 | isSupported: function (index) { 77 | return ((this.cachedPrefix + this.props[2]) in this.q); 78 | }, 79 | _getProp:function(index){ 80 | return this.q[this.cachedPrefix + this.props[index]]; 81 | }, 82 | _execute: function (index) { 83 | if (index) { 84 | this._callbacks = (index == 1) ? this.visibleCallbacks : this.hiddenCallbacks; 85 | var n = this._callbacks.length; 86 | while(n--){ 87 | this._callbacks[n](); 88 | } 89 | } 90 | }, 91 | _visible: function () { 92 | window.visibly._execute(1); 93 | window.visibly.visibilitychange.call(window.visibly, 'visible'); 94 | }, 95 | _hidden: function () { 96 | window.visibly._execute(2); 97 | window.visibly.visibilitychange.call(window.visibly, 'hidden'); 98 | }, 99 | _nativeSwitch: function () { 100 | this[this._getProp(2) ? '_hidden' : '_visible'](); 101 | }, 102 | _listen: function () { 103 | try { /*if no native page visibility support found..*/ 104 | if (!(this.isSupported())) { 105 | if (this.q.addEventListener) { /*for browsers without focusin/out support eg. firefox, opera use focus/blur*/ 106 | window.addEventListener(this.m[0], this._visible, 1); 107 | window.addEventListener(this.m[1], this._hidden, 1); 108 | } else { /*IE <10s most reliable focus events are onfocusin/onfocusout*/ 109 | if (this.q.attachEvent) { 110 | this.q.attachEvent('onfocusin', this._visible); 111 | this.q.attachEvent('onfocusout', this._hidden); 112 | } 113 | } 114 | } else { /*switch support based on prefix detected earlier*/ 115 | this.q.addEventListener(this.cachedPrefix + this.props[1], function () { 116 | window.visibly._nativeSwitch.apply(window.visibly, arguments); 117 | }, 1); 118 | } 119 | } catch (e) {} 120 | }, 121 | init: function () { 122 | this.getPrefix(); 123 | this._listen(); 124 | } 125 | }; 126 | 127 | this.visibly.init(); 128 | })(); 129 | -------------------------------------------------------------------------------- /dist/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | -------------------------------------------------------------------------------- /app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /app/scripts/services/service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app.service') 4 | .constant('pieChartSampleData', [ 5 | { 6 | key: 'One', 7 | y: 5 8 | }, 9 | { 10 | key: 'Two', 11 | y: 2 12 | }, 13 | { 14 | key: 'Three', 15 | y: 9 16 | }, 17 | { 18 | key: 'Four', 19 | y: 7 20 | }, 21 | { 22 | key: 'Five', 23 | y: 4 24 | }, 25 | { 26 | key: 'Six', 27 | y: 3 28 | }, 29 | { 30 | key: 'Seven', 31 | y: 7 32 | }, 33 | { 34 | key: 'Eight', 35 | y: 4 36 | }, 37 | { 38 | key: 'Nine', 39 | y: 3 40 | } 41 | ]) 42 | .constant('stackedAreaChartSampleData', [ 43 | { 44 | key: 'Series 1', 45 | values: [ 46 | [ 1051675200000 , 0] , 47 | [ 1054353600000 , 7.2481659343222] , 48 | [ 1056945600000 , 9.2512381306992] , 49 | [ 1059624000000 , 11.341210982529] , 50 | [ 1062302400000 , 14.734820409020] , 51 | [ 1064894400000 , 12.387148007542] , 52 | [ 1067576400000 , 18.436471461827] , 53 | [ 1070168400000 , 19.830742266977] , 54 | [ 1072846800000 , 22.643205829887] , 55 | [ 1075525200000 , 26.743156781239] , 56 | [ 1078030800000 , 29.597478802228] , 57 | [ 1080709200000 , 30.831697585341] , 58 | [ 1083297600000 , 28.054068024708] , 59 | [ 1085976000000 , 29.294079423832] , 60 | [ 1088568000000 , 30.269264061274] , 61 | [ 1091246400000 , 24.934526898906] , 62 | [ 1093924800000 , 24.265982759406] , 63 | [ 1096516800000 , 27.217794897473] , 64 | [ 1099195200000 , 30.802601992077] , 65 | [ 1101790800000 , 36.331003758254] , 66 | [ 1104469200000 , 43.142498700060] , 67 | [ 1107147600000 , 40.558263931958] , 68 | [ 1109566800000 , 42.543622385800] , 69 | [ 1112245200000 , 41.683584710331] , 70 | [ 1114833600000 , 36.375367302328] , 71 | [ 1117512000000 , 40.719688980730] , 72 | [ 1120104000000 , 43.897963036919] , 73 | [ 1122782400000 , 49.797033975368] , 74 | [ 1125460800000 , 47.085993935989] , 75 | [ 1128052800000 , 46.601972859745] , 76 | [ 1130734800000 , 41.567784572762] , 77 | [ 1133326800000 , 47.296923737245] , 78 | [ 1136005200000 , 47.642969612080] , 79 | [ 1138683600000 , 50.781515820954] , 80 | [ 1141102800000 , 52.600229204305] , 81 | [ 1143781200000 , 55.599684490628] , 82 | [ 1146369600000 , 57.920388436633] , 83 | [ 1149048000000 , 53.503593218971] , 84 | [ 1151640000000 , 53.522973979964] , 85 | [ 1154318400000 , 49.846822298548] , 86 | [ 1156996800000 , 54.721341614650] , 87 | [ 1159588800000 , 58.186236223191] , 88 | [ 1162270800000 , 63.908065540997] , 89 | [ 1164862800000 , 69.767285129367] , 90 | [ 1167541200000 , 72.534013373592] , 91 | [ 1170219600000 , 77.991819436573] , 92 | [ 1172638800000 , 78.143584404990] , 93 | [ 1175313600000 , 83.702398665233] , 94 | [ 1177905600000 , 91.140859312418] , 95 | [ 1180584000000 , 98.590960607028] , 96 | [ 1183176000000 , 96.245634754228] , 97 | [ 1185854400000 , 92.326364432615] , 98 | [ 1188532800000 , 97.068765332230] , 99 | [ 1191124800000 , 105.81025556260] , 100 | [ 1193803200000 , 114.38348777791] , 101 | [ 1196398800000 , 103.59604949810] , 102 | [ 1199077200000 , 101.72488429307] , 103 | [ 1201755600000 , 89.840147735028] , 104 | [ 1204261200000 , 86.963597532664] , 105 | [ 1206936000000 , 84.075505208491] , 106 | [ 1209528000000 , 93.170105645831] , 107 | [ 1212206400000 , 103.62838083121] , 108 | [ 1214798400000 , 87.458241365091] , 109 | [ 1217476800000 , 85.808374141319] , 110 | [ 1220155200000 , 93.158054469193] , 111 | [ 1222747200000 , 65.973252382360] , 112 | [ 1225425600000 , 44.580686638224] , 113 | [ 1228021200000 , 36.418977140128] , 114 | [ 1230699600000 , 38.727678144761] , 115 | [ 1233378000000 , 36.692674173387] , 116 | [ 1235797200000 , 30.033022809480] , 117 | [ 1238472000000 , 36.707532162718] , 118 | [ 1241064000000 , 52.191457688389] , 119 | [ 1243742400000 , 56.357883979735] , 120 | [ 1246334400000 , 57.629002180305] , 121 | [ 1249012800000 , 66.650985790166] , 122 | [ 1251691200000 , 70.839243432186] , 123 | [ 1254283200000 , 78.731998491499] , 124 | [ 1256961600000 , 72.375528540349] , 125 | [ 1259557200000 , 81.738387881630] , 126 | [ 1262235600000 , 87.539792394232] , 127 | [ 1264914000000 , 84.320762662273] , 128 | [ 1267333200000 , 90.621278391889] , 129 | [ 1270008000000 , 102.47144881651] , 130 | [ 1272600000000 , 102.79320353429] , 131 | [ 1275278400000 , 90.529736050479] , 132 | [ 1277870400000 , 76.580859994531] , 133 | [ 1280548800000 , 86.548979376972] , 134 | [ 1283227200000 , 81.879653334089] , 135 | [ 1285819200000 , 101.72550015956] , 136 | [ 1288497600000 , 107.97964852260] , 137 | [ 1291093200000 , 106.16240630785] , 138 | [ 1293771600000 , 114.84268599533] , 139 | [ 1296450000000 , 121.60793322282] , 140 | [ 1298869200000 , 133.41437346605] , 141 | [ 1301544000000 , 125.46646042904] , 142 | [ 1304136000000 , 129.76784954301] , 143 | [ 1306814400000 , 128.15798861044] , 144 | [ 1309406400000 , 121.92388706072] , 145 | [ 1312084800000 , 116.70036100870] , 146 | [ 1314763200000 , 88.367701837033] , 147 | [ 1317355200000 , 59.159665765725] , 148 | [ 1320033600000 , 79.793568139753] , 149 | [ 1322629200000 , 75.903834028417] , 150 | [ 1325307600000 , 72.704218209157] , 151 | [ 1327986000000 , 84.936990804097] , 152 | [ 1330491600000 , 93.388148670744] 153 | ] 154 | } 155 | ]); 156 | -------------------------------------------------------------------------------- /app/scripts/vendor/meteor-ddp.js: -------------------------------------------------------------------------------- 1 | /* MeteorDdp - a client for DDP version pre1 */ 2 | /* Copied from https://github.com/eddflrs/meteor-ddp and wrapped into AngularJS factory */ 3 | 4 | angular.module('app.service').factory('MeteorDdp', function () { 5 | 6 | var MeteorDdp = function(wsUri) { 7 | this.VERSIONS = ["pre1"]; 8 | 9 | this.wsUri = wsUri; 10 | this.sock; 11 | this.defs = {}; // { deferred_id => deferred_object } 12 | this.subs = {}; // { pub_name => deferred_id } 13 | this.watchers = {}; // { coll_name => [cb1, cb2, ...] } 14 | this.collections = {}; // { coll_name => {docId => {doc}, docId => {doc}, ...} } 15 | }; 16 | 17 | MeteorDdp.prototype._Ids = function() { 18 | var count = 0; 19 | return { 20 | next: function() { 21 | return ++count + ''; 22 | } 23 | } 24 | }(); 25 | 26 | MeteorDdp.prototype.connect = function() { 27 | var self = this; 28 | var conn = new $.Deferred(); 29 | 30 | self.sock = new WebSocket(self.wsUri); 31 | 32 | self.sock.onopen = function() { 33 | self.send({ 34 | msg: 'connect', 35 | version: self.VERSIONS[0], 36 | support: self.VERSIONS 37 | }); 38 | }; 39 | 40 | self.sock.onerror = function(err) { 41 | conn.reject(err); 42 | }; 43 | 44 | self.sock.onmessage = function(msg) { 45 | var data = JSON.parse(msg.data); 46 | 47 | console.log(msg); 48 | 49 | switch (data.msg) { 50 | case 'connected': 51 | conn.resolve(data); 52 | break; 53 | case 'result': 54 | self._resolveCall(data); 55 | break; 56 | case 'updated': 57 | // TODO method call was acked 58 | break; 59 | case 'changed': 60 | self._changeDoc(data); 61 | break; 62 | case 'added': 63 | self._addDoc(data); 64 | break; 65 | case 'removed': 66 | self._removeDoc(data); 67 | break; 68 | case 'ready': 69 | self._resolveSubs(data); 70 | break; 71 | case 'nosub': 72 | self._resolveNoSub(data); 73 | break; 74 | case 'addedBefore': 75 | self._addDoc(data); 76 | break; 77 | case 'movedBefore': 78 | // TODO 79 | break; 80 | } 81 | }; 82 | return conn.promise(); 83 | }; 84 | 85 | MeteorDdp.prototype._resolveNoSub = function(data) { 86 | if (data.error) { 87 | var error = data.error; 88 | this.defs[data.id].reject(error.reason || 'Subscription not found'); 89 | } else { 90 | this.defs[data.id].resolve(); 91 | } 92 | }; 93 | 94 | MeteorDdp.prototype._resolveCall = function(data) { 95 | if (data.error) { 96 | this.defs[data.id].reject(data.error.reason); 97 | } else if (typeof data.result !== 'undefined') { 98 | this.defs[data.id].resolve(data.result); 99 | } 100 | }; 101 | 102 | MeteorDdp.prototype._resolveSubs = function(data) { 103 | var subIds = data.subs; 104 | for (var i = 0; i < subIds.length; i++) { 105 | this.defs[subIds[i]].resolve(); 106 | } 107 | }; 108 | 109 | MeteorDdp.prototype._changeDoc = function(msg) { 110 | var collName = msg.collection; 111 | var id = msg.id; 112 | var fields = msg.fields; 113 | var cleared = msg.cleared; 114 | var coll = this.collections[collName]; 115 | 116 | if (fields) { 117 | for (var k in fields) { 118 | coll[id][k] = fields[k]; 119 | } 120 | } else if (cleared) { 121 | for (var i = 0; i < cleared.length; i++) { 122 | var fieldName = cleared[i]; 123 | delete coll[id][fieldName]; 124 | } 125 | } 126 | 127 | var changedDoc = coll[id]; 128 | this._notifyWatchers(collName, changedDoc, id, msg.msg); 129 | }; 130 | 131 | MeteorDdp.prototype._addDoc = function(msg) { 132 | var collName = msg.collection; 133 | var id = msg.id; 134 | if (!this.collections[collName]) { 135 | this.collections[collName] = {}; 136 | } 137 | /* NOTE: Ordered docs will have a 'before' field containing the id of 138 | * the doc after it. If it is the last doc, it will be null. 139 | */ 140 | this.collections[collName][id] = msg.fields; 141 | 142 | var changedDoc = this.collections[collName][id]; 143 | this._notifyWatchers(collName, changedDoc, id, msg.msg); 144 | }; 145 | 146 | MeteorDdp.prototype._removeDoc = function(msg) { 147 | var collName = msg.collection; 148 | var id = msg.id; 149 | var doc = this.collections[collName][id]; 150 | 151 | var docCopy = JSON.parse(JSON.stringify(doc)); 152 | delete this.collections[collName][id]; 153 | this._notifyWatchers(collName, docCopy, id, msg.msg); 154 | }; 155 | 156 | MeteorDdp.prototype._notifyWatchers = function(collName, changedDoc, docId, message) { 157 | changedDoc = JSON.parse(JSON.stringify(changedDoc)); // make a copy 158 | changedDoc._id = docId; // id might be useful to watchers, attach it. 159 | 160 | if (!this.watchers[collName]) { 161 | this.watchers[collName] = []; 162 | } else { 163 | for (var i = 0; i < this.watchers[collName].length; i++) { 164 | this.watchers[collName][i](changedDoc, message); 165 | } 166 | } 167 | }; 168 | 169 | MeteorDdp.prototype._deferredSend = function(actionType, name, params) { 170 | var id = this._Ids.next(); 171 | this.defs[id] = new $.Deferred(); 172 | 173 | var args = params || []; 174 | 175 | var o = { 176 | msg: actionType, 177 | params: args, 178 | id: id 179 | }; 180 | 181 | if (actionType === 'method') { 182 | o.method = name; 183 | } else if (actionType === 'sub') { 184 | o.name = name; 185 | this.subs[name] = id; 186 | } 187 | 188 | this.send(o); 189 | return this.defs[id].promise(); 190 | }; 191 | 192 | MeteorDdp.prototype.call = function(methodName, params) { 193 | return this._deferredSend('method', methodName, params); 194 | }; 195 | 196 | MeteorDdp.prototype.subscribe = function(pubName, params) { 197 | return this._deferredSend('sub', pubName, params); 198 | }; 199 | 200 | MeteorDdp.prototype.unsubscribe = function(pubName) { 201 | this.defs[id] = new $.Deferred(); 202 | if (!this.subs[pubName]) { 203 | this.defs[id].reject(pubName + " was never subscribed"); 204 | } else { 205 | var id = this.subs[pubName]; 206 | var o = { 207 | msg: 'unsub', 208 | id: id 209 | }; 210 | this.send(o); 211 | } 212 | return this.defs[id].promise(); 213 | }; 214 | 215 | MeteorDdp.prototype.watch = function(collectionName, cb) { 216 | if (!this.watchers[collectionName]) { 217 | this.watchers[collectionName] = []; 218 | } 219 | this.watchers[collectionName].push(cb); 220 | }; 221 | 222 | MeteorDdp.prototype.getCollection = function(collectionName) { 223 | return this.collections[collectionName] || null; 224 | } 225 | 226 | MeteorDdp.prototype.getDocument = function(collectionName, docId) { 227 | return this.collections[collectionName][docId] || null; 228 | } 229 | 230 | MeteorDdp.prototype.send = function(msg) { 231 | console.log('send ' + JSON.stringify(msg)); 232 | this.sock.send(JSON.stringify(msg)); 233 | }; 234 | 235 | MeteorDdp.prototype.close = function() { 236 | this.sock.close(); 237 | }; 238 | 239 | return MeteorDdp; 240 | 241 | }); -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2014-01-06 using generator-angular 0.5.1 2 | 'use strict'; 3 | 4 | // # Globbing 5 | // for performance reasons we're only matching one level down: 6 | // 'test/spec/{,*/}*.js' 7 | // use this if you want to recursively match all subfolders: 8 | // 'test/spec/**/*.js' 9 | 10 | module.exports = function (grunt) { 11 | require('load-grunt-tasks')(grunt); 12 | require('time-grunt')(grunt); 13 | 14 | grunt.initConfig({ 15 | yeoman: { 16 | // configurable paths 17 | app: require('./bower.json').appPath || 'app', 18 | dist: 'dist' 19 | }, 20 | watch: { 21 | coffee: { 22 | files: ['<%= yeoman.app %>/scripts/{,*/}*.coffee'], 23 | tasks: ['coffee:dist'] 24 | }, 25 | coffeeTest: { 26 | files: ['test/spec/{,*/}*.coffee'], 27 | tasks: ['coffee:test'] 28 | }, 29 | less: { 30 | files: ['<%= yeoman.app %>/styles/{,*/}*.less'], 31 | tasks: ['concat:less','less:development'] 32 | }, 33 | livereload: { 34 | options: { 35 | livereload: '<%= connect.options.livereload %>' 36 | }, 37 | files: [ 38 | '<%= yeoman.app %>/{,*/}*.html', 39 | '.tmp/styles/{,*/}*.css', 40 | '{.tmp,<%= yeoman.app %>}/scripts/**/*.js', 41 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 42 | '<%= yeoman.app %>/bower_components/malhar-angular-dashboard/dist/{,*/}*.js', 43 | '<%= yeoman.app %>/bower_components/malhar-angular-dashboard/dist/{,*/}*.css', 44 | '<%= yeoman.app %>/bower_components/malhar-angular-widgets/dist/{,*/}*.js' 45 | ] 46 | } 47 | }, 48 | autoprefixer: { 49 | options: ['last 1 version'], 50 | dist: { 51 | files: [{ 52 | expand: true, 53 | cwd: '.tmp/styles/', 54 | src: '{,*/}*.css', 55 | dest: '.tmp/styles/' 56 | }] 57 | } 58 | }, 59 | connect: { 60 | options: { 61 | port: 9000, 62 | // Change this to '0.0.0.0' to access the server from outside. 63 | hostname: 'localhost', 64 | livereload: 35729 65 | }, 66 | livereload: { 67 | options: { 68 | open: true, 69 | base: [ 70 | '.tmp', 71 | '<%= yeoman.app %>' 72 | ] 73 | } 74 | }, 75 | test: { 76 | options: { 77 | port: 9000, 78 | base: [ 79 | '.tmp', 80 | 'test', 81 | '<%= yeoman.app %>' 82 | ] 83 | } 84 | }, 85 | dist: { 86 | options: { 87 | base: '<%= yeoman.dist %>' 88 | } 89 | } 90 | }, 91 | clean: { 92 | dist: { 93 | files: [{ 94 | dot: true, 95 | src: [ 96 | '.tmp', 97 | '<%= yeoman.dist %>/*', 98 | '!<%= yeoman.dist %>/.git*' 99 | ] 100 | }] 101 | }, 102 | server: '.tmp', 103 | distBower: 'dist/bower_components' 104 | }, 105 | jshint: { 106 | options: { 107 | jshintrc: '.jshintrc', 108 | ignores: [ 109 | '<%= yeoman.app %>/scripts/vendor/{,*/}*.js' 110 | ] 111 | }, 112 | all: [ 113 | 'Gruntfile.js', 114 | '<%= yeoman.app %>/scripts/{,*/}*.js' 115 | ] 116 | }, 117 | coffee: { 118 | options: { 119 | sourceMap: true, 120 | sourceRoot: '' 121 | }, 122 | dist: { 123 | files: [{ 124 | expand: true, 125 | cwd: '<%= yeoman.app %>/scripts', 126 | src: '{,*/}*.coffee', 127 | dest: '.tmp/scripts', 128 | ext: '.js' 129 | }] 130 | }, 131 | test: { 132 | files: [{ 133 | expand: true, 134 | cwd: 'test/spec', 135 | src: '{,*/}*.coffee', 136 | dest: '.tmp/spec', 137 | ext: '.js' 138 | }] 139 | } 140 | }, 141 | // not used since Uglify task does concat, 142 | // but still available if needed 143 | concat: { 144 | less: { 145 | src: ['<%= yeoman.app %>/styles/themes/<%= less.theme %>.less','<%= yeoman.app %>/styles/main.less'], 146 | dest: '.tmp/styles/main.less' 147 | } 148 | }, 149 | rev: { 150 | dist: { 151 | files: { 152 | src: [ 153 | '<%= yeoman.dist %>/scripts/{,*/}*.js', 154 | '<%= yeoman.dist %>/styles/{,*/}*.css', 155 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 156 | '<%= yeoman.dist %>/styles/fonts/*' 157 | ] 158 | } 159 | } 160 | }, 161 | useminPrepare: { 162 | html: '<%= yeoman.app %>/index.html', 163 | options: { 164 | dest: '<%= yeoman.dist %>' 165 | } 166 | }, 167 | usemin: { 168 | html: ['<%= yeoman.dist %>/index.html'], 169 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 170 | options: { 171 | dirs: ['<%= yeoman.dist %>'] 172 | } 173 | }, 174 | svgmin: { 175 | dist: { 176 | files: [{ 177 | expand: true, 178 | cwd: '<%= yeoman.app %>/images', 179 | src: '{,*/}*.svg', 180 | dest: '<%= yeoman.dist %>/images' 181 | }] 182 | } 183 | }, 184 | cssmin: { 185 | // By default, your `index.html` will take care of 186 | // minification. This option is pre-configured if you do not wish to use 187 | // Usemin blocks. 188 | // dist: { 189 | // files: { 190 | // '<%= yeoman.dist %>/styles/main.css': [ 191 | // '.tmp/styles/{,*/}*.css', 192 | // '<%= yeoman.app %>/styles/{,*/}*.css' 193 | // ] 194 | // } 195 | // } 196 | }, 197 | htmlmin: { 198 | dist: { 199 | options: { 200 | /*removeCommentsFromCDATA: true, 201 | // https://github.com/yeoman/grunt-usemin/issues/44 202 | //collapseWhitespace: true, 203 | collapseBooleanAttributes: true, 204 | removeAttributeQuotes: true, 205 | removeRedundantAttributes: true, 206 | useShortDoctype: true, 207 | removeEmptyAttributes: true, 208 | removeOptionalTags: true*/ 209 | }, 210 | files: [{ 211 | expand: true, 212 | cwd: '<%= yeoman.app %>', 213 | src: ['*.html', 'views/*.html'], 214 | dest: '<%= yeoman.dist %>' 215 | }] 216 | } 217 | }, 218 | // Put files not handled in other tasks here 219 | copy: { 220 | dist: { 221 | files: [{ 222 | expand: true, 223 | dot: true, 224 | cwd: '<%= yeoman.app %>', 225 | dest: '<%= yeoman.dist %>', 226 | src: [ 227 | '*.{ico,png,txt}', 228 | '.htaccess', 229 | 'bower_components/**/*', 230 | 'images/{,*/}*.{gif,webp}', 231 | 'styles/fonts/*' 232 | ] 233 | }, { 234 | expand: true, 235 | cwd: '.tmp/images', 236 | dest: '<%= yeoman.dist %>/images', 237 | src: [ 238 | 'generated/*' 239 | ] 240 | }, { 241 | expand: true, 242 | cwd: '.tmp/styles', 243 | dest: '<%= yeoman.dist %>/styles', 244 | src: '{,*/}*.css' 245 | }, { 246 | expand: true, 247 | cwd: '<%= yeoman.app %>/fonts', 248 | dest: '<%= yeoman.dist %>/fonts', 249 | src: '**/*.*' 250 | }, { 251 | expand: true, 252 | cwd: '<%= yeoman.app %>/scripts', 253 | dest: '<%= yeoman.dist %>/scripts', 254 | src: '**/*.html' 255 | }, { 256 | expand: true, 257 | cwd: '<%= yeoman.app %>/template', 258 | dest: '<%= yeoman.dist %>/template', 259 | src: '**/*.html' 260 | }, { 261 | expand: true, 262 | cwd: '<%= yeoman.app %>', 263 | dest: '<%= yeoman.dist %>', 264 | src: 'settings.js' 265 | }] 266 | }, 267 | styles: { 268 | expand: true, 269 | cwd: '<%= yeoman.app %>/styles', 270 | dest: '.tmp/styles/', 271 | src: '{,*/}*.css' 272 | } 273 | }, 274 | concurrent: { 275 | server: [ 276 | 'coffee:dist', 277 | 'copy:styles', 278 | 'concat:less' 279 | ], 280 | test: [ 281 | 'coffee', 282 | 'copy:styles' 283 | ], 284 | dist: [ 285 | 'coffee', 286 | 'copy:styles', 287 | 'svgmin', 288 | 'htmlmin' 289 | ] 290 | }, 291 | karma: { 292 | unit: { 293 | configFile: 'karma.conf.js', 294 | singleRun: true 295 | } 296 | }, 297 | cdnify: { 298 | dist: { 299 | html: ['<%= yeoman.dist %>/*.html'] 300 | } 301 | }, 302 | ngmin: { 303 | dist: { 304 | files: [{ 305 | expand: true, 306 | cwd: '<%= yeoman.dist %>/scripts', 307 | src: '*.js', 308 | dest: '<%= yeoman.dist %>/scripts' 309 | }] 310 | } 311 | }, 312 | uglify: { 313 | dist: { 314 | files: { 315 | '<%= yeoman.dist %>/scripts/scripts.js': [ 316 | '<%= yeoman.dist %>/scripts/scripts.js' 317 | ] 318 | } 319 | } 320 | }, 321 | less: { 322 | theme: 'default', 323 | local: { 324 | options: { 325 | paths: ['<%= yeoman.app %>/styles'], 326 | sourceMap: true 327 | }, 328 | files: { 329 | '<%= yeoman.app %>/styles/main.css': ['.tmp/styles/main.less'] 330 | } 331 | }, 332 | development: { 333 | options: { 334 | paths: ['<%= yeoman.app %>/styles'], 335 | sourceMap: true 336 | }, 337 | files: { 338 | '.tmp/styles/main.css': ['.tmp/styles/main.less'] 339 | } 340 | }, 341 | production: { 342 | options: { 343 | paths: ['<%= yeoman.app %>/styles'] 344 | }, 345 | files: { 346 | '.tmp/styles/main.css': ['.tmp/styles/main.less'] 347 | } 348 | } 349 | } 350 | }); 351 | 352 | grunt.registerTask('server', function (target) { 353 | if (target === 'dist') { 354 | return grunt.task.run(['build', 'connect:dist:keepalive']); 355 | } 356 | 357 | grunt.task.run([ 358 | 'clean:server', 359 | 'concurrent:server', 360 | 'less:development', 361 | 'autoprefixer', 362 | 'connect:livereload', 363 | 'watch' 364 | ]); 365 | }); 366 | 367 | grunt.registerTask('run', ['connect:dist:keepalive']); 368 | 369 | grunt.registerTask('test', [ 370 | 'clean:server', 371 | 'concurrent:test', 372 | 'autoprefixer', 373 | 'connect:test', 374 | 'karma' 375 | ]); 376 | 377 | grunt.registerTask('build', [ 378 | 'clean:dist', 379 | 'useminPrepare', 380 | 'concurrent:dist', 381 | 'concat', 382 | 'less:production', 383 | 'autoprefixer', 384 | 'copy:dist', 385 | 'cdnify', 386 | 'ngmin', 387 | 'cssmin', 388 | 'uglify', 389 | 'rev', 390 | 'usemin', 391 | 'clean:distBower' 392 | ]); 393 | 394 | grunt.registerTask('default', [ 395 | 'jshint', 396 | 'test', 397 | 'build' 398 | ]); 399 | }; 400 | -------------------------------------------------------------------------------- /app/scripts/services/datamodel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app.service', []); 4 | 5 | angular.module('app.service') 6 | .factory('RestTimeSeriesDataModel', function (settings, WidgetDataModel, $http) { 7 | function RestTimeSeriesDataModel() { 8 | } 9 | 10 | RestTimeSeriesDataModel.prototype = Object.create(WidgetDataModel.prototype); 11 | 12 | RestTimeSeriesDataModel.prototype.init = function () { 13 | WidgetDataModel.prototype.init.call(this); 14 | this.mode = this.dataModelOptions ? this.dataModelOptions.mode : 'MINUTES'; 15 | 16 | this.widgetScope.$on('modeChanged', function (event, mode) { 17 | this.mode = mode; 18 | this.load(); 19 | }.bind(this)); 20 | 21 | this.load(); 22 | }; 23 | 24 | RestTimeSeriesDataModel.prototype.load = function () { 25 | var params = { 26 | bucket: this.mode, 27 | metric: this.dataModelOptions.metric 28 | }; 29 | 30 | $http.get('/data', { 31 | params: params 32 | }).success(function (data) { 33 | var chart = { 34 | data: data, 35 | chartOptions: { 36 | vAxis: {} 37 | } 38 | }; 39 | 40 | this.updateScope(chart); 41 | }.bind(this)); 42 | }; 43 | 44 | return RestTimeSeriesDataModel; 45 | }) 46 | .factory('RestTopNDataModel', function (settings, WidgetDataModel, $http) { 47 | function RestTopNDataModel() { 48 | } 49 | 50 | RestTopNDataModel.prototype = Object.create(WidgetDataModel.prototype); 51 | 52 | RestTopNDataModel.prototype.init = function () { 53 | WidgetDataModel.prototype.init.call(this); 54 | 55 | this.load(); 56 | }; 57 | 58 | RestTopNDataModel.prototype.load = function () { 59 | $http.get('/topn', { 60 | params: { 61 | limit: this.dataModelOptions.limit, 62 | dimension: this.dataModelOptions.dimension 63 | } 64 | }).success(function (data) { 65 | this.updateScope(data); 66 | }.bind(this)); 67 | }; 68 | 69 | return RestTopNDataModel; 70 | }) 71 | .factory('MeteorTimeSeriesDataModel', function (settings, MeteorDdp, WidgetDataModel) { 72 | function MeteorTimeSeriesDataModel() { 73 | var ddp = new MeteorDdp(settings.meteorURL); //TODO 74 | this.ddp = ddp; 75 | 76 | var that = this; 77 | 78 | ddp.connect().done(function() { 79 | console.log('Meteor connected'); 80 | that.update(); 81 | }); 82 | } 83 | 84 | MeteorTimeSeriesDataModel.prototype = Object.create(WidgetDataModel.prototype); 85 | 86 | MeteorTimeSeriesDataModel.prototype.init = function () { 87 | WidgetDataModel.prototype.init.call(this); 88 | }; 89 | 90 | //TODO 91 | MeteorTimeSeriesDataModel.prototype.update = function (collection) { 92 | this.items = []; 93 | collection = collection ? collection : this.dataModelOptions.collection; 94 | 95 | this.ddp.subscribe(collection); //TODO 96 | 97 | var that = this; 98 | 99 | this.ddp.watch(collection, function(doc, msg) { 100 | if (msg === 'added') { 101 | that.updateScope(doc); 102 | that.widgetScope.$apply(); 103 | } 104 | }); 105 | }; 106 | 107 | MeteorTimeSeriesDataModel.prototype.updateScope = function (value) { 108 | if (value.hasOwnProperty('history')) { 109 | //console.log(_.pluck(value.history, 'timestamp')); 110 | this.items.push.apply(this.items, value.history); 111 | } else { 112 | this.items.push(value); 113 | } 114 | 115 | if (this.items.length > 100) { //TODO 116 | this.items.splice(0, this.items.length - 100); 117 | } 118 | 119 | var chart = { 120 | data: this.items, 121 | max: 30 122 | }; 123 | 124 | WidgetDataModel.prototype.updateScope.call(this, chart); 125 | this.data = []; 126 | }; 127 | 128 | return MeteorTimeSeriesDataModel; 129 | }) 130 | .factory('MeteorDataModel', function (settings, MeteorDdp, WidgetDataModel) { 131 | function MeteorTimeSeriesDataModel() { 132 | var ddp = new MeteorDdp(settings.meteorURL); //TODO 133 | this.ddp = ddp; 134 | 135 | var that = this; 136 | 137 | ddp.connect().done(function() { 138 | console.log('Meteor connected'); 139 | that.update(); 140 | }); 141 | } 142 | 143 | MeteorTimeSeriesDataModel.prototype = Object.create(WidgetDataModel.prototype); 144 | 145 | MeteorTimeSeriesDataModel.prototype.init = function () { 146 | WidgetDataModel.prototype.init.call(this); 147 | }; 148 | 149 | //TODO 150 | MeteorTimeSeriesDataModel.prototype.update = function (collection) { 151 | this.items = []; 152 | collection = collection ? collection : this.dataModelOptions.collection; 153 | 154 | this.ddp.subscribe(collection); //TODO get whole collection instead of 'added' events 155 | 156 | var that = this; 157 | 158 | this.ddp.watch(collection, function(value) { 159 | //console.log(value); 160 | that.updateScope(value); 161 | that.widgetScope.$apply(); 162 | }); 163 | }; 164 | 165 | return MeteorTimeSeriesDataModel; 166 | }) 167 | .factory('WebSocketWidgetDataModel', function (WidgetDataModel, webSocket) { 168 | function WebSocketDataModel() { 169 | } 170 | 171 | WebSocketDataModel.prototype = Object.create(WidgetDataModel.prototype); 172 | 173 | WebSocketDataModel.prototype.init = function () { 174 | this.topic = null; 175 | this.callback = null; 176 | if (this.dataModelOptions && this.dataModelOptions.defaultTopic) { 177 | this.update(this.dataModelOptions.defaultTopic); 178 | } 179 | }; 180 | 181 | WebSocketDataModel.prototype.update = function (newTopic) { 182 | var that = this; 183 | 184 | if (this.topic && this.callback) { 185 | webSocket.unsubscribe(this.topic, this.callback); 186 | } 187 | 188 | var callback = function (message) { 189 | that.updateScope(message); 190 | that.widgetScope.$apply(); 191 | }; 192 | 193 | this.topic = newTopic; 194 | this.callback = webSocket.subscribe(this.topic, callback, this.widgetScope); 195 | }; 196 | 197 | WebSocketDataModel.prototype.destroy = function () { 198 | WidgetDataModel.prototype.destroy.call(this); 199 | 200 | if (this.topic && this.callback) { 201 | webSocket.unsubscribe(this.topic, this.callback); 202 | } 203 | }; 204 | 205 | return WebSocketDataModel; 206 | }) 207 | .factory('RandomValueDataModel', function (WidgetDataModel, $interval) { 208 | function RandomValueDataModel() { 209 | } 210 | 211 | RandomValueDataModel.prototype = Object.create(WidgetDataModel.prototype); 212 | 213 | RandomValueDataModel.prototype.init = function () { 214 | var base = Math.floor(Math.random() * 10) * 10; 215 | 216 | this.updateScope(base); 217 | 218 | var that = this; 219 | 220 | this.intervalPromise = $interval(function () { 221 | var random = base + Math.random(); 222 | that.updateScope(random); 223 | }, 500); 224 | }; 225 | 226 | RandomValueDataModel.prototype.destroy = function () { 227 | WidgetDataModel.prototype.destroy.call(this); 228 | $interval.cancel(this.intervalPromise); 229 | }; 230 | 231 | return RandomValueDataModel; 232 | }) 233 | .factory('RandomTopNDataModel', function (WidgetDataModel, $interval) { 234 | function RandomTopNDataModel() { 235 | } 236 | 237 | RandomTopNDataModel.prototype = Object.create(WidgetDataModel.prototype); 238 | 239 | RandomTopNDataModel.prototype.init = function () { 240 | this.intervalPromise = $interval(function () { 241 | var topTen = _.map(_.range(0, 10), function (index) { 242 | return { 243 | name: 'item' + index, 244 | value: Math.floor(Math.random() * 100) 245 | }; 246 | }); 247 | this.updateScope(topTen); 248 | }.bind(this), 500); 249 | }; 250 | 251 | RandomTopNDataModel.prototype.destroy = function () { 252 | WidgetDataModel.prototype.destroy.call(this); 253 | $interval.cancel(this.intervalPromise); 254 | }; 255 | 256 | return RandomTopNDataModel; 257 | }) 258 | .factory('RandomTimeSeriesDataModel', function (WidgetDataModel, $interval) { 259 | function RandomTimeSeriesDataModel() { 260 | } 261 | 262 | RandomTimeSeriesDataModel.prototype = Object.create(WidgetDataModel.prototype); 263 | 264 | RandomTimeSeriesDataModel.prototype.init = function () { 265 | var max = 30; 266 | var data = []; 267 | var chartValue = 50; 268 | 269 | function nextValue() { 270 | chartValue += Math.random() * 40 - 20; 271 | chartValue = chartValue < 0 ? 0 : chartValue > 100 ? 100 : chartValue; 272 | return chartValue; 273 | } 274 | 275 | var now = Date.now(); 276 | for (var i = max - 1; i >= 0; i--) { 277 | data.push({ 278 | timestamp: now - i * 1000, 279 | value: nextValue() 280 | }); 281 | } 282 | var chart = { 283 | data: data, 284 | max: max, 285 | chartOptions: { 286 | vAxis: {} 287 | } 288 | }; 289 | this.updateScope(chart); 290 | 291 | this.intervalPromise = $interval(function () { 292 | data.shift(); 293 | data.push({ 294 | timestamp: Date.now(), 295 | value: nextValue() 296 | }); 297 | 298 | var chart = { 299 | data: data, 300 | max: max 301 | }; 302 | 303 | this.updateScope(chart); 304 | }.bind(this), 1000); 305 | }; 306 | 307 | RandomTimeSeriesDataModel.prototype.destroy = function () { 308 | WidgetDataModel.prototype.destroy.call(this); 309 | $interval.cancel(this.intervalPromise); 310 | }; 311 | 312 | return RandomTimeSeriesDataModel; 313 | }) 314 | .factory('RandomD3TimeSeriesDataModel', function (WidgetDataModel, $interval) { 315 | function RandomTimeSeriesDataModel() { 316 | } 317 | 318 | RandomTimeSeriesDataModel.prototype = Object.create(WidgetDataModel.prototype); 319 | 320 | RandomTimeSeriesDataModel.prototype.init = function () { 321 | var max = 30; 322 | var data = []; 323 | var chartValue = 50; 324 | 325 | function nextValue() { 326 | chartValue += Math.round(Math.random() * 40 - 20); 327 | chartValue = chartValue < 0 ? 0 : chartValue > 100 ? 100 : chartValue; 328 | return chartValue; 329 | } 330 | 331 | var now = Date.now(); 332 | for (var i = max - 1; i >= 0; i--) { 333 | data.push({ 334 | timestamp: now - i * 1000, 335 | value: nextValue() 336 | }); 337 | } 338 | var chart = [ 339 | { 340 | 'key': 'Series', 341 | values: data 342 | } 343 | ]; 344 | 345 | this.updateScope(chart); 346 | 347 | this.intervalPromise = $interval(function () { 348 | data.shift(); 349 | data.push({ 350 | timestamp: Date.now(), 351 | value: nextValue() 352 | }); 353 | 354 | var chart = [ 355 | { 356 | 'key': 'Series', 357 | values: data 358 | } 359 | ]; 360 | 361 | this.updateScope(chart); 362 | }.bind(this), 1000); 363 | }; 364 | 365 | RandomTimeSeriesDataModel.prototype.destroy = function () { 366 | WidgetDataModel.prototype.destroy.call(this); 367 | $interval.cancel(this.intervalPromise); 368 | }; 369 | 370 | return RandomTimeSeriesDataModel; 371 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2014 DataTorrent, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /app/styles/themes/cyborg.less: -------------------------------------------------------------------------------- 1 | // Cyborg 3.0.3 2 | // Variables 3 | // -------------------------------------------------- 4 | 5 | 6 | // Global values 7 | // -------------------------------------------------- 8 | 9 | // Grays 10 | // ------------------------- 11 | 12 | @gray-darker: #222; 13 | @gray-dark: #282828; 14 | @gray: #555; 15 | @gray-light: #888; 16 | @gray-lighter: #ADAFAE; // #eee 17 | 18 | // Brand colors 19 | // ------------------------- 20 | 21 | @brand-primary: #2A9FD6; 22 | @brand-success: #77B300; 23 | @brand-warning: #FF8800; 24 | @brand-danger: #CC0000; 25 | @brand-info: #9933CC; 26 | 27 | // Scaffolding 28 | // ------------------------- 29 | 30 | @body-bg: #060606; 31 | @text-color: @gray-light; 32 | 33 | // Links 34 | // ------------------------- 35 | 36 | @link-color: @brand-primary; 37 | @link-hover-color: @link-color; 38 | 39 | // Typography 40 | // ------------------------- 41 | 42 | @font-family-sans-serif: "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 43 | @font-family-serif: Georgia, "Times New Roman", Times, serif; 44 | @font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; 45 | @font-family-base: @font-family-sans-serif; 46 | 47 | @font-size-base: 14px; 48 | @font-size-large: ceil(@font-size-base * 1.25); // ~18px 49 | @font-size-small: ceil(@font-size-base * 0.85); // ~12px 50 | 51 | @font-size-h1: floor(@font-size-base * 2.6); // ~36px 52 | @font-size-h2: floor(@font-size-base * 2.15); // ~30px 53 | @font-size-h3: ceil(@font-size-base * 1.7); // ~24px 54 | @font-size-h4: ceil(@font-size-base * 1.25); // ~18px 55 | @font-size-h5: @font-size-base; 56 | @font-size-h6: ceil(@font-size-base * 0.85); // ~12px 57 | 58 | @line-height-base: 1.428571429; // 20/14 59 | @line-height-computed: floor(@font-size-base * @line-height-base); // ~20px 60 | 61 | @headings-font-family: @font-family-base; 62 | @headings-font-weight: 500; 63 | @headings-line-height: 1.1; 64 | @headings-color: #fff; 65 | 66 | 67 | // Iconography 68 | // ------------------------- 69 | 70 | @icon-font-path: "../fonts/"; 71 | @icon-font-name: "glyphicons-halflings-regular"; 72 | 73 | 74 | // Components 75 | // ------------------------- 76 | // Based on 14px font-size and 1.428 line-height (~20px to start) 77 | 78 | @padding-base-vertical: 8px; 79 | @padding-base-horizontal: 12px; 80 | 81 | @padding-large-vertical: 14px; 82 | @padding-large-horizontal: 16px; 83 | 84 | @padding-small-vertical: 5px; 85 | @padding-small-horizontal: 10px; 86 | 87 | @padding-xs-vertical: 1px; 88 | @padding-xs-horizontal: 5px; 89 | 90 | @line-height-large: 1.33; 91 | @line-height-small: 1.5; 92 | 93 | @border-radius-base: 4px; 94 | @border-radius-large: 6px; 95 | @border-radius-small: 3px; 96 | 97 | @component-active-color: #fff; 98 | @component-active-bg: @brand-primary; 99 | 100 | @caret-width-base: 4px; 101 | @caret-width-large: 5px; 102 | 103 | // Tables 104 | // ------------------------- 105 | 106 | @table-cell-padding: 8px; 107 | @table-condensed-cell-padding: 5px; 108 | 109 | @table-bg: darken(@gray-darker, 4%); // overall background-color 110 | @table-bg-accent: darken(@table-bg, 6%); // for striping 111 | @table-bg-hover: @gray-dark; 112 | @table-bg-active: @table-bg-hover; 113 | 114 | @table-border-color: @gray-dark; // table and cell border 115 | 116 | 117 | // Buttons 118 | // ------------------------- 119 | 120 | @btn-font-weight: normal; 121 | 122 | @btn-default-color: #fff; 123 | @btn-default-bg: lighten(@gray-dark, 10%); 124 | @btn-default-border: @btn-default-bg; 125 | 126 | @btn-primary-color: @btn-default-color; 127 | @btn-primary-bg: @brand-primary; 128 | @btn-primary-border: @btn-primary-bg; 129 | 130 | @btn-success-color: @btn-default-color; 131 | @btn-success-bg: @brand-success; 132 | @btn-success-border: @btn-success-bg; 133 | 134 | @btn-warning-color: @btn-default-color; 135 | @btn-warning-bg: @brand-warning; 136 | @btn-warning-border: @btn-warning-bg; 137 | 138 | @btn-danger-color: @btn-default-color; 139 | @btn-danger-bg: @brand-danger; 140 | @btn-danger-border: @btn-danger-bg; 141 | 142 | @btn-info-color: @btn-default-color; 143 | @btn-info-bg: @brand-info; 144 | @btn-info-border: @btn-info-bg; 145 | 146 | @btn-link-disabled-color: @gray-light; 147 | 148 | 149 | // Forms 150 | // ------------------------- 151 | 152 | @input-bg: #fff; 153 | @input-bg-disabled: @gray-lighter; 154 | 155 | @input-color: @text-color; 156 | @input-border: @gray-dark; 157 | @input-border-radius: @border-radius-base; 158 | @input-border-focus: #66afe9; 159 | 160 | @input-color-placeholder: @gray-light; 161 | 162 | @input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); 163 | @input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); 164 | @input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); 165 | 166 | @legend-color: @text-color; 167 | @legend-border-color: @gray-dark; 168 | 169 | @input-group-addon-bg: @gray-lighter; 170 | @input-group-addon-border-color: @input-border; 171 | 172 | 173 | // Dropdowns 174 | // ------------------------- 175 | 176 | @dropdown-bg: @gray-darker; 177 | @dropdown-border: rgba(255,255,255,0.1); 178 | @dropdown-fallback-border: #444; 179 | @dropdown-divider-bg: rgba(255,255,255,0.1); 180 | 181 | @dropdown-link-color: #fff; 182 | @dropdown-link-hover-color: #fff; 183 | @dropdown-link-hover-bg: @dropdown-link-active-bg; 184 | 185 | @dropdown-link-active-color: #fff; 186 | @dropdown-link-active-bg: @component-active-bg; 187 | 188 | @dropdown-link-disabled-color: @text-muted; 189 | 190 | @dropdown-header-color: @text-muted; 191 | 192 | 193 | // COMPONENT VARIABLES 194 | // -------------------------------------------------- 195 | 196 | 197 | // Z-index master list 198 | // ------------------------- 199 | // Used for a bird's eye view of components dependent on the z-axis 200 | // Try to avoid customizing these :) 201 | 202 | @zindex-navbar: 1000; 203 | @zindex-dropdown: 1000; 204 | @zindex-popover: 1010; 205 | @zindex-tooltip: 1030; 206 | @zindex-navbar-fixed: 1030; 207 | @zindex-modal-background: 1040; 208 | @zindex-modal: 1050; 209 | 210 | // Media queries breakpoints 211 | // -------------------------------------------------- 212 | 213 | // Extra small screen / phone 214 | // Note: Deprecated @screen-xs and @screen-phone as of v3.0.1 215 | @screen-xs: 480px; 216 | @screen-xs-min: @screen-xs; 217 | @screen-phone: @screen-xs-min; 218 | 219 | // Small screen / tablet 220 | // Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1 221 | @screen-sm: 768px; 222 | @screen-sm-min: @screen-sm; 223 | @screen-tablet: @screen-sm-min; 224 | 225 | // Medium screen / desktop 226 | // Note: Deprecated @screen-md and @screen-desktop as of v3.0.1 227 | @screen-md: 992px; 228 | @screen-md-min: @screen-md; 229 | @screen-desktop: @screen-md-min; 230 | 231 | // Large screen / wide desktop 232 | // Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1 233 | @screen-lg: 1200px; 234 | @screen-lg-min: @screen-lg; 235 | @screen-lg-desktop: @screen-lg-min; 236 | 237 | // So media queries don't overlap when required, provide a maximum 238 | @screen-xs-max: (@screen-sm-min - 1); 239 | @screen-sm-max: (@screen-md-min - 1); 240 | @screen-md-max: (@screen-lg-min - 1); 241 | 242 | 243 | // Grid system 244 | // -------------------------------------------------- 245 | 246 | // Number of columns in the grid system 247 | @grid-columns: 12; 248 | // Padding, to be divided by two and applied to the left and right of all columns 249 | @grid-gutter-width: 30px; 250 | 251 | // Navbar collapse 252 | 253 | // Point at which the navbar becomes uncollapsed 254 | @grid-float-breakpoint: @screen-sm-min; 255 | // Point at which the navbar begins collapsing 256 | @grid-float-breakpoint-max: (@grid-float-breakpoint - 1); 257 | 258 | 259 | // Navbar 260 | // ------------------------- 261 | 262 | // Basics of a navbar 263 | @navbar-height: 50px; 264 | @navbar-margin-bottom: @line-height-computed; 265 | @navbar-border-radius: @border-radius-base; 266 | @navbar-padding-horizontal: floor(@grid-gutter-width / 2); 267 | @navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2); 268 | 269 | @navbar-default-color: @text-color; 270 | @navbar-default-bg: @body-bg; 271 | @navbar-default-border: darken(@navbar-default-bg, 6.5%); 272 | 273 | // Navbar links 274 | @navbar-default-link-color: @text-color; 275 | @navbar-default-link-hover-color: #fff; 276 | @navbar-default-link-hover-bg: transparent; 277 | @navbar-default-link-active-color: #fff; 278 | @navbar-default-link-active-bg: transparent; 279 | @navbar-default-link-disabled-color: @gray-light; 280 | @navbar-default-link-disabled-bg: transparent; 281 | 282 | // Navbar brand label 283 | @navbar-default-brand-color: #fff; 284 | @navbar-default-brand-hover-color: #fff; 285 | @navbar-default-brand-hover-bg: transparent; 286 | 287 | // Navbar toggle 288 | @navbar-default-toggle-hover-bg: @gray-dark; 289 | @navbar-default-toggle-icon-bar-bg: #ccc; 290 | @navbar-default-toggle-border-color: @gray-dark; 291 | 292 | 293 | // Inverted navbar 294 | // 295 | // Reset inverted navbar basics 296 | @navbar-inverse-color: @gray-light; 297 | @navbar-inverse-bg: @gray-darker; 298 | @navbar-inverse-border: darken(@navbar-inverse-bg, 10%); 299 | 300 | // Inverted navbar links 301 | @navbar-inverse-link-color: @gray-light; 302 | @navbar-inverse-link-hover-color: #fff; 303 | @navbar-inverse-link-hover-bg: transparent; 304 | @navbar-inverse-link-active-color: @navbar-inverse-link-hover-color; 305 | @navbar-inverse-link-active-bg: transparent; 306 | @navbar-inverse-link-disabled-color: #aaa; 307 | @navbar-inverse-link-disabled-bg: transparent; 308 | 309 | // Inverted navbar brand label 310 | @navbar-inverse-brand-color: #fff; 311 | @navbar-inverse-brand-hover-color: #fff; 312 | @navbar-inverse-brand-hover-bg: transparent; 313 | 314 | // Inverted navbar toggle 315 | @navbar-inverse-toggle-hover-bg: #333; 316 | @navbar-inverse-toggle-icon-bar-bg: #fff; 317 | @navbar-inverse-toggle-border-color: #333; 318 | 319 | 320 | // Navs 321 | // ------------------------- 322 | 323 | @nav-link-padding: 10px 15px; 324 | @nav-link-hover-bg: @gray-darker; 325 | 326 | @nav-disabled-link-color: @gray-light; 327 | @nav-disabled-link-hover-color: @gray-light; 328 | 329 | @nav-open-link-hover-color: @gray-darker; 330 | 331 | // Tabs 332 | @nav-tabs-border-color: @gray-dark; 333 | 334 | @nav-tabs-link-hover-border-color: transparent; 335 | 336 | @nav-tabs-active-link-hover-bg: @brand-primary; 337 | @nav-tabs-active-link-hover-color: #fff; 338 | @nav-tabs-active-link-hover-border-color: @gray-dark; 339 | 340 | @nav-tabs-justified-link-border-color: #ddd; 341 | @nav-tabs-justified-active-link-border-color: @body-bg; 342 | 343 | // Pills 344 | @nav-pills-border-radius: @border-radius-base; 345 | @nav-pills-active-link-hover-bg: @component-active-bg; 346 | @nav-pills-active-link-hover-color: @component-active-color; 347 | 348 | 349 | // Pagination 350 | // ------------------------- 351 | 352 | @pagination-bg: @gray-darker; 353 | @pagination-border: @gray-dark; 354 | 355 | @pagination-hover-bg: @component-active-bg; 356 | 357 | @pagination-active-bg: @brand-primary; 358 | @pagination-active-color: #fff; 359 | 360 | @pagination-disabled-color: @gray-light; 361 | 362 | 363 | // Pager 364 | // ------------------------- 365 | 366 | @pager-border-radius: 15px; 367 | @pager-disabled-color: @gray-light; 368 | 369 | 370 | // Jumbotron 371 | // ------------------------- 372 | 373 | @jumbotron-padding: 30px; 374 | @jumbotron-color: inherit; 375 | @jumbotron-bg: darken(@gray-darker, 5%); 376 | @jumbotron-heading-color: inherit; 377 | @jumbotron-font-size: ceil(@font-size-base * 1.5); 378 | 379 | 380 | // Form states and alerts 381 | // ------------------------- 382 | 383 | @state-success-text: #fff; 384 | @state-success-bg: @brand-success; 385 | @state-success-border: darken(spin(@state-success-bg, -10), 5%); 386 | 387 | @state-info-text: #fff; 388 | @state-info-bg: @brand-info; 389 | @state-info-border: darken(spin(@state-info-bg, -10), 7%); 390 | 391 | @state-warning-text: #fff; 392 | @state-warning-bg: @brand-warning; 393 | @state-warning-border: darken(spin(@state-warning-bg, -10), 3%); 394 | 395 | @state-danger-text: #fff; 396 | @state-danger-bg: @brand-danger; 397 | @state-danger-border: darken(spin(@state-danger-bg, -10), 3%); 398 | 399 | 400 | // Tooltips 401 | // ------------------------- 402 | @tooltip-max-width: 200px; 403 | @tooltip-color: #fff; 404 | @tooltip-bg: rgba(0,0,0,.9); 405 | 406 | @tooltip-arrow-width: 5px; 407 | @tooltip-arrow-color: @tooltip-bg; 408 | 409 | 410 | // Popovers 411 | // ------------------------- 412 | @popover-bg: lighten(@body-bg, 10%); 413 | @popover-max-width: 276px; 414 | @popover-border-color: rgba(0,0,0,.2); 415 | @popover-fallback-border-color: #999; 416 | 417 | @popover-title-bg: darken(@popover-bg, 3%); 418 | 419 | @popover-arrow-width: 10px; 420 | @popover-arrow-color: @popover-bg; 421 | 422 | @popover-arrow-outer-width: (@popover-arrow-width + 1); 423 | @popover-arrow-outer-color: rgba(0,0,0,.25); 424 | @popover-arrow-outer-fallback-color: #999; 425 | 426 | 427 | // Labels 428 | // ------------------------- 429 | 430 | @label-default-bg: @btn-default-bg; 431 | @label-primary-bg: @brand-primary; 432 | @label-success-bg: @brand-success; 433 | @label-info-bg: @brand-info; 434 | @label-warning-bg: @brand-warning; 435 | @label-danger-bg: @brand-danger; 436 | 437 | @label-color: #fff; 438 | @label-link-hover-color: #fff; 439 | 440 | 441 | // Modals 442 | // ------------------------- 443 | @modal-inner-padding: 20px; 444 | 445 | @modal-title-padding: 15px; 446 | @modal-title-line-height: @line-height-base; 447 | 448 | @modal-content-bg: lighten(@body-bg, 10%); 449 | @modal-content-border-color: rgba(0,0,0,.2); 450 | @modal-content-fallback-border-color: #999; 451 | 452 | @modal-backdrop-bg: #000; 453 | @modal-header-border-color: @gray-dark; 454 | @modal-footer-border-color: @modal-header-border-color; 455 | 456 | 457 | // Alerts 458 | // ------------------------- 459 | @alert-padding: 15px; 460 | @alert-border-radius: @border-radius-base; 461 | @alert-link-font-weight: bold; 462 | 463 | @alert-success-bg: @state-success-bg; 464 | @alert-success-text: @state-success-text; 465 | @alert-success-border: @state-success-border; 466 | 467 | @alert-info-bg: @state-info-bg; 468 | @alert-info-text: @state-info-text; 469 | @alert-info-border: @state-info-border; 470 | 471 | @alert-warning-bg: @state-warning-bg; 472 | @alert-warning-text: @state-warning-text; 473 | @alert-warning-border: @state-warning-border; 474 | 475 | @alert-danger-bg: @state-danger-bg; 476 | @alert-danger-text: @state-danger-text; 477 | @alert-danger-border: @state-danger-border; 478 | 479 | 480 | // Progress bars 481 | // ------------------------- 482 | @progress-bg: @gray-darker; 483 | @progress-bar-color: #fff; 484 | 485 | @progress-bar-bg: @brand-primary; 486 | @progress-bar-success-bg: @brand-success; 487 | @progress-bar-warning-bg: @brand-warning; 488 | @progress-bar-danger-bg: @brand-danger; 489 | @progress-bar-info-bg: @brand-info; 490 | 491 | 492 | // List group 493 | // ------------------------- 494 | @list-group-bg: @gray-darker; 495 | @list-group-border: @gray-dark; 496 | @list-group-border-radius: @border-radius-base; 497 | 498 | @list-group-hover-bg: lighten(@list-group-bg, 15%); 499 | @list-group-active-color: @component-active-color; 500 | @list-group-active-bg: @component-active-bg; 501 | @list-group-active-border: @list-group-active-bg; 502 | 503 | @list-group-link-color: @text-color; 504 | @list-group-link-heading-color: #fff; 505 | 506 | 507 | // Panels 508 | // ------------------------- 509 | @panel-bg: @gray-darker; 510 | @panel-inner-border: @gray-dark; 511 | @panel-border-radius: @border-radius-base; 512 | @panel-footer-bg: @panel-default-heading-bg; 513 | 514 | @panel-default-text: @text-color; 515 | @panel-default-border: @panel-inner-border; 516 | @panel-default-heading-bg: lighten(@gray-darker, 10%); 517 | 518 | @panel-primary-text: #fff; 519 | @panel-primary-border: @brand-primary; 520 | @panel-primary-heading-bg: @brand-primary; 521 | 522 | @panel-success-text: @state-success-text; 523 | @panel-success-border: @state-success-border; 524 | @panel-success-heading-bg: @state-success-bg; 525 | 526 | @panel-warning-text: @state-warning-text; 527 | @panel-warning-border: @state-warning-border; 528 | @panel-warning-heading-bg: @state-warning-bg; 529 | 530 | @panel-danger-text: @state-danger-text; 531 | @panel-danger-border: @state-danger-border; 532 | @panel-danger-heading-bg: @state-danger-bg; 533 | 534 | @panel-info-text: @state-info-text; 535 | @panel-info-border: @state-info-border; 536 | @panel-info-heading-bg: @state-info-bg; 537 | 538 | 539 | // Thumbnails 540 | // ------------------------- 541 | @thumbnail-padding: 4px; 542 | @thumbnail-bg: @body-bg; 543 | @thumbnail-border: #ddd; 544 | @thumbnail-border-radius: @border-radius-base; 545 | 546 | @thumbnail-caption-color: @text-color; 547 | @thumbnail-caption-padding: 9px; 548 | 549 | 550 | // Wells 551 | // ------------------------- 552 | @well-bg: darken(@gray-darker, 5%); 553 | 554 | 555 | // Badges 556 | // ------------------------- 557 | @badge-color: #fff; 558 | @badge-link-hover-color: #fff; 559 | @badge-bg: @brand-primary; 560 | 561 | @badge-active-color: @brand-primary; 562 | @badge-active-bg: #fff; 563 | 564 | @badge-font-weight: bold; 565 | @badge-line-height: 1; 566 | @badge-border-radius: 10px; 567 | 568 | 569 | // Breadcrumbs 570 | // ------------------------- 571 | @breadcrumb-bg: @gray-darker; 572 | @breadcrumb-color: #fff; 573 | @breadcrumb-active-color: @text-color; 574 | @breadcrumb-separator: "/"; 575 | 576 | 577 | // Carousel 578 | // ------------------------ 579 | 580 | @carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); 581 | 582 | @carousel-control-color: #fff; 583 | @carousel-control-width: 15%; 584 | @carousel-control-opacity: .5; 585 | @carousel-control-font-size: 20px; 586 | 587 | @carousel-indicator-active-bg: #fff; 588 | @carousel-indicator-border-color: #fff; 589 | 590 | @carousel-caption-color: #fff; 591 | 592 | 593 | // Close 594 | // ------------------------ 595 | @close-font-weight: bold; 596 | @close-color: #000; 597 | @close-text-shadow: 0 1px 0 #fff; 598 | 599 | 600 | // Code 601 | // ------------------------ 602 | @code-color: #c7254e; 603 | @code-bg: #f9f2f4; 604 | 605 | @pre-bg: #f5f5f5; 606 | @pre-color: @gray-dark; 607 | @pre-border-color: #ccc; 608 | @pre-scrollable-max-height: 340px; 609 | 610 | // Type 611 | // ------------------------ 612 | @text-muted: @gray-light; 613 | @abbr-border-color: @gray-light; 614 | @headings-small-color: @gray-light; 615 | @blockquote-small-color: @gray; 616 | @blockquote-border-color: @gray-dark; 617 | @page-header-border-color: @gray-dark; 618 | 619 | // Miscellaneous 620 | // ------------------------- 621 | 622 | // Hr border color 623 | @hr-border: @gray-dark; 624 | 625 | // Horizontal forms & lists 626 | @component-offset-horizontal: 180px; 627 | 628 | 629 | // Container sizes 630 | // -------------------------------------------------- 631 | 632 | // Small screen / tablet 633 | @container-tablet: ((720px + @grid-gutter-width)); 634 | @container-sm: @container-tablet; 635 | 636 | // Medium screen / desktop 637 | @container-desktop: ((940px + @grid-gutter-width)); 638 | @container-md: @container-desktop; 639 | 640 | // Large screen / wide desktop 641 | @container-large-desktop: ((1140px + @grid-gutter-width)); 642 | @container-lg: @container-large-desktop; 643 | -------------------------------------------------------------------------------- /app/styles/themes/default.less: -------------------------------------------------------------------------------- 1 | // 2 | // Variables 3 | // -------------------------------------------------- 4 | 5 | 6 | // Global values 7 | // -------------------------------------------------- 8 | 9 | // Grays 10 | // ------------------------- 11 | 12 | @gray-darker: lighten(#000, 13.5%); // #222 13 | @gray-dark: lighten(#000, 20%); // #333 14 | @gray: lighten(#000, 33.5%); // #555 15 | @gray-light: lighten(#000, 60%); // #999 16 | @gray-lighter: lighten(#000, 93.5%); // #eee 17 | 18 | // Brand colors 19 | // ------------------------- 20 | 21 | @brand-primary: #0F5E83; 22 | @brand-success: #5cb85c; 23 | @brand-warning: #f0ad4e; 24 | @brand-danger: #d9534f; 25 | @brand-info: #5bc0de; 26 | 27 | // Scaffolding 28 | // ------------------------- 29 | 30 | @body-bg: #fff; 31 | @text-color: @gray-dark; 32 | 33 | // Links 34 | // ------------------------- 35 | 36 | @link-color: @brand-primary; 37 | @link-hover-color: darken(@link-color, 15%); 38 | 39 | // Typography 40 | // ------------------------- 41 | 42 | @font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; 43 | @font-family-serif: Georgia, "Times New Roman", Times, serif; 44 | @font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; 45 | @font-family-base: @font-family-sans-serif; 46 | 47 | @font-size-base: 14px; 48 | @font-size-large: ceil(@font-size-base * 1.25); // ~18px 49 | @font-size-small: ceil(@font-size-base * 0.85); // ~12px 50 | 51 | @font-size-h1: floor(@font-size-base * 2.6); // ~36px 52 | @font-size-h2: floor(@font-size-base * 2.15); // ~30px 53 | @font-size-h3: ceil(@font-size-base * 1.7); // ~24px 54 | @font-size-h4: ceil(@font-size-base * 1.25); // ~18px 55 | @font-size-h5: @font-size-base; 56 | @font-size-h6: ceil(@font-size-base * 0.85); // ~12px 57 | 58 | @line-height-base: 1.428571429; // 20/14 59 | @line-height-computed: floor(@font-size-base * @line-height-base); // ~20px 60 | 61 | @headings-font-family: @font-family-base; 62 | @headings-font-weight: 500; 63 | @headings-line-height: 1.1; 64 | @headings-color: inherit; 65 | 66 | 67 | // Iconography 68 | // ------------------------- 69 | 70 | @icon-font-path: "../../bower_components/bootstrap/dist/fonts/"; 71 | @icon-font-name: "glyphicons-halflings-regular"; 72 | 73 | 74 | // Components 75 | // ------------------------- 76 | // Based on 14px font-size and 1.428 line-height (~20px to start) 77 | 78 | @padding-base-vertical: 6px; 79 | @padding-base-horizontal: 12px; 80 | 81 | @padding-large-vertical: 10px; 82 | @padding-large-horizontal: 16px; 83 | 84 | @padding-small-vertical: 5px; 85 | @padding-small-horizontal: 10px; 86 | 87 | @padding-xs-vertical: 1px; 88 | @padding-xs-horizontal: 5px; 89 | 90 | @line-height-large: 1.33; 91 | @line-height-small: 1.5; 92 | 93 | @border-radius-base: 4px; 94 | @border-radius-large: 6px; 95 | @border-radius-small: 3px; 96 | 97 | @component-active-color: #fff; 98 | @component-active-bg: @brand-primary; 99 | 100 | @caret-width-base: 4px; 101 | @caret-width-large: 5px; 102 | 103 | // Tables 104 | // ------------------------- 105 | 106 | @table-cell-padding: 8px; 107 | @table-condensed-cell-padding: 5px; 108 | 109 | @table-bg: transparent; // overall background-color 110 | @table-bg-accent: #f9f9f9; // for striping 111 | @table-bg-hover: #f5f5f5; 112 | @table-bg-active: @table-bg-hover; 113 | 114 | @table-border-color: #ddd; // table and cell border 115 | 116 | 117 | // Buttons 118 | // ------------------------- 119 | 120 | @btn-font-weight: normal; 121 | 122 | @btn-default-color: #333; 123 | @btn-default-bg: #fff; 124 | @btn-default-border: #ccc; 125 | 126 | @btn-primary-color: #fff; 127 | @btn-primary-bg: @brand-primary; 128 | @btn-primary-border: darken(@btn-primary-bg, 5%); 129 | 130 | @btn-success-color: #fff; 131 | @btn-success-bg: @brand-success; 132 | @btn-success-border: darken(@btn-success-bg, 5%); 133 | 134 | @btn-warning-color: #fff; 135 | @btn-warning-bg: @brand-warning; 136 | @btn-warning-border: darken(@btn-warning-bg, 5%); 137 | 138 | @btn-danger-color: #fff; 139 | @btn-danger-bg: @brand-danger; 140 | @btn-danger-border: darken(@btn-danger-bg, 5%); 141 | 142 | @btn-info-color: #fff; 143 | @btn-info-bg: @brand-info; 144 | @btn-info-border: darken(@btn-info-bg, 5%); 145 | 146 | @btn-link-disabled-color: @gray-light; 147 | 148 | 149 | // Forms 150 | // ------------------------- 151 | 152 | @input-bg: #fff; 153 | @input-bg-disabled: @gray-lighter; 154 | 155 | @input-color: @gray; 156 | @input-border: #ccc; 157 | @input-border-radius: @border-radius-base; 158 | @input-border-focus: #66afe9; 159 | 160 | @input-color-placeholder: @gray-light; 161 | 162 | @input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); 163 | @input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); 164 | @input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); 165 | 166 | @legend-color: @gray-dark; 167 | @legend-border-color: #e5e5e5; 168 | 169 | @input-group-addon-bg: @gray-lighter; 170 | @input-group-addon-border-color: @input-border; 171 | 172 | 173 | // Dropdowns 174 | // ------------------------- 175 | 176 | @dropdown-bg: #fff; 177 | @dropdown-border: rgba(0,0,0,.15); 178 | @dropdown-fallback-border: #ccc; 179 | @dropdown-divider-bg: #e5e5e5; 180 | 181 | @dropdown-link-color: @gray-dark; 182 | @dropdown-link-hover-color: darken(@gray-dark, 5%); 183 | @dropdown-link-hover-bg: #f5f5f5; 184 | 185 | @dropdown-link-active-color: @component-active-color; 186 | @dropdown-link-active-bg: @component-active-bg; 187 | 188 | @dropdown-link-disabled-color: @gray-light; 189 | 190 | @dropdown-header-color: @gray-light; 191 | 192 | 193 | // COMPONENT VARIABLES 194 | // -------------------------------------------------- 195 | 196 | 197 | // Z-index master list 198 | // ------------------------- 199 | // Used for a bird's eye view of components dependent on the z-axis 200 | // Try to avoid customizing these :) 201 | 202 | @zindex-navbar: 1000; 203 | @zindex-dropdown: 1000; 204 | @zindex-popover: 1010; 205 | @zindex-tooltip: 1030; 206 | @zindex-navbar-fixed: 1030; 207 | @zindex-modal-background: 1040; 208 | @zindex-modal: 1050; 209 | 210 | // Media queries breakpoints 211 | // -------------------------------------------------- 212 | 213 | // Extra small screen / phone 214 | // Note: Deprecated @screen-xs and @screen-phone as of v3.0.1 215 | @screen-xs: 480px; 216 | @screen-xs-min: @screen-xs; 217 | @screen-phone: @screen-xs-min; 218 | 219 | // Small screen / tablet 220 | // Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1 221 | @screen-sm: 768px; 222 | @screen-sm-min: @screen-sm; 223 | @screen-tablet: @screen-sm-min; 224 | 225 | // Medium screen / desktop 226 | // Note: Deprecated @screen-md and @screen-desktop as of v3.0.1 227 | @screen-md: 992px; 228 | @screen-md-min: @screen-md; 229 | @screen-desktop: @screen-md-min; 230 | 231 | // Large screen / wide desktop 232 | // Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1 233 | @screen-lg: 1200px; 234 | @screen-lg-min: @screen-lg; 235 | @screen-lg-desktop: @screen-lg-min; 236 | 237 | // So media queries don't overlap when required, provide a maximum 238 | @screen-xs-max: (@screen-sm-min - 1); 239 | @screen-sm-max: (@screen-md-min - 1); 240 | @screen-md-max: (@screen-lg-min - 1); 241 | 242 | 243 | // Grid system 244 | // -------------------------------------------------- 245 | 246 | // Number of columns in the grid system 247 | @grid-columns: 12; 248 | // Padding, to be divided by two and applied to the left and right of all columns 249 | @grid-gutter-width: 30px; 250 | 251 | // Navbar collapse 252 | 253 | // Point at which the navbar becomes uncollapsed 254 | @grid-float-breakpoint: @screen-sm-min; 255 | // Point at which the navbar begins collapsing 256 | @grid-float-breakpoint-max: (@grid-float-breakpoint - 1); 257 | 258 | 259 | // Navbar 260 | // ------------------------- 261 | 262 | // Basics of a navbar 263 | @navbar-height: 50px; 264 | @navbar-margin-bottom: @line-height-computed; 265 | @navbar-border-radius: @border-radius-base; 266 | @navbar-padding-horizontal: floor(@grid-gutter-width / 2); 267 | @navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2); 268 | 269 | @navbar-default-color: #777; 270 | @navbar-default-bg: #f8f8f8; 271 | @navbar-default-border: darken(@navbar-default-bg, 6.5%); 272 | 273 | // Navbar links 274 | @navbar-default-link-color: #777; 275 | @navbar-default-link-hover-color: #333; 276 | @navbar-default-link-hover-bg: transparent; 277 | @navbar-default-link-active-color: #555; 278 | @navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%); 279 | @navbar-default-link-disabled-color: #ccc; 280 | @navbar-default-link-disabled-bg: transparent; 281 | 282 | // Navbar brand label 283 | @navbar-default-brand-color: @navbar-default-link-color; 284 | @navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%); 285 | @navbar-default-brand-hover-bg: transparent; 286 | 287 | // Navbar toggle 288 | @navbar-default-toggle-hover-bg: #ddd; 289 | @navbar-default-toggle-icon-bar-bg: #ccc; 290 | @navbar-default-toggle-border-color: #ddd; 291 | 292 | 293 | // Inverted navbar 294 | // 295 | // Reset inverted navbar basics 296 | @navbar-inverse-color: @gray-light; 297 | @navbar-inverse-bg: #222; 298 | @navbar-inverse-border: darken(@navbar-inverse-bg, 10%); 299 | 300 | // Inverted navbar links 301 | @navbar-inverse-link-color: @gray-light; 302 | @navbar-inverse-link-hover-color: #fff; 303 | @navbar-inverse-link-hover-bg: transparent; 304 | @navbar-inverse-link-active-color: @navbar-inverse-link-hover-color; 305 | @navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%); 306 | @navbar-inverse-link-disabled-color: #444; 307 | @navbar-inverse-link-disabled-bg: transparent; 308 | 309 | // Inverted navbar brand label 310 | @navbar-inverse-brand-color: @navbar-inverse-link-color; 311 | @navbar-inverse-brand-hover-color: #fff; 312 | @navbar-inverse-brand-hover-bg: transparent; 313 | 314 | // Inverted navbar toggle 315 | @navbar-inverse-toggle-hover-bg: #333; 316 | @navbar-inverse-toggle-icon-bar-bg: #fff; 317 | @navbar-inverse-toggle-border-color: #333; 318 | 319 | 320 | // Navs 321 | // ------------------------- 322 | 323 | @nav-link-padding: 10px 15px; 324 | @nav-link-hover-bg: @gray-lighter; 325 | 326 | @nav-disabled-link-color: @gray-light; 327 | @nav-disabled-link-hover-color: @gray-light; 328 | 329 | @nav-open-link-hover-color: #fff; 330 | 331 | // Tabs 332 | @nav-tabs-border-color: #ddd; 333 | 334 | @nav-tabs-link-hover-border-color: @gray-lighter; 335 | 336 | @nav-tabs-active-link-hover-bg: @body-bg; 337 | @nav-tabs-active-link-hover-color: @gray; 338 | @nav-tabs-active-link-hover-border-color: #ddd; 339 | 340 | @nav-tabs-justified-link-border-color: #ddd; 341 | @nav-tabs-justified-active-link-border-color: @body-bg; 342 | 343 | // Pills 344 | @nav-pills-border-radius: @border-radius-base; 345 | @nav-pills-active-link-hover-bg: @component-active-bg; 346 | @nav-pills-active-link-hover-color: @component-active-color; 347 | 348 | 349 | // Pagination 350 | // ------------------------- 351 | 352 | @pagination-bg: #fff; 353 | @pagination-border: #ddd; 354 | 355 | @pagination-hover-bg: @gray-lighter; 356 | 357 | @pagination-active-bg: @brand-primary; 358 | @pagination-active-color: #fff; 359 | 360 | @pagination-disabled-color: @gray-light; 361 | 362 | 363 | // Pager 364 | // ------------------------- 365 | 366 | @pager-border-radius: 15px; 367 | @pager-disabled-color: @gray-light; 368 | 369 | 370 | // Jumbotron 371 | // ------------------------- 372 | 373 | @jumbotron-padding: 30px; 374 | @jumbotron-color: inherit; 375 | @jumbotron-bg: @gray-lighter; 376 | @jumbotron-heading-color: inherit; 377 | @jumbotron-font-size: ceil(@font-size-base * 1.5); 378 | 379 | 380 | // Form states and alerts 381 | // ------------------------- 382 | 383 | @state-success-text: #3c763d; 384 | @state-success-bg: #dff0d8; 385 | @state-success-border: darken(spin(@state-success-bg, -10), 5%); 386 | 387 | @state-info-text: #31708f; 388 | @state-info-bg: #d9edf7; 389 | @state-info-border: darken(spin(@state-info-bg, -10), 7%); 390 | 391 | @state-warning-text: #8a6d3b; 392 | @state-warning-bg: #fcf8e3; 393 | @state-warning-border: darken(spin(@state-warning-bg, -10), 5%); 394 | 395 | @state-danger-text: #a94442; 396 | @state-danger-bg: #f2dede; 397 | @state-danger-border: darken(spin(@state-danger-bg, -10), 5%); 398 | 399 | 400 | // Tooltips 401 | // ------------------------- 402 | @tooltip-max-width: 200px; 403 | @tooltip-color: #fff; 404 | @tooltip-bg: #000; 405 | 406 | @tooltip-arrow-width: 5px; 407 | @tooltip-arrow-color: @tooltip-bg; 408 | 409 | 410 | // Popovers 411 | // ------------------------- 412 | @popover-bg: #fff; 413 | @popover-max-width: 276px; 414 | @popover-border-color: rgba(0,0,0,.2); 415 | @popover-fallback-border-color: #ccc; 416 | 417 | @popover-title-bg: darken(@popover-bg, 3%); 418 | 419 | @popover-arrow-width: 10px; 420 | @popover-arrow-color: #fff; 421 | 422 | @popover-arrow-outer-width: (@popover-arrow-width + 1); 423 | @popover-arrow-outer-color: rgba(0,0,0,.25); 424 | @popover-arrow-outer-fallback-color: #999; 425 | 426 | 427 | // Labels 428 | // ------------------------- 429 | 430 | @label-default-bg: @gray-light; 431 | @label-primary-bg: @brand-primary; 432 | @label-success-bg: @brand-success; 433 | @label-info-bg: @brand-info; 434 | @label-warning-bg: @brand-warning; 435 | @label-danger-bg: @brand-danger; 436 | 437 | @label-color: #fff; 438 | @label-link-hover-color: #fff; 439 | 440 | 441 | // Modals 442 | // ------------------------- 443 | @modal-inner-padding: 20px; 444 | 445 | @modal-title-padding: 15px; 446 | @modal-title-line-height: @line-height-base; 447 | 448 | @modal-content-bg: #fff; 449 | @modal-content-border-color: rgba(0,0,0,.2); 450 | @modal-content-fallback-border-color: #999; 451 | 452 | @modal-backdrop-bg: #000; 453 | @modal-header-border-color: #e5e5e5; 454 | @modal-footer-border-color: @modal-header-border-color; 455 | 456 | 457 | // Alerts 458 | // ------------------------- 459 | @alert-padding: 15px; 460 | @alert-border-radius: @border-radius-base; 461 | @alert-link-font-weight: bold; 462 | 463 | @alert-success-bg: @state-success-bg; 464 | @alert-success-text: @state-success-text; 465 | @alert-success-border: @state-success-border; 466 | 467 | @alert-info-bg: @state-info-bg; 468 | @alert-info-text: @state-info-text; 469 | @alert-info-border: @state-info-border; 470 | 471 | @alert-warning-bg: @state-warning-bg; 472 | @alert-warning-text: @state-warning-text; 473 | @alert-warning-border: @state-warning-border; 474 | 475 | @alert-danger-bg: @state-danger-bg; 476 | @alert-danger-text: @state-danger-text; 477 | @alert-danger-border: @state-danger-border; 478 | 479 | 480 | // Progress bars 481 | // ------------------------- 482 | @progress-bg: #f5f5f5; 483 | @progress-bar-color: #fff; 484 | 485 | @progress-bar-bg: @brand-primary; 486 | @progress-bar-success-bg: @brand-success; 487 | @progress-bar-warning-bg: @brand-warning; 488 | @progress-bar-danger-bg: @brand-danger; 489 | @progress-bar-info-bg: @brand-info; 490 | 491 | 492 | // List group 493 | // ------------------------- 494 | @list-group-bg: #fff; 495 | @list-group-border: #ddd; 496 | @list-group-border-radius: @border-radius-base; 497 | 498 | @list-group-hover-bg: #f5f5f5; 499 | @list-group-active-color: @component-active-color; 500 | @list-group-active-bg: @component-active-bg; 501 | @list-group-active-border: @list-group-active-bg; 502 | 503 | @list-group-link-color: #555; 504 | @list-group-link-heading-color: #333; 505 | 506 | 507 | // Panels 508 | // ------------------------- 509 | @panel-bg: #fff; 510 | @panel-inner-border: #ddd; 511 | @panel-border-radius: @border-radius-base; 512 | @panel-footer-bg: #f5f5f5; 513 | 514 | @panel-default-text: @gray-dark; 515 | @panel-default-border: #ddd; 516 | @panel-default-heading-bg: #f5f5f5; 517 | 518 | @panel-primary-text: #fff; 519 | @panel-primary-border: @brand-primary; 520 | @panel-primary-heading-bg: @brand-primary; 521 | 522 | @panel-success-text: @state-success-text; 523 | @panel-success-border: @state-success-border; 524 | @panel-success-heading-bg: @state-success-bg; 525 | 526 | @panel-warning-text: @state-warning-text; 527 | @panel-warning-border: @state-warning-border; 528 | @panel-warning-heading-bg: @state-warning-bg; 529 | 530 | @panel-danger-text: @state-danger-text; 531 | @panel-danger-border: @state-danger-border; 532 | @panel-danger-heading-bg: @state-danger-bg; 533 | 534 | @panel-info-text: @state-info-text; 535 | @panel-info-border: @state-info-border; 536 | @panel-info-heading-bg: @state-info-bg; 537 | 538 | 539 | // Thumbnails 540 | // ------------------------- 541 | @thumbnail-padding: 4px; 542 | @thumbnail-bg: @body-bg; 543 | @thumbnail-border: #ddd; 544 | @thumbnail-border-radius: @border-radius-base; 545 | 546 | @thumbnail-caption-color: @text-color; 547 | @thumbnail-caption-padding: 9px; 548 | 549 | 550 | // Wells 551 | // ------------------------- 552 | @well-bg: #f5f5f5; 553 | 554 | 555 | // Badges 556 | // ------------------------- 557 | @badge-color: #fff; 558 | @badge-link-hover-color: #fff; 559 | @badge-bg: @gray-light; 560 | 561 | @badge-active-color: @link-color; 562 | @badge-active-bg: #fff; 563 | 564 | @badge-font-weight: bold; 565 | @badge-line-height: 1; 566 | @badge-border-radius: 10px; 567 | 568 | 569 | // Breadcrumbs 570 | // ------------------------- 571 | @breadcrumb-bg: #f5f5f5; 572 | @breadcrumb-color: #ccc; 573 | @breadcrumb-active-color: @gray-light; 574 | @breadcrumb-separator: "/"; 575 | 576 | 577 | // Carousel 578 | // ------------------------ 579 | 580 | @carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); 581 | 582 | @carousel-control-color: #fff; 583 | @carousel-control-width: 15%; 584 | @carousel-control-opacity: .5; 585 | @carousel-control-font-size: 20px; 586 | 587 | @carousel-indicator-active-bg: #fff; 588 | @carousel-indicator-border-color: #fff; 589 | 590 | @carousel-caption-color: #fff; 591 | 592 | 593 | // Close 594 | // ------------------------ 595 | @close-font-weight: bold; 596 | @close-color: #000; 597 | @close-text-shadow: 0 1px 0 #fff; 598 | 599 | 600 | // Code 601 | // ------------------------ 602 | @code-color: #c7254e; 603 | @code-bg: #f9f2f4; 604 | 605 | @pre-bg: #f5f5f5; 606 | @pre-color: @gray-dark; 607 | @pre-border-color: #ccc; 608 | @pre-scrollable-max-height: 340px; 609 | 610 | // Type 611 | // ------------------------ 612 | @text-muted: @gray-light; 613 | @abbr-border-color: @gray-light; 614 | @headings-small-color: @gray-light; 615 | @blockquote-small-color: @gray-light; 616 | @blockquote-border-color: @gray-lighter; 617 | @page-header-border-color: @gray-lighter; 618 | 619 | // Miscellaneous 620 | // ------------------------- 621 | 622 | // Hr border color 623 | @hr-border: @gray-lighter; 624 | 625 | // Horizontal forms & lists 626 | @component-offset-horizontal: 180px; 627 | 628 | 629 | // Container sizes 630 | // -------------------------------------------------- 631 | 632 | // Small screen / tablet 633 | @container-tablet: ((720px + @grid-gutter-width)); 634 | @container-sm: @container-tablet; 635 | 636 | // Medium screen / desktop 637 | @container-desktop: ((940px + @grid-gutter-width)); 638 | @container-md: @container-desktop; 639 | 640 | // Large screen / wide desktop 641 | @container-large-desktop: ((1140px + @grid-gutter-width)); 642 | @container-lg: @container-large-desktop; 643 | 644 | 645 | 646 | // ---------------------------------- 647 | // angular-dashboard variables 648 | // ---------------------------------- -------------------------------------------------------------------------------- /app/styles/themes/simplex.less: -------------------------------------------------------------------------------- 1 | // Simplex 3.0.3 2 | // Variables 3 | // -------------------------------------------------- 4 | 5 | 6 | // Global values 7 | // -------------------------------------------------- 8 | 9 | // Grays 10 | // ------------------------- 11 | 12 | @gray-darker: lighten(#000, 13.5%); // #222 13 | @gray-dark: lighten(#000, 20%); // #333 14 | @gray: lighten(#000, 33.5%); // #555 15 | @gray-light: lighten(#000, 60%); // #999 16 | @gray-lighter: lighten(#000, 93.5%); // #eee 17 | 18 | // Brand colors 19 | // ------------------------- 20 | 21 | @brand-primary: #D9230F; 22 | @brand-success: #3D9400; 23 | @brand-warning: #9B479F; 24 | @brand-danger: #D9831F; 25 | @brand-info: #029ACF; 26 | 27 | // Scaffolding 28 | // ------------------------- 29 | 30 | @body-bg: #F7F7F7; 31 | @text-color: @gray-dark; 32 | 33 | // Links 34 | // ------------------------- 35 | 36 | @link-color: @brand-primary; 37 | @link-hover-color: darken(@link-color, 15%); 38 | 39 | // Typography 40 | // ------------------------- 41 | 42 | @font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; 43 | @font-family-serif: Georgia, "Times New Roman", Times, serif; 44 | @font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; 45 | @font-family-base: @font-family-sans-serif; 46 | 47 | @font-size-base: 13px; 48 | @font-size-large: ceil(@font-size-base * 1.25); // ~18px 49 | @font-size-small: ceil(@font-size-base * 0.85); // ~12px 50 | 51 | @font-size-h1: floor(@font-size-base * 2.6); // ~36px 52 | @font-size-h2: floor(@font-size-base * 2.15); // ~30px 53 | @font-size-h3: ceil(@font-size-base * 1.7); // ~24px 54 | @font-size-h4: ceil(@font-size-base * 1.25); // ~18px 55 | @font-size-h5: @font-size-base; 56 | @font-size-h6: ceil(@font-size-base * 0.85); // ~12px 57 | 58 | @line-height-base: 1.428571429; // 20/14 59 | @line-height-computed: floor(@font-size-base * @line-height-base); // ~20px 60 | 61 | @headings-font-family: "Josefin Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 62 | @headings-font-weight: 500; 63 | @headings-line-height: 1.1; 64 | @headings-color: inherit; 65 | 66 | 67 | // Iconography 68 | // ------------------------- 69 | 70 | @icon-font-path: "../fonts/"; 71 | @icon-font-name: "glyphicons-halflings-regular"; 72 | 73 | 74 | // Components 75 | // ------------------------- 76 | // Based on 14px font-size and 1.428 line-height (~20px to start) 77 | 78 | @padding-base-vertical: 8px; 79 | @padding-base-horizontal: 12px; 80 | 81 | @padding-large-vertical: 14px; 82 | @padding-large-horizontal: 16px; 83 | 84 | @padding-small-vertical: 5px; 85 | @padding-small-horizontal: 10px; 86 | 87 | @padding-xs-vertical: 1px; 88 | @padding-xs-horizontal: 5px; 89 | 90 | @line-height-large: 1.33; 91 | @line-height-small: 1.5; 92 | 93 | @border-radius-base: 4px; 94 | @border-radius-large: 6px; 95 | @border-radius-small: 3px; 96 | 97 | @component-active-color: #fff; 98 | @component-active-bg: @brand-primary; 99 | 100 | @caret-width-base: 4px; 101 | @caret-width-large: 5px; 102 | 103 | // Tables 104 | // ------------------------- 105 | 106 | @table-cell-padding: 8px; 107 | @table-condensed-cell-padding: 5px; 108 | 109 | @table-bg: transparent; // overall background-color 110 | @table-bg-accent: #f9f9f9; // for striping 111 | @table-bg-hover: #f5f5f5; 112 | @table-bg-active: @table-bg-hover; 113 | 114 | @table-border-color: #ddd; // table and cell border 115 | 116 | 117 | // Buttons 118 | // ------------------------- 119 | 120 | @btn-font-weight: normal; 121 | 122 | @btn-default-color: #fff; 123 | @btn-default-bg: #474949; 124 | @btn-default-border: @btn-default-bg; 125 | 126 | @btn-primary-color: @btn-default-color; 127 | @btn-primary-bg: @brand-primary; 128 | @btn-primary-border: @btn-primary-bg; 129 | 130 | @btn-success-color: @btn-default-color; 131 | @btn-success-bg: @brand-success; 132 | @btn-success-border: @btn-success-bg; 133 | 134 | @btn-warning-color: @btn-default-color; 135 | @btn-warning-bg: @brand-warning; 136 | @btn-warning-border: @btn-warning-bg; 137 | 138 | @btn-danger-color: @btn-default-color; 139 | @btn-danger-bg: @brand-danger; 140 | @btn-danger-border: @btn-danger-bg; 141 | 142 | @btn-info-color: @btn-default-color; 143 | @btn-info-bg: @brand-info; 144 | @btn-info-border: @btn-info-bg; 145 | 146 | @btn-link-disabled-color: @gray-light; 147 | 148 | 149 | // Forms 150 | // ------------------------- 151 | 152 | @input-bg: #fff; 153 | @input-bg-disabled: @gray-lighter; 154 | 155 | @input-color: @text-color; 156 | @input-border: #ccc; 157 | @input-border-radius: @border-radius-base; 158 | @input-border-focus: #66afe9; 159 | 160 | @input-color-placeholder: @gray-light; 161 | 162 | @input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); 163 | @input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); 164 | @input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); 165 | 166 | @legend-color: @text-color; 167 | @legend-border-color: #e5e5e5; 168 | 169 | @input-group-addon-bg: @gray-lighter; 170 | @input-group-addon-border-color: @input-border; 171 | 172 | 173 | // Dropdowns 174 | // ------------------------- 175 | 176 | @dropdown-bg: #fff; 177 | @dropdown-border: rgba(0,0,0,.15); 178 | @dropdown-fallback-border: #ccc; 179 | @dropdown-divider-bg: #e5e5e5; 180 | 181 | @dropdown-link-color: @gray-dark; 182 | @dropdown-link-hover-color: #fff; 183 | @dropdown-link-hover-bg: @dropdown-link-active-bg; 184 | 185 | @dropdown-link-active-color: #fff; 186 | @dropdown-link-active-bg: @component-active-bg; 187 | 188 | @dropdown-link-disabled-color: @text-muted; 189 | 190 | @dropdown-header-color: @text-muted; 191 | 192 | 193 | // COMPONENT VARIABLES 194 | // -------------------------------------------------- 195 | 196 | 197 | // Z-index master list 198 | // ------------------------- 199 | // Used for a bird's eye view of components dependent on the z-axis 200 | // Try to avoid customizing these :) 201 | 202 | @zindex-navbar: 1000; 203 | @zindex-dropdown: 1000; 204 | @zindex-popover: 1010; 205 | @zindex-tooltip: 1030; 206 | @zindex-navbar-fixed: 1030; 207 | @zindex-modal-background: 1040; 208 | @zindex-modal: 1050; 209 | 210 | // Media queries breakpoints 211 | // -------------------------------------------------- 212 | 213 | // Extra small screen / phone 214 | // Note: Deprecated @screen-xs and @screen-phone as of v3.0.1 215 | @screen-xs: 480px; 216 | @screen-xs-min: @screen-xs; 217 | @screen-phone: @screen-xs-min; 218 | 219 | // Small screen / tablet 220 | // Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1 221 | @screen-sm: 768px; 222 | @screen-sm-min: @screen-sm; 223 | @screen-tablet: @screen-sm-min; 224 | 225 | // Medium screen / desktop 226 | // Note: Deprecated @screen-md and @screen-desktop as of v3.0.1 227 | @screen-md: 992px; 228 | @screen-md-min: @screen-md; 229 | @screen-desktop: @screen-md-min; 230 | 231 | // Large screen / wide desktop 232 | // Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1 233 | @screen-lg: 1200px; 234 | @screen-lg-min: @screen-lg; 235 | @screen-lg-desktop: @screen-lg-min; 236 | 237 | // So media queries don't overlap when required, provide a maximum 238 | @screen-xs-max: (@screen-sm-min - 1); 239 | @screen-sm-max: (@screen-md-min - 1); 240 | @screen-md-max: (@screen-lg-min - 1); 241 | 242 | 243 | // Grid system 244 | // -------------------------------------------------- 245 | 246 | // Number of columns in the grid system 247 | @grid-columns: 12; 248 | // Padding, to be divided by two and applied to the left and right of all columns 249 | @grid-gutter-width: 30px; 250 | 251 | // Navbar collapse 252 | 253 | // Point at which the navbar becomes uncollapsed 254 | @grid-float-breakpoint: @screen-sm-min; 255 | // Point at which the navbar begins collapsing 256 | @grid-float-breakpoint-max: (@grid-float-breakpoint - 1); 257 | 258 | 259 | // Navbar 260 | // ------------------------- 261 | 262 | // Basics of a navbar 263 | @navbar-height: 40px; 264 | @navbar-margin-bottom: @line-height-computed; 265 | @navbar-border-radius: @border-radius-base; 266 | @navbar-padding-horizontal: floor(@grid-gutter-width / 2); // ~15px 267 | @navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2); 268 | 269 | @navbar-default-color: @gray-dark; 270 | @navbar-default-bg: #fff; 271 | @navbar-default-border: darken(@navbar-default-bg, 6.5%); 272 | 273 | // Navbar links 274 | @navbar-default-link-color: @gray-dark; 275 | @navbar-default-link-hover-color: @brand-primary; 276 | @navbar-default-link-hover-bg: transparent; 277 | @navbar-default-link-active-color: @navbar-default-link-hover-color; 278 | @navbar-default-link-active-bg: transparent; 279 | @navbar-default-link-disabled-color: #444; 280 | @navbar-default-link-disabled-bg: transparent; 281 | 282 | // Navbar brand label 283 | @navbar-default-brand-color: @navbar-default-link-color; 284 | @navbar-default-brand-hover-color: @navbar-default-link-hover-color; 285 | @navbar-default-brand-hover-bg: transparent; 286 | 287 | // Navbar toggle 288 | @navbar-default-toggle-hover-bg: #ddd; 289 | @navbar-default-toggle-icon-bar-bg: #ccc; 290 | @navbar-default-toggle-border-color: #ddd; 291 | 292 | 293 | // Inverted navbar 294 | // 295 | // Reset inverted navbar basics 296 | @navbar-inverse-color: #fff; 297 | @navbar-inverse-bg: @brand-primary; 298 | @navbar-inverse-border: darken(@navbar-inverse-bg, 10%); 299 | 300 | // Inverted navbar links 301 | @navbar-inverse-link-color: #fff; 302 | @navbar-inverse-link-hover-color: lighten(@brand-primary, 40%); 303 | @navbar-inverse-link-hover-bg: transparent; 304 | @navbar-inverse-link-active-color: @navbar-inverse-link-hover-color; 305 | @navbar-inverse-link-active-bg: transparent; 306 | @navbar-inverse-link-disabled-color: #ccc; 307 | @navbar-inverse-link-disabled-bg: transparent; 308 | 309 | // Inverted navbar brand label 310 | @navbar-inverse-brand-color: @navbar-inverse-link-color; 311 | @navbar-inverse-brand-hover-color: #fff; 312 | @navbar-inverse-brand-hover-bg: transparent; 313 | 314 | // Inverted navbar toggle 315 | @navbar-inverse-toggle-hover-bg: darken(@navbar-inverse-bg, 10%); 316 | @navbar-inverse-toggle-icon-bar-bg: #fff; 317 | @navbar-inverse-toggle-border-color: darken(@navbar-inverse-bg, 10%); 318 | 319 | 320 | // Navs 321 | // ------------------------- 322 | 323 | @nav-link-padding: 10px 15px; 324 | @nav-link-hover-bg: @gray-lighter; 325 | 326 | @nav-disabled-link-color: @gray-light; 327 | @nav-disabled-link-hover-color: @gray-light; 328 | 329 | @nav-open-link-hover-color: #fff; 330 | 331 | // Tabs 332 | @nav-tabs-border-color: #ddd; 333 | 334 | @nav-tabs-link-hover-border-color: @gray-lighter; 335 | 336 | @nav-tabs-active-link-hover-bg: @body-bg; 337 | @nav-tabs-active-link-hover-color: @gray; 338 | @nav-tabs-active-link-hover-border-color: #ddd; 339 | 340 | @nav-tabs-justified-link-border-color: #ddd; 341 | @nav-tabs-justified-active-link-border-color: @body-bg; 342 | 343 | // Pills 344 | @nav-pills-border-radius: @border-radius-base; 345 | @nav-pills-active-link-hover-bg: @component-active-bg; 346 | @nav-pills-active-link-hover-color: @component-active-color; 347 | 348 | 349 | // Pagination 350 | // ------------------------- 351 | 352 | @pagination-bg: #fff; 353 | @pagination-border: #ddd; 354 | 355 | @pagination-hover-bg: @gray-lighter; 356 | 357 | @pagination-active-bg: #f5f5f5; 358 | @pagination-active-color: @gray-light; 359 | 360 | @pagination-disabled-color: @gray-light; 361 | 362 | 363 | // Pager 364 | // ------------------------- 365 | 366 | @pager-border-radius: 15px; 367 | @pager-disabled-color: @gray-light; 368 | 369 | 370 | // Jumbotron 371 | // ------------------------- 372 | 373 | @jumbotron-padding: 30px; 374 | @jumbotron-color: inherit; 375 | @jumbotron-bg: darken(@body-bg, 3%); 376 | @jumbotron-heading-color: inherit; 377 | @jumbotron-font-size: ceil(@font-size-base * 1.5); 378 | 379 | 380 | // Form states and alerts 381 | // ------------------------- 382 | 383 | @state-success-text: #468847; 384 | @state-success-bg: #dff0d8; 385 | @state-success-border: darken(spin(@state-success-bg, -10), 5%); 386 | 387 | @state-info-text: #3a87ad; 388 | @state-info-bg: #d9edf7; 389 | @state-info-border: darken(spin(@state-info-bg, -10), 7%); 390 | 391 | @state-warning-text: #c09853; 392 | @state-warning-bg: #fcf8e3; 393 | @state-warning-border: darken(spin(@state-warning-bg, -10), 3%); 394 | 395 | @state-danger-text: #b94a48; 396 | @state-danger-bg: #f2dede; 397 | @state-danger-border: darken(spin(@state-danger-bg, -10), 3%); 398 | 399 | 400 | // Tooltips 401 | // ------------------------- 402 | @tooltip-max-width: 200px; 403 | @tooltip-color: #fff; 404 | @tooltip-bg: rgba(0,0,0,.9); 405 | 406 | @tooltip-arrow-width: 5px; 407 | @tooltip-arrow-color: @tooltip-bg; 408 | 409 | 410 | // Popovers 411 | // ------------------------- 412 | @popover-bg: #fff; 413 | @popover-max-width: 276px; 414 | @popover-border-color: rgba(0,0,0,.2); 415 | @popover-fallback-border-color: #ccc; 416 | 417 | @popover-title-bg: darken(@popover-bg, 3%); 418 | 419 | @popover-arrow-width: 10px; 420 | @popover-arrow-color: #fff; 421 | 422 | @popover-arrow-outer-width: (@popover-arrow-width + 1); 423 | @popover-arrow-outer-color: rgba(0,0,0,.25); 424 | @popover-arrow-outer-fallback-color: #999; 425 | 426 | 427 | // Labels 428 | // ------------------------- 429 | 430 | @label-default-bg: @btn-default-bg; 431 | @label-primary-bg: @brand-primary; 432 | @label-success-bg: @brand-success; 433 | @label-info-bg: @brand-info; 434 | @label-warning-bg: @brand-warning; 435 | @label-danger-bg: @brand-danger; 436 | 437 | @label-color: #fff; 438 | @label-link-hover-color: #fff; 439 | 440 | 441 | // Modals 442 | // ------------------------- 443 | @modal-inner-padding: 20px; 444 | 445 | @modal-title-padding: 15px; 446 | @modal-title-line-height: @line-height-base; 447 | 448 | @modal-content-bg: #fff; 449 | @modal-content-border-color: rgba(0,0,0,.2); 450 | @modal-content-fallback-border-color: #999; 451 | 452 | @modal-backdrop-bg: #000; 453 | @modal-header-border-color: #e5e5e5; 454 | @modal-footer-border-color: @modal-header-border-color; 455 | 456 | 457 | // Alerts 458 | // ------------------------- 459 | @alert-padding: 15px; 460 | @alert-border-radius: @border-radius-base; 461 | @alert-link-font-weight: bold; 462 | 463 | @alert-success-bg: @state-success-bg; 464 | @alert-success-text: @state-success-text; 465 | @alert-success-border: @state-success-border; 466 | 467 | @alert-info-bg: @state-info-bg; 468 | @alert-info-text: @state-info-text; 469 | @alert-info-border: @state-info-border; 470 | 471 | @alert-warning-bg: @state-warning-bg; 472 | @alert-warning-text: @state-warning-text; 473 | @alert-warning-border: @state-warning-border; 474 | 475 | @alert-danger-bg: @state-danger-bg; 476 | @alert-danger-text: @state-danger-text; 477 | @alert-danger-border: @state-danger-border; 478 | 479 | 480 | // Progress bars 481 | // ------------------------- 482 | @progress-bg: #f5f5f5; 483 | @progress-bar-color: #fff; 484 | 485 | @progress-bar-bg: @brand-primary; 486 | @progress-bar-success-bg: @brand-success; 487 | @progress-bar-warning-bg: @brand-warning; 488 | @progress-bar-danger-bg: @brand-danger; 489 | @progress-bar-info-bg: @brand-info; 490 | 491 | 492 | // List group 493 | // ------------------------- 494 | @list-group-bg: #fff; 495 | @list-group-border: #ddd; 496 | @list-group-border-radius: @border-radius-base; 497 | 498 | @list-group-hover-bg: #f5f5f5; 499 | @list-group-active-color: @component-active-color; 500 | @list-group-active-bg: @component-active-bg; 501 | @list-group-active-border: @list-group-active-bg; 502 | 503 | @list-group-link-color: #555; 504 | @list-group-link-heading-color: #333; 505 | 506 | 507 | // Panels 508 | // ------------------------- 509 | @panel-bg: #fff; 510 | @panel-inner-border: #ddd; 511 | @panel-border-radius: @border-radius-base; 512 | @panel-footer-bg: @panel-default-heading-bg; 513 | 514 | @panel-default-text: @gray-dark; 515 | @panel-default-border: #ddd; 516 | @panel-default-heading-bg: @body-bg; 517 | 518 | @panel-primary-text: #fff; 519 | @panel-primary-border: @brand-primary; 520 | @panel-primary-heading-bg: @brand-primary; 521 | 522 | @panel-success-text: @state-success-text; 523 | @panel-success-border: @state-success-border; 524 | @panel-success-heading-bg: @state-success-bg; 525 | 526 | @panel-warning-text: @state-warning-text; 527 | @panel-warning-border: @state-warning-border; 528 | @panel-warning-heading-bg: @state-warning-bg; 529 | 530 | @panel-danger-text: @state-danger-text; 531 | @panel-danger-border: @state-danger-border; 532 | @panel-danger-heading-bg: @state-danger-bg; 533 | 534 | @panel-info-text: @state-info-text; 535 | @panel-info-border: @state-info-border; 536 | @panel-info-heading-bg: @state-info-bg; 537 | 538 | 539 | // Thumbnails 540 | // ------------------------- 541 | @thumbnail-padding: 4px; 542 | @thumbnail-bg: @body-bg; 543 | @thumbnail-border: #ddd; 544 | @thumbnail-border-radius: @border-radius-base; 545 | 546 | @thumbnail-caption-color: @text-color; 547 | @thumbnail-caption-padding: 9px; 548 | 549 | 550 | // Wells 551 | // ------------------------- 552 | @well-bg: darken(@body-bg, 3%); 553 | 554 | 555 | // Badges 556 | // ------------------------- 557 | @badge-color: #fff; 558 | @badge-link-hover-color: #fff; 559 | @badge-bg: @gray-light; 560 | 561 | @badge-active-color: @link-color; 562 | @badge-active-bg: #fff; 563 | 564 | @badge-font-weight: bold; 565 | @badge-line-height: 1; 566 | @badge-border-radius: 10px; 567 | 568 | 569 | // Breadcrumbs 570 | // ------------------------- 571 | @breadcrumb-bg: transparent; 572 | @breadcrumb-color: #ccc; 573 | @breadcrumb-active-color: @gray-light; 574 | @breadcrumb-separator: "/"; 575 | 576 | 577 | // Carousel 578 | // ------------------------ 579 | 580 | @carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); 581 | 582 | @carousel-control-color: #fff; 583 | @carousel-control-width: 15%; 584 | @carousel-control-opacity: .5; 585 | @carousel-control-font-size: 20px; 586 | 587 | @carousel-indicator-active-bg: #fff; 588 | @carousel-indicator-border-color: #fff; 589 | 590 | @carousel-caption-color: #fff; 591 | 592 | 593 | // Close 594 | // ------------------------ 595 | @close-font-weight: bold; 596 | @close-color: #000; 597 | @close-text-shadow: 0 1px 0 #fff; 598 | 599 | 600 | // Code 601 | // ------------------------ 602 | @code-color: #c7254e; 603 | @code-bg: #f9f2f4; 604 | 605 | @pre-bg: #f5f5f5; 606 | @pre-color: @gray-dark; 607 | @pre-border-color: #ccc; 608 | @pre-scrollable-max-height: 340px; 609 | 610 | // Type 611 | // ------------------------ 612 | @text-muted: @gray-light; 613 | @abbr-border-color: @gray-light; 614 | @headings-small-color: @gray-light; 615 | @blockquote-small-color: @gray-light; 616 | @blockquote-border-color: @gray-lighter; 617 | @page-header-border-color: @gray-lighter; 618 | 619 | // Miscellaneous 620 | // ------------------------- 621 | 622 | // Hr border color 623 | @hr-border: @gray-lighter; 624 | 625 | // Horizontal forms & lists 626 | @component-offset-horizontal: 180px; 627 | 628 | 629 | // Container sizes 630 | // -------------------------------------------------- 631 | 632 | // Small screen / tablet 633 | @container-tablet: ((720px + @grid-gutter-width)); 634 | @container-sm: @container-tablet; 635 | 636 | // Medium screen / desktop 637 | @container-desktop: ((940px + @grid-gutter-width)); 638 | @container-md: @container-desktop; 639 | 640 | // Large screen / wide desktop 641 | @container-large-desktop: ((1140px + @grid-gutter-width)); 642 | @container-lg: @container-large-desktop; 643 | -------------------------------------------------------------------------------- /app/styles/themes/amelia.less: -------------------------------------------------------------------------------- 1 | // Amelia 3.0.3 2 | // Variables 3 | // -------------------------------------------------- 4 | 5 | 6 | // Global values 7 | // -------------------------------------------------- 8 | 9 | // Grays 10 | // ------------------------- 11 | 12 | @gray-darker: #111; 13 | @gray-dark: #444; 14 | @gray: lighten(#000, 33.5%); // #555 15 | @gray-light: #bbb; 16 | @gray-lighter: #ddd; 17 | 18 | // Brand colors 19 | // ------------------------- 20 | 21 | @brand-primary: #AD1D28; 22 | @brand-success: #48CA3B; 23 | @brand-warning: #DEBB27; 24 | @brand-danger: #DF6E1E; 25 | @brand-info: #4D3A7D; 26 | 27 | // Scaffolding 28 | // ------------------------- 29 | 30 | @body-bg: #108A93; 31 | @text-color: #fff; 32 | 33 | // Links 34 | // ------------------------- 35 | 36 | @link-color: lighten(@brand-warning, 15%); 37 | @link-hover-color: @brand-warning; 38 | 39 | // Typography 40 | // ------------------------- 41 | 42 | @font-family-sans-serif: "Cabin", Arial, sans-serif; 43 | @font-family-serif: Georgia, "Times New Roman", Times, serif; 44 | @font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; 45 | @font-family-base: @font-family-sans-serif; 46 | 47 | @font-size-base: 14px; 48 | @font-size-large: ceil(@font-size-base * 1.25); // ~18px 49 | @font-size-small: ceil(@font-size-base * 0.85); // ~12px 50 | 51 | @font-size-h1: floor(@font-size-base * 2.6); // ~36px 52 | @font-size-h2: floor(@font-size-base * 2.15); // ~30px 53 | @font-size-h3: ceil(@font-size-base * 1.7); // ~24px 54 | @font-size-h4: ceil(@font-size-base * 1.25); // ~18px 55 | @font-size-h5: @font-size-base; 56 | @font-size-h6: ceil(@font-size-base * 0.85); // ~12px 57 | 58 | @line-height-base: 1.428571429; // 20/14 59 | @line-height-computed: floor(@font-size-base * @line-height-base); // ~20px 60 | 61 | @headings-font-family: 'Lobster', cursive; 62 | @headings-font-weight: 500; 63 | @headings-line-height: 1.1; 64 | @headings-color: inherit; 65 | 66 | 67 | // Iconography 68 | // ------------------------- 69 | 70 | @icon-font-path: "../fonts/"; 71 | @icon-font-name: "glyphicons-halflings-regular"; 72 | 73 | 74 | // Components 75 | // ------------------------- 76 | // Based on 14px font-size and 1.428 line-height (~20px to start) 77 | 78 | @padding-base-vertical: 8px; 79 | @padding-base-horizontal: 12px; 80 | 81 | @padding-large-vertical: 14px; 82 | @padding-large-horizontal: 16px; 83 | 84 | @padding-small-vertical: 5px; 85 | @padding-small-horizontal: 10px; 86 | 87 | @padding-xs-vertical: 1px; 88 | @padding-xs-horizontal: 5px; 89 | 90 | @line-height-large: 1.33; 91 | @line-height-small: 1.5; 92 | 93 | @border-radius-base: 4px; 94 | @border-radius-large: 6px; 95 | @border-radius-small: 3px; 96 | 97 | @component-active-color: #fff; 98 | @component-active-bg: lighten(@body-bg, 10%); 99 | 100 | @caret-width-base: 4px; 101 | @caret-width-large: 5px; 102 | 103 | // Tables 104 | // ------------------------- 105 | 106 | @table-cell-padding: 8px; 107 | @table-condensed-cell-padding: 5px; 108 | 109 | @table-bg: transparent; // overall background-color 110 | @table-bg-accent: darken(@body-bg, 2.5%); // for striping 111 | @table-bg-hover: lighten(@body-bg, 5%); 112 | @table-bg-active: darken(@body-bg, 5%); 113 | 114 | @table-border-color: darken(@body-bg, 5%); // table and cell border 115 | 116 | 117 | // Buttons 118 | // ------------------------- 119 | 120 | @btn-font-weight: normal; 121 | 122 | @btn-default-color: @gray-dark; 123 | @btn-default-bg: @gray-lighter; 124 | @btn-default-border: @btn-default-bg; 125 | 126 | @btn-primary-color: #fff; 127 | @btn-primary-bg: @brand-primary; 128 | @btn-primary-border: @btn-primary-bg; 129 | 130 | @btn-success-color: @btn-primary-color; 131 | @btn-success-bg: @brand-success; 132 | @btn-success-border: @btn-success-bg; 133 | 134 | @btn-warning-color: @btn-primary-color; 135 | @btn-warning-bg: @brand-warning; 136 | @btn-warning-border: @btn-warning-bg; 137 | 138 | @btn-danger-color: @btn-primary-color; 139 | @btn-danger-bg: @brand-danger; 140 | @btn-danger-border: @btn-danger-bg; 141 | 142 | @btn-info-color: @btn-primary-color; 143 | @btn-info-bg: @brand-info; 144 | @btn-info-border: @btn-info-bg; 145 | 146 | @btn-link-disabled-color: @gray-light; 147 | 148 | 149 | // Forms 150 | // ------------------------- 151 | 152 | @input-bg: #fff; 153 | @input-bg-disabled: @gray-lighter; 154 | 155 | @input-color: @gray-dark; 156 | @input-border: #ccc; 157 | @input-border-radius: @border-radius-base; 158 | @input-border-focus: #66afe9; 159 | 160 | @input-color-placeholder: @gray-light; 161 | 162 | @input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); 163 | @input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); 164 | @input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); 165 | 166 | @legend-color: @text-color; 167 | @legend-border-color: darken(@body-bg, 5%); 168 | 169 | @input-group-addon-bg: @gray-lighter; 170 | @input-group-addon-border-color: @input-border; 171 | 172 | 173 | // Dropdowns 174 | // ------------------------- 175 | 176 | @dropdown-bg: #fff; 177 | @dropdown-border: rgba(0,0,0,.15); 178 | @dropdown-fallback-border: #ccc; 179 | @dropdown-divider-bg: #e5e5e5; 180 | 181 | @dropdown-link-color: @gray-dark; 182 | @dropdown-link-hover-color: #fff; 183 | @dropdown-link-hover-bg: @dropdown-link-active-bg; 184 | 185 | @dropdown-link-active-color: #fff; 186 | @dropdown-link-active-bg: @component-active-bg; 187 | 188 | @dropdown-link-disabled-color: @gray-lighter; 189 | 190 | @dropdown-header-color: @gray-lighter; 191 | 192 | 193 | // COMPONENT VARIABLES 194 | // -------------------------------------------------- 195 | 196 | 197 | // Z-index master list 198 | // ------------------------- 199 | // Used for a bird's eye view of components dependent on the z-axis 200 | // Try to avoid customizing these :) 201 | 202 | @zindex-navbar: 1000; 203 | @zindex-dropdown: 1000; 204 | @zindex-popover: 1010; 205 | @zindex-tooltip: 1030; 206 | @zindex-navbar-fixed: 1030; 207 | @zindex-modal-background: 1040; 208 | @zindex-modal: 1050; 209 | 210 | // Media queries breakpoints 211 | // -------------------------------------------------- 212 | 213 | // Extra small screen / phone 214 | // Note: Deprecated @screen-xs and @screen-phone as of v3.0.1 215 | @screen-xs: 480px; 216 | @screen-xs-min: @screen-xs; 217 | @screen-phone: @screen-xs-min; 218 | 219 | // Small screen / tablet 220 | // Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1 221 | @screen-sm: 768px; 222 | @screen-sm-min: @screen-sm; 223 | @screen-tablet: @screen-sm-min; 224 | 225 | // Medium screen / desktop 226 | // Note: Deprecated @screen-md and @screen-desktop as of v3.0.1 227 | @screen-md: 992px; 228 | @screen-md-min: @screen-md; 229 | @screen-desktop: @screen-md-min; 230 | 231 | // Large screen / wide desktop 232 | // Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1 233 | @screen-lg: 1200px; 234 | @screen-lg-min: @screen-lg; 235 | @screen-lg-desktop: @screen-lg-min; 236 | 237 | // So media queries don't overlap when required, provide a maximum 238 | @screen-xs-max: (@screen-sm-min - 1); 239 | @screen-sm-max: (@screen-md-min - 1); 240 | @screen-md-max: (@screen-lg-min - 1); 241 | 242 | 243 | // Grid system 244 | // -------------------------------------------------- 245 | 246 | // Number of columns in the grid system 247 | @grid-columns: 12; 248 | // Padding, to be divided by two and applied to the left and right of all columns 249 | @grid-gutter-width: 30px; 250 | 251 | // Navbar collapse 252 | 253 | // Point at which the navbar becomes uncollapsed 254 | @grid-float-breakpoint: @screen-sm-min; 255 | // Point at which the navbar begins collapsing 256 | @grid-float-breakpoint-max: (@grid-float-breakpoint - 1); 257 | 258 | 259 | // Navbar 260 | // ------------------------- 261 | 262 | // Basics of a navbar 263 | @navbar-height: 50px; 264 | @navbar-margin-bottom: @line-height-computed; 265 | @navbar-border-radius: @border-radius-base; 266 | @navbar-padding-horizontal: floor(@grid-gutter-width / 2); 267 | @navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2); 268 | 269 | @navbar-default-color: @gray-light; 270 | @navbar-default-bg: @brand-primary; 271 | @navbar-default-border: darken(@navbar-default-bg, 6.5%); 272 | 273 | // Navbar links 274 | @navbar-default-link-color: #fff; 275 | @navbar-default-link-hover-color: #fff; 276 | @navbar-default-link-hover-bg: lighten(@navbar-default-bg, 10%); 277 | @navbar-default-link-active-color: #fff; 278 | @navbar-default-link-active-bg: @navbar-default-link-hover-bg; 279 | @navbar-default-link-disabled-color: #ccc; 280 | @navbar-default-link-disabled-bg: transparent; 281 | 282 | // Navbar brand label 283 | @navbar-default-brand-color: @navbar-default-link-color; 284 | @navbar-default-brand-hover-color: @navbar-default-brand-color; 285 | @navbar-default-brand-hover-bg: none; 286 | 287 | // Navbar toggle 288 | @navbar-default-toggle-hover-bg: @navbar-default-link-hover-bg; 289 | @navbar-default-toggle-icon-bar-bg: #fff; 290 | @navbar-default-toggle-border-color: @navbar-default-link-hover-bg; 291 | 292 | 293 | // Inverted navbar 294 | // 295 | // Reset inverted navbar basics 296 | @navbar-inverse-color: @gray-light; 297 | @navbar-inverse-bg: @brand-warning; 298 | @navbar-inverse-border: darken(@navbar-inverse-bg, 10%); 299 | 300 | // Inverted navbar links 301 | @navbar-inverse-link-color: #fff; 302 | @navbar-inverse-link-hover-color: #fff; 303 | @navbar-inverse-link-hover-bg: lighten(@navbar-inverse-bg, 10%); 304 | @navbar-inverse-link-active-color: #fff; 305 | @navbar-inverse-link-active-bg: @navbar-inverse-link-hover-bg; 306 | @navbar-inverse-link-disabled-color: #444; 307 | @navbar-inverse-link-disabled-bg: transparent; 308 | 309 | // Inverted navbar brand label 310 | @navbar-inverse-brand-color: @navbar-inverse-link-color; 311 | @navbar-inverse-brand-hover-color: #fff; 312 | @navbar-inverse-brand-hover-bg: none; 313 | 314 | // Inverted navbar toggle 315 | @navbar-inverse-toggle-hover-bg: @navbar-inverse-link-hover-bg; 316 | @navbar-inverse-toggle-icon-bar-bg: #fff; 317 | @navbar-inverse-toggle-border-color: @navbar-inverse-link-hover-bg; 318 | 319 | 320 | // Navs 321 | // ------------------------- 322 | 323 | @nav-link-padding: 10px 15px; 324 | @nav-link-hover-bg: lighten(@body-bg, 10%); 325 | 326 | @nav-disabled-link-color: @gray-lighter; 327 | @nav-disabled-link-hover-color: @gray-lighter; 328 | 329 | @nav-open-link-hover-color: @nav-link-hover-bg; 330 | 331 | // Tabs 332 | @nav-tabs-border-color: @nav-link-hover-bg; 333 | 334 | @nav-tabs-link-hover-border-color: transparent; 335 | 336 | @nav-tabs-active-link-hover-bg: @nav-link-hover-bg; 337 | @nav-tabs-active-link-hover-color: #fff; 338 | @nav-tabs-active-link-hover-border-color: transparent; 339 | 340 | @nav-tabs-justified-link-border-color: transparent; 341 | @nav-tabs-justified-active-link-border-color: transparent; 342 | 343 | // Pills 344 | @nav-pills-border-radius: @border-radius-base; 345 | @nav-pills-active-link-hover-bg: @component-active-bg; 346 | @nav-pills-active-link-hover-color: #fff; 347 | 348 | 349 | // Pagination 350 | // ------------------------- 351 | 352 | @pagination-bg: lighten(@body-bg, 5%); 353 | @pagination-border: transparent; 354 | 355 | @pagination-hover-bg: @component-active-bg; 356 | 357 | @pagination-active-bg: lighten(@body-bg, 10%); 358 | @pagination-active-color: #fff; 359 | 360 | @pagination-disabled-color: #fff; 361 | 362 | 363 | // Pager 364 | // ------------------------- 365 | 366 | @pager-border-radius: 15px; 367 | @pager-disabled-color: @gray-lighter; 368 | 369 | 370 | // Jumbotron 371 | // ------------------------- 372 | 373 | @jumbotron-padding: 30px; 374 | @jumbotron-color: inherit; 375 | @jumbotron-bg: darken(@body-bg, 5%); 376 | @jumbotron-heading-color: inherit; 377 | @jumbotron-font-size: ceil(@font-size-base * 1.5); 378 | 379 | 380 | // Form states and alerts 381 | // ------------------------- 382 | 383 | @state-success-text: #fff; 384 | @state-success-bg: @brand-success; 385 | @state-success-border: darken(spin(@state-success-bg, -10), 5%); 386 | 387 | @state-info-text: #fff; 388 | @state-info-bg: @brand-info; 389 | @state-info-border: darken(spin(@state-info-bg, -10), 7%); 390 | 391 | @state-warning-text: #fff; 392 | @state-warning-bg: @brand-warning; 393 | @state-warning-border: darken(spin(@state-warning-bg, -10), 3%); 394 | 395 | @state-danger-text: #fff; 396 | @state-danger-bg: @brand-danger; 397 | @state-danger-border: darken(spin(@state-danger-bg, -10), 3%); 398 | 399 | 400 | // Tooltips 401 | // ------------------------- 402 | @tooltip-max-width: 200px; 403 | @tooltip-color: #fff; 404 | @tooltip-bg: rgba(0,0,0,.9); 405 | 406 | @tooltip-arrow-width: 5px; 407 | @tooltip-arrow-color: @tooltip-bg; 408 | 409 | 410 | // Popovers 411 | // ------------------------- 412 | @popover-bg: #fff; 413 | @popover-max-width: 276px; 414 | @popover-border-color: rgba(0,0,0,.2); 415 | @popover-fallback-border-color: #ccc; 416 | 417 | @popover-title-bg: darken(@popover-bg, 3%); 418 | 419 | @popover-arrow-width: 10px; 420 | @popover-arrow-color: #fff; 421 | 422 | @popover-arrow-outer-width: (@popover-arrow-width + 1); 423 | @popover-arrow-outer-color: rgba(0,0,0,.25); 424 | @popover-arrow-outer-fallback-color: #999; 425 | 426 | 427 | // Labels 428 | // ------------------------- 429 | 430 | @label-default-bg: @btn-default-bg; 431 | @label-primary-bg: @brand-primary; 432 | @label-success-bg: @brand-success; 433 | @label-info-bg: @brand-info; 434 | @label-warning-bg: @brand-warning; 435 | @label-danger-bg: @brand-danger; 436 | 437 | @label-color: #fff; 438 | @label-link-hover-color: #fff; 439 | 440 | 441 | // Modals 442 | // ------------------------- 443 | @modal-inner-padding: 20px; 444 | 445 | @modal-title-padding: 15px; 446 | @modal-title-line-height: @line-height-base; 447 | 448 | @modal-content-bg: lighten(@body-bg, 5%); 449 | @modal-content-border-color: rgba(0,0,0,.2); 450 | @modal-content-fallback-border-color: #999; 451 | 452 | @modal-backdrop-bg: #000; 453 | @modal-header-border-color: darken(@body-bg, 5%); 454 | @modal-footer-border-color: @modal-header-border-color; 455 | 456 | 457 | // Alerts 458 | // ------------------------- 459 | @alert-padding: 15px; 460 | @alert-border-radius: @border-radius-base; 461 | @alert-link-font-weight: bold; 462 | 463 | @alert-success-bg: @state-success-bg; 464 | @alert-success-text: @state-success-text; 465 | @alert-success-border: @state-success-border; 466 | 467 | @alert-info-bg: @state-info-bg; 468 | @alert-info-text: @state-info-text; 469 | @alert-info-border: @state-info-border; 470 | 471 | @alert-warning-bg: @state-warning-bg; 472 | @alert-warning-text: @state-warning-text; 473 | @alert-warning-border: @state-warning-border; 474 | 475 | @alert-danger-bg: @state-danger-bg; 476 | @alert-danger-text: @state-danger-text; 477 | @alert-danger-border: @state-danger-border; 478 | 479 | 480 | // Progress bars 481 | // ------------------------- 482 | @progress-bg: darken(@body-bg, 5%); 483 | @progress-bar-color: #fff; 484 | 485 | @progress-bar-bg: @brand-primary; 486 | @progress-bar-success-bg: @brand-success; 487 | @progress-bar-warning-bg: @brand-warning; 488 | @progress-bar-danger-bg: @brand-danger; 489 | @progress-bar-info-bg: @brand-info; 490 | 491 | 492 | // List group 493 | // ------------------------- 494 | @list-group-bg: transparent; 495 | @list-group-border: darken(@body-bg, 5%); 496 | @list-group-border-radius: @border-radius-base; 497 | 498 | @list-group-hover-bg: lighten(@body-bg, 10%); 499 | @list-group-active-color: @component-active-color; 500 | @list-group-active-bg: @component-active-bg; 501 | @list-group-active-border: @list-group-border; 502 | 503 | @list-group-link-color: @link-color; 504 | @list-group-link-heading-color: #fff; 505 | 506 | 507 | // Panels 508 | // ------------------------- 509 | @panel-bg: lighten(@body-bg, 5%); 510 | @panel-inner-border: darken(@body-bg, 5%); 511 | @panel-border-radius: @border-radius-base; 512 | @panel-footer-bg: @panel-default-heading-bg; 513 | 514 | @panel-default-text: #fff; 515 | @panel-default-border: darken(@body-bg, 5%); 516 | @panel-default-heading-bg: lighten(@body-bg, 15%); 517 | 518 | @panel-primary-text: #fff; 519 | @panel-primary-border: @brand-primary; 520 | @panel-primary-heading-bg: @brand-primary; 521 | 522 | @panel-success-text: @state-success-text; 523 | @panel-success-border: @state-success-border; 524 | @panel-success-heading-bg: @state-success-bg; 525 | 526 | @panel-warning-text: @state-warning-text; 527 | @panel-warning-border: @state-warning-border; 528 | @panel-warning-heading-bg: @state-warning-bg; 529 | 530 | @panel-danger-text: @state-danger-text; 531 | @panel-danger-border: @state-danger-border; 532 | @panel-danger-heading-bg: @state-danger-bg; 533 | 534 | @panel-info-text: @state-info-text; 535 | @panel-info-border: @state-info-border; 536 | @panel-info-heading-bg: @state-info-bg; 537 | 538 | 539 | // Thumbnails 540 | // ------------------------- 541 | @thumbnail-padding: 4px; 542 | @thumbnail-bg: @body-bg; 543 | @thumbnail-border: #ddd; 544 | @thumbnail-border-radius: @border-radius-base; 545 | 546 | @thumbnail-caption-color: @text-color; 547 | @thumbnail-caption-padding: 9px; 548 | 549 | 550 | // Wells 551 | // ------------------------- 552 | @well-bg: darken(@body-bg, 5%); 553 | 554 | 555 | // Badges 556 | // ------------------------- 557 | @badge-color: #fff; 558 | @badge-link-hover-color: #fff; 559 | @badge-bg: @brand-primary; 560 | 561 | @badge-active-color: #fff; 562 | @badge-active-bg: @brand-primary; 563 | 564 | @badge-font-weight: bold; 565 | @badge-line-height: 1; 566 | @badge-border-radius: 10px; 567 | 568 | 569 | // Breadcrumbs 570 | // ------------------------- 571 | @breadcrumb-bg: lighten(@body-bg, 5%); 572 | @breadcrumb-color: @gray-lighter; 573 | @breadcrumb-active-color: #fff; 574 | @breadcrumb-separator: "/"; 575 | 576 | 577 | // Carousel 578 | // ------------------------ 579 | 580 | @carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); 581 | 582 | @carousel-control-color: #fff; 583 | @carousel-control-width: 15%; 584 | @carousel-control-opacity: .5; 585 | @carousel-control-font-size: 20px; 586 | 587 | @carousel-indicator-active-bg: #fff; 588 | @carousel-indicator-border-color: #fff; 589 | 590 | @carousel-caption-color: #fff; 591 | 592 | 593 | // Close 594 | // ------------------------ 595 | @close-font-weight: bold; 596 | @close-color: #000; 597 | @close-text-shadow: 0 1px 0 #fff; 598 | 599 | 600 | // Code 601 | // ------------------------ 602 | @code-color: #c7254e; 603 | @code-bg: #f9f2f4; 604 | 605 | @pre-bg: #f5f5f5; 606 | @pre-color: @gray-dark; 607 | @pre-border-color: #ccc; 608 | @pre-scrollable-max-height: 340px; 609 | 610 | // Type 611 | // ------------------------ 612 | @text-muted: rgba(255, 255, 255, 0.6); 613 | @abbr-border-color: @gray-light; 614 | @headings-small-color: @gray-light; 615 | @blockquote-small-color: rgba(255, 255, 255, 0.6); 616 | @blockquote-border-color: rgba(255, 255, 255, 0.6); 617 | @page-header-border-color: darken(@body-bg, 5%); 618 | 619 | // Miscellaneous 620 | // ------------------------- 621 | 622 | // Hr border color 623 | @hr-border: darken(@body-bg, 5%); 624 | 625 | // Horizontal forms & lists 626 | @component-offset-horizontal: 180px; 627 | 628 | 629 | // Container sizes 630 | // -------------------------------------------------- 631 | 632 | // Small screen / tablet 633 | @container-tablet: ((720px + @grid-gutter-width)); 634 | @container-sm: @container-tablet; 635 | 636 | // Medium screen / desktop 637 | @container-desktop: ((940px + @grid-gutter-width)); 638 | @container-md: @container-desktop; 639 | 640 | // Large screen / wide desktop 641 | @container-large-desktop: ((1140px + @grid-gutter-width)); 642 | @container-lg: @container-large-desktop; 643 | 644 | 645 | // ---------------------------------- 646 | // angular-dashboard variables 647 | // ---------------------------------- --------------------------------------------------------------------------------