├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── app ├── index.js └── templates │ ├── Gruntfile.js │ ├── _README.md │ ├── _bower.json │ ├── _package.json │ ├── assets │ ├── javascripts │ │ ├── application.coffee │ │ └── dashing.coffee │ └── stylesheets │ │ └── application.scss │ ├── bowerrc │ ├── dashboards │ ├── layout.gerb │ └── sample.gerb │ ├── gitattributes │ ├── gitignore │ ├── jobs │ ├── buzzwords.go │ ├── convergence.go │ └── sample.go │ ├── public │ ├── 404.html │ ├── favicon.ico │ └── images │ │ └── logo.png │ ├── server.go │ └── widgets │ ├── clock │ ├── clock.coffee │ ├── clock.html │ └── clock.scss │ ├── comments │ ├── comments.coffee │ ├── comments.html │ └── comments.scss │ ├── graph │ ├── graph.coffee │ ├── graph.html │ └── graph.scss │ ├── iframe │ ├── iframe.coffee │ ├── iframe.html │ └── iframe.scss │ ├── image │ ├── image.coffee │ ├── image.html │ └── image.scss │ ├── list │ ├── list.coffee │ ├── list.html │ └── list.scss │ ├── meter │ ├── meter.coffee │ ├── meter.html │ └── meter.scss │ ├── number │ ├── number.coffee │ ├── number.html │ └── number.scss │ └── text │ ├── text.coffee │ ├── text.html │ └── text.scss ├── dashboard ├── index.js └── templates │ └── dashboard.gerb ├── job ├── index.js └── templates │ └── _job.go ├── package.json ├── screenshot.png ├── test ├── test-creation.js └── test-load.js └── widget ├── index.js └── templates ├── _widget.coffee ├── _widget.scss └── widget.html /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*.js] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | temp/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "white": true 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Chris Heng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | generator-dashing-go 2 | ==================== 3 | 4 | This is a [Yeoman][1] generator for creating dashboards with the [dashing-go][2] library, a Go port of [Shopify/dashing][3]. 5 | 6 | The dashboard runs on the [Martini][4] microframework, while the frontend dependencies are managed by [Grunt][5] and [Bower][6]. 7 | 8 |  9 | 10 | For a live demo, check out the [sample dashboard][7]. 11 | 12 | ## Requirements 13 | 14 | * [Git][8] 15 | * [Node.js ~0.10][9] 16 | * [Go ~1.2][10] 17 | 18 | ## Installation 19 | 20 | Install Yeoman, and dependencies: 21 | 22 | npm install -g yo grunt grunt-cli bower 23 | 24 | Install the dashing-go generator: 25 | 26 | npm install -g generator-dashing-go 27 | 28 | ## Creating a project 29 | 30 | Create a new directory: 31 | 32 | mkdir my-dashboard && cd $_ 33 | 34 | Generate the project (this will also run the initial Bower and Grunt tasks): 35 | 36 | yo dashing-go 37 | 38 | Grab the Go dependencies: 39 | 40 | go get 41 | 42 | Start the server: 43 | 44 | go run server.go 45 | 46 | The sample dashboard is now available at [http://localhost:3000](http://localhost:3000). 47 | 48 | ## Asset pipeline 49 | 50 | The `init` task copies third party assets (installed by Bower) into position and compiles BatmanJS. This should have been run automatically as a post-install script, but if you've added new dependencies or need to do it manually: 51 | 52 | bower install 53 | grunt init 54 | 55 | ## Hot reloading 56 | 57 | You may also start a livereload server that watches the `assets` directory for changes and runs the build pipeline automatically. When you access it (port 9000 by default), it injects a script into the page that initiates a reload whenever the compiled assets are updated. 58 | 59 | grunt serve 60 | 61 | ## Building Stuff 62 | 63 | grunt build 64 | 65 | If you need to build just `application.js` or `application.css`, you may run each task individually: 66 | 67 | grunt js 68 | grunt css 69 | 70 | ## Minifying Stuff 71 | 72 | grunt minify 73 | 74 | `application.js` and `application.css` are minified in-place. 75 | 76 | ## Generating Stuff 77 | 78 | Create new dashboards, jobs and widgets: 79 | 80 | yo dashing-go:dashboard foo 81 | yo dashing-go:job foo 82 | yo dashing-go:widget foo 83 | 84 | Note the following naming conventions (the generator automatically enforces them): 85 | 86 | * dashed-slug for widget and dashboard names 87 | * under_score for Go job filenames 88 | * camelCase for Go job structs 89 | 90 | Existing third party Dashing widgets should be compatible with dashing-go. 91 | 92 | ## License 93 | 94 | Released under the MIT license. 95 | 96 | [1]: http://yeoman.io 97 | [2]: https://github.com/gigablah/dashing-go 98 | [3]: http://shopify.github.io/dashing 99 | [4]: http://martini.codegangsta.io 100 | [5]: http://gruntjs.com 101 | [6]: http://bower.io 102 | [7]: http://dashing.kuanyen.net 103 | [8]: http://git-scm.com 104 | [9]: http://nodejs.org 105 | [10]: http://golang.org 106 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var yeoman = require('yeoman-generator'); 5 | 6 | var DashingGoGenerator = yeoman.generators.Base.extend({ 7 | init: function () { 8 | this.pkg = require('../package.json'); 9 | 10 | this.on('end', function () { 11 | if (!this.options['skip-install']) { 12 | this.installDependencies(); 13 | } 14 | }); 15 | }, 16 | 17 | askFor: function () { 18 | var done = this.async(); 19 | 20 | // have Yeoman greet the user 21 | this.log(this.yeoman); 22 | 23 | this.log('\n' + 24 | " _ _ _ \n" + 25 | " __| | __ _ ___| |__ (_)_ __ __ _ __ _ ___ \n" + 26 | " / _` |/ _` / __| '_ \\| | '_ \\ / _` |___/ _` |/ _ \\ \n" + 27 | "| (_| | (_| \\__ \\ | | | | | | | (_| /__| (_| | (_) |\n" + 28 | " \\__,_|\\__,_|___/_| |_|_|_| |_|\\__, | \\__, |\\___/ \n" + 29 | " |___/ |___/ \n" + 30 | '\n'); 31 | 32 | var prompts = [{ 33 | name: 'appName', 34 | message: 'What would you like to call your project?', 35 | default: 'dashboard' 36 | }]; 37 | 38 | this.prompt(prompts, function (props) { 39 | this.appName = props.appName; 40 | 41 | done(); 42 | }.bind(this)); 43 | }, 44 | 45 | app: function () { 46 | this.directory('assets', 'assets'); 47 | this.directory('dashboards', 'dashboards'); 48 | this.directory('jobs', 'jobs'); 49 | this.directory('public', 'public'); 50 | this.directory('widgets', 'widgets'); 51 | 52 | this.copy('server.go', 'server.go'); 53 | 54 | this.mkdir('build'); 55 | this.mkdir('public/javascripts'); 56 | this.mkdir('public/stylesheets'); 57 | }, 58 | 59 | git: function () { 60 | this.copy('gitignore', '.gitignore'); 61 | this.copy('gitattributes', '.gitattributes'); 62 | }, 63 | 64 | bower: function () { 65 | this.copy('bowerrc', '.bowerrc'); 66 | this.template('_bower.json', 'bower.json'); 67 | }, 68 | 69 | package: function () { 70 | this.template('_package.json', 'package.json'); 71 | }, 72 | 73 | grunt: function () { 74 | this.copy('Gruntfile.js', 'Gruntfile.js'); 75 | } 76 | }); 77 | 78 | module.exports = DashingGoGenerator; 79 | -------------------------------------------------------------------------------- /app/templates/Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var LIVERELOAD_PORT = 35729; 4 | 5 | module.exports = function (grunt) { 6 | // Load all grunt tasks 7 | require('load-grunt-tasks')(grunt); 8 | // Show elapsed time at the end 9 | require('time-grunt')(grunt); 10 | 11 | // Project configuration. 12 | grunt.initConfig({ 13 | // Metadata. 14 | pkg: grunt.file.readJSON('package.json'), 15 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> */\n', 16 | dir: { 17 | asset: 'assets/', 18 | build: 'build/', 19 | bower: 'bower_components/' 20 | }, 21 | // Task configuration. 22 | clean: { 23 | build: [ 24 | '<%= dir.build %>*' 25 | ] 26 | }, 27 | copy: { 28 | static: { 29 | files: [ 30 | { cwd: '<%= dir.bower %>font-awesome/fonts/', src: ['**'], dest: 'public/fonts/', expand: true } 31 | ] 32 | }, 33 | js: { 34 | files: [ 35 | { cwd: '<%= dir.bower %>es5-shim/', src: ['es5-shim.js'], dest: '<%= dir.build %>dashing/', expand: true }, 36 | { cwd: '<%= dir.bower %>jquery/dist/', src: ['jquery.js'], dest: '<%= dir.build %>dashing/', expand: true }, 37 | { cwd: '<%= dir.bower %>gridster/dist/', src: ['jquery.gridster.js'], dest: '<%= dir.build %>application/', expand: true }, 38 | { cwd: '<%= dir.bower %>jquery-knob/js/', src: ['jquery.knob.js'], dest: '<%= dir.build %>application/', expand: true }, 39 | { cwd: '<%= dir.bower %>jquery-leanmodal/', src: ['jquery.leanModal.js'], dest: '<%= dir.build %>application/', expand: true }, 40 | { cwd: '<%= dir.bower %>rickshaw/', src: ['rickshaw.js'], dest: '<%= dir.build %>application/', expand: true }, 41 | { cwd: '<%= dir.bower %>d3/', src: ['d3.js'], dest: '<%= dir.build %>application/', expand: true } 42 | ] 43 | }, 44 | css: { 45 | files: [ 46 | { cwd: '<%= dir.bower %>font-awesome/css/', src: ['font-awesome.css'], dest: '<%= dir.build %>css/lib/', expand: true }, 47 | { cwd: '<%= dir.bower %>gridster/dist/', src: ['jquery.gridster.css'], dest: '<%= dir.build %>css/lib/', expand: true } 48 | ] 49 | }, 50 | coffee: { 51 | files: [ 52 | { cwd: '<%= dir.asset %>javascripts/', src: ['dashing.coffee'], dest: '<%= dir.build %>dashing/', expand: true }, 53 | { cwd: '<%= dir.asset %>javascripts/', src: ['application.coffee'], dest: '<%= dir.build %>application/', expand: true } 54 | ] 55 | } 56 | }, 57 | 'string-replace': { 58 | fix_abstract_binding: { 59 | options: { 60 | replacements: [ 61 | { pattern: /(@accessor 'filteredValue')$/m, replacement: "$1," }, 62 | { pattern: /(@accessor 'unfilteredValue')$/m, replacement: "$1," } 63 | ] 64 | }, 65 | src: '<%= dir.bower %>batman/src/view/bindings/abstract_binding.coffee', 66 | dest: '<%= dir.bower %>batman/src/view/bindings/abstract_binding.coffee' 67 | }, 68 | fix_view: { 69 | options: { 70 | replacements: [ 71 | { pattern: /(@accessor 'node')$/m, replacement: "$1," } 72 | ] 73 | }, 74 | src: '<%= dir.bower %>batman/src/view/view.coffee', 75 | dest: '<%= dir.bower %>batman/src/view/view.coffee' 76 | } 77 | }, 78 | snockets: { 79 | batman: { 80 | src: '<%= dir.bower %>batman/src/batman.coffee', 81 | dest: '<%= dir.build %>dashing/batman.js' 82 | }, 83 | batman_jquery: { 84 | src: '<%= dir.bower %>batman/src/platform/jquery.coffee', 85 | dest: '<%= dir.build %>dashing/batman.jquery.js' 86 | }, 87 | dashing: { 88 | src: '<%= dir.build %>dashing/dashing.coffee', 89 | dest: '<%= dir.build %>application/dashing.js' 90 | }, 91 | application: { 92 | src: '<%= dir.build %>application/application.coffee', 93 | dest: 'public/javascripts/application.js' 94 | } 95 | }, 96 | sass: { 97 | widgets: { 98 | cwd: 'widgets/', 99 | src: '**/*.scss', 100 | dest: '<%= dir.build %>css/widgets/', 101 | expand: true, 102 | flatten: true, 103 | ext: '.css' 104 | }, 105 | application: { 106 | src: '<%= dir.asset %>stylesheets/application.scss', 107 | dest: '<%= dir.build %>css/application.css' 108 | } 109 | }, 110 | concat: { 111 | css: { 112 | src: [ 113 | '<%= dir.build %>css/lib/*.css', 114 | '<%= dir.build %>css/widgets/*.css', 115 | '<%= dir.build %>css/application.css' 116 | ], 117 | dest: 'public/stylesheets/application.css', 118 | } 119 | }, 120 | uglify: { 121 | options: { 122 | banner: '<%= banner %>' 123 | }, 124 | application: { 125 | src: '<%= snockets.application.dest %>', 126 | dest: '<%= snockets.application.dest %>' 127 | } 128 | }, 129 | cssmin: { 130 | application: { 131 | src: '<%= concat.css.dest %>', 132 | dest: '<%= concat.css.dest %>' 133 | } 134 | }, 135 | watch: { 136 | options: { 137 | livereload: LIVERELOAD_PORT 138 | }, 139 | src: { 140 | files: [ 141 | '<%= dir.asset %>javascripts/**/*', 142 | '<%= dir.asset %>stylesheets/**/*', 143 | 'widgets/**/*' 144 | ], 145 | tasks: ['build'] 146 | }, 147 | livereload: { 148 | options: { 149 | livereload: true 150 | }, 151 | files: [ 152 | 'public/**/*' 153 | ] 154 | } 155 | }, 156 | connect: { 157 | options: { 158 | hostname: '0.0.0.0', 159 | port: 9000, 160 | livereload: LIVERELOAD_PORT 161 | }, 162 | server: { 163 | proxies: [ 164 | { 165 | context: [''], 166 | host: '0.0.0.0', 167 | port: 3000, 168 | https: false, 169 | changeOrigin: false 170 | } 171 | ], 172 | }, 173 | livereload: { 174 | options: { 175 | open: true, 176 | base: ['public'], 177 | middleware: function () { 178 | return [require('grunt-connect-proxy/lib/utils').proxyRequest]; 179 | } 180 | } 181 | } 182 | } 183 | }); 184 | 185 | grunt.registerTask('init', ['clean', 'copy:static', 'copy:js', 'copy:css', 'string-replace', 'snockets:batman', 'snockets:batman_jquery']); 186 | 187 | grunt.registerTask('js', ['copy:coffee', 'snockets:dashing', 'snockets:application']); 188 | grunt.registerTask('css', ['sass', 'concat:css']); 189 | grunt.registerTask('build', ['js', 'css']); 190 | grunt.registerTask('minify', ['uglify', 'cssmin']); 191 | 192 | grunt.registerTask('serve', ['build', 'configureProxies:server', 'connect:livereload', 'watch']); 193 | }; 194 | -------------------------------------------------------------------------------- /app/templates/_README.md: -------------------------------------------------------------------------------- 1 | # <%= _.slugify(appName) %> 2 | 3 | A dashboard to do the thing with the stuff. 4 | 5 | ## Getting Started 6 | 7 | The `init` task copies third party assets (installed by Bower) into position and compiles BatmanJS. This should have been run automatically as a post-install script, but if you've added new dependencies or need to do it manually: 8 | 9 | bower install 10 | grunt init 11 | 12 | ## Running Stuff 13 | 14 | This starts the backend (port 3000 by default). 15 | 16 | go run server.go 17 | 18 | You may also start a livereload server that watches the `assets` directory for changes and runs the build pipeline automatically. When you access it (port 9000 by default), it injects a script into the page that initiates a reload whenever the compiled assets are updated. 19 | 20 | grunt serve 21 | 22 | ## Building Stuff 23 | 24 | grunt build 25 | 26 | If you need to build just `application.js` or `application.css`, you may run each task individually: 27 | 28 | grunt js 29 | grunt css 30 | 31 | ## Minifying Stuff 32 | 33 | grunt minify 34 | 35 | `application.js` and `application.css` are minified in-place. 36 | 37 | ## Generating Stuff 38 | 39 | Create new dashboards, jobs and widgets: 40 | 41 | yo dashing-go:dashboard foo 42 | yo dashing-go:job foo 43 | yo dashing-go:widget foo 44 | 45 | Note the following naming conventions (the generator automatically enforces them): 46 | 47 | * dashed-slug for widget and dashboard names 48 | * under_score for Go job filenames 49 | * camelCase for Go job structs 50 | 51 | Existing third party Dashing widgets should be compatible with dashing-go. 52 | 53 | ## License 54 | 55 | Released under the MIT license. 56 | -------------------------------------------------------------------------------- /app/templates/_bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= _.slugify(appName) %>", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "jquery": "~2.1.0", 6 | "es5-shim": "~2.3.0", 7 | "font-awesome": "~4.0.3", 8 | "batman": "~0.14.1", 9 | "d3": "~3.4.3", 10 | "rickshaw": "~1.4.6", 11 | "gridster": "~0.5.1", 12 | "jquery-knob": "~1.2.3", 13 | "jquery-leanmodal": "FinelySliced/leanModal.js" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= _.slugify(appName) %>", 3 | "version": "0.0.0", 4 | "description": "A dashing-go dashboard", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/gigablah/generator-dashing-go.git" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "grunt": "~0.4.1", 12 | "load-grunt-tasks": "~0.3.0", 13 | "time-grunt": "~0.2.0", 14 | "grunt-contrib-clean": "~0.5.0", 15 | "grunt-contrib-copy": "~0.5.0", 16 | "grunt-contrib-concat": "~0.3.0", 17 | "grunt-contrib-uglify": "~0.4.0", 18 | "grunt-contrib-cssmin": "~0.9.0", 19 | "grunt-contrib-watch": "~0.6.0", 20 | "grunt-contrib-connect": "~0.7.0", 21 | "grunt-connect-proxy": "~0.1.10", 22 | "grunt-sass": "~0.7.0", 23 | "grunt-string-replace": "~0.2.7", 24 | "grunt-snockets": "umurgdk/grunt-snockets" 25 | }, 26 | "engines": { 27 | "node": ">=0.10.0" 28 | }, 29 | "scripts": { 30 | "postinstall" : "grunt init build" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/templates/assets/javascripts/application.coffee: -------------------------------------------------------------------------------- 1 | # dashing.js is located in the dashing framework 2 | # It includes jquery & batman for you. 3 | #= require dashing.js 4 | 5 | #= require_tree ./ 6 | #= require_tree ../../widgets 7 | 8 | console.log("Yeah! The dashboard has started!") 9 | 10 | Dashing.on 'ready', -> 11 | Dashing.widget_margins ||= [5, 5] 12 | Dashing.widget_base_dimensions ||= [300, 360] 13 | Dashing.numColumns ||= 4 14 | 15 | contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns 16 | 17 | Batman.setImmediate -> 18 | $('.gridster').width(contentWidth) 19 | $('.gridster ul:first').gridster 20 | widget_margins: Dashing.widget_margins 21 | widget_base_dimensions: Dashing.widget_base_dimensions 22 | avoid_overlapped_widgets: !Dashing.customGridsterLayout 23 | draggable: 24 | stop: Dashing.showGridsterInstructions 25 | start: -> Dashing.currentWidgetPositions = Dashing.getWidgetPositions() 26 | -------------------------------------------------------------------------------- /app/templates/assets/javascripts/dashing.coffee: -------------------------------------------------------------------------------- 1 | #= require jquery 2 | #= require es5-shim 3 | #= require batman 4 | #= require batman.jquery 5 | 6 | 7 | Batman.Filters.prettyNumber = (num) -> 8 | num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") unless isNaN(num) 9 | 10 | Batman.Filters.dashize = (str) -> 11 | dashes_rx1 = /([A-Z]+)([A-Z][a-z])/g; 12 | dashes_rx2 = /([a-z\d])([A-Z])/g; 13 | 14 | return str.replace(dashes_rx1, '$1_$2').replace(dashes_rx2, '$1_$2').replace(/_/g, '-').toLowerCase() 15 | 16 | Batman.Filters.shortenedNumber = (num) -> 17 | return num if isNaN(num) 18 | if num >= 1000000000 19 | (num / 1000000000).toFixed(1) + 'B' 20 | else if num >= 1000000 21 | (num / 1000000).toFixed(1) + 'M' 22 | else if num >= 1000 23 | (num / 1000).toFixed(1) + 'K' 24 | else 25 | num 26 | 27 | class window.Dashing extends Batman.App 28 | @on 'reload', (data) -> 29 | window.location.reload(true) 30 | 31 | @root -> 32 | Dashing.params = Batman.URI.paramsFromQuery(window.location.search.slice(1)); 33 | 34 | class Dashing.Widget extends Batman.View 35 | constructor: -> 36 | # Set the view path 37 | @constructor::source = Batman.Filters.underscore(@constructor.name) 38 | super 39 | 40 | @mixin($(@node).data()) 41 | Dashing.widgets[@id] ||= [] 42 | Dashing.widgets[@id].push(@) 43 | @mixin(Dashing.lastEvents[@id]) # in case the events from the server came before the widget was rendered 44 | 45 | type = Batman.Filters.dashize(@view) 46 | $(@node).addClass("widget widget-#{type} #{@id}") 47 | 48 | @accessor 'updatedAtMessage', -> 49 | if updatedAt = @get('updatedAt') 50 | timestamp = new Date(updatedAt * 1000) 51 | hours = timestamp.getHours() 52 | minutes = ("0" + timestamp.getMinutes()).slice(-2) 53 | "Last updated at #{hours}:#{minutes}" 54 | 55 | @::on 'ready', -> 56 | Dashing.Widget.fire 'ready' 57 | 58 | receiveData: (data) => 59 | @mixin(data) 60 | @onData(data) 61 | 62 | onData: (data) => 63 | # Widgets override this to handle incoming data 64 | 65 | Dashing.AnimatedValue = 66 | get: Batman.Property.defaultAccessor.get 67 | set: (k, to) -> 68 | if !to? || isNaN(to) 69 | @[k] = to 70 | else 71 | timer = "interval_#{k}" 72 | num = if (!isNaN(@[k]) && @[k]?) then @[k] else 0 73 | unless @[timer] || num == to 74 | to = parseFloat(to) 75 | num = parseFloat(num) 76 | up = to > num 77 | num_interval = Math.abs(num - to) / 90 78 | @[timer] = 79 | setInterval => 80 | num = if up then Math.ceil(num+num_interval) else Math.floor(num-num_interval) 81 | if (up && num > to) || (!up && num < to) 82 | num = to 83 | clearInterval(@[timer]) 84 | @[timer] = null 85 | delete @[timer] 86 | @[k] = num 87 | @set k, to 88 | , 10 89 | @[k] = num 90 | 91 | Dashing.widgets = widgets = {} 92 | Dashing.lastEvents = lastEvents = {} 93 | Dashing.debugMode = false 94 | 95 | source = new EventSource('events') 96 | source.addEventListener 'open', (e) -> 97 | console.log("Connection opened", e) 98 | 99 | source.addEventListener 'error', (e)-> 100 | console.log("Connection error", e) 101 | if (e.currentTarget.readyState == EventSource.CLOSED) 102 | console.log("Connection closed") 103 | setTimeout (-> 104 | window.location.reload() 105 | ), 5*60*1000 106 | 107 | source.addEventListener 'message', (e) -> 108 | data = JSON.parse(e.data) 109 | if lastEvents[data.id]?.updatedAt != data.updatedAt 110 | if Dashing.debugMode 111 | console.log("Received data for #{data.id}", data) 112 | lastEvents[data.id] = data 113 | if widgets[data.id]?.length > 0 114 | for widget in widgets[data.id] 115 | widget.receiveData(data) 116 | 117 | source.addEventListener 'dashboards', (e) -> 118 | data = JSON.parse(e.data) 119 | if Dashing.debugMode 120 | console.log("Received data for dashboards", data) 121 | if data.dashboard is '*' or window.location.pathname is "/#{data.dashboard}" 122 | Dashing.fire data.event, data 123 | 124 | $(document).ready -> 125 | Dashing.run() 126 | -------------------------------------------------------------------------------- /app/templates/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | /* 2 | //=require_directory . 3 | //=require_tree ../../widgets 4 | */ 5 | // ---------------------------------------------------------------------------- 6 | // Sass declarations 7 | // ---------------------------------------------------------------------------- 8 | $background-color: #222; 9 | $text-color: #fff; 10 | 11 | $background-warning-color-1: #e82711; 12 | $background-warning-color-2: #9b2d23; 13 | $text-warning-color: #fff; 14 | 15 | $background-danger-color-1: #eeae32; 16 | $background-danger-color-2: #ff9618; 17 | $text-danger-color: #fff; 18 | 19 | @-webkit-keyframes status-warning-background { 20 | 0% { background-color: $background-warning-color-1; } 21 | 50% { background-color: $background-warning-color-2; } 22 | 100% { background-color: $background-warning-color-1; } 23 | } 24 | @-webkit-keyframes status-danger-background { 25 | 0% { background-color: $background-danger-color-1; } 26 | 50% { background-color: $background-danger-color-2; } 27 | 100% { background-color: $background-danger-color-1; } 28 | } 29 | @mixin animation($animation-name, $duration, $function, $animation-iteration-count:""){ 30 | -webkit-animation: $animation-name $duration $function #{$animation-iteration-count}; 31 | -moz-animation: $animation-name $duration $function #{$animation-iteration-count}; 32 | -ms-animation: $animation-name $duration $function #{$animation-iteration-count}; 33 | } 34 | 35 | // ---------------------------------------------------------------------------- 36 | // Base styles 37 | // ---------------------------------------------------------------------------- 38 | html { 39 | font-size: 100%; 40 | -webkit-text-size-adjust: 100%; 41 | -ms-text-size-adjust: 100%; 42 | } 43 | 44 | body { 45 | margin: 0; 46 | background-color: $background-color; 47 | font-size: 20px; 48 | color: $text-color; 49 | font-family: 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif; 50 | } 51 | 52 | b, strong { 53 | font-weight: bold; 54 | } 55 | 56 | a { 57 | text-decoration: none; 58 | color: inherit; 59 | } 60 | 61 | img { 62 | border: 0; 63 | -ms-interpolation-mode: bicubic; 64 | vertical-align: middle; 65 | } 66 | 67 | img, object { 68 | max-width: 100%; 69 | } 70 | 71 | iframe { 72 | max-width: 100%; 73 | } 74 | 75 | table { 76 | border-collapse: collapse; 77 | border-spacing: 0; 78 | width: 100%; 79 | } 80 | 81 | td { 82 | vertical-align: middle; 83 | } 84 | 85 | ul, ol { 86 | padding: 0; 87 | margin: 0; 88 | } 89 | 90 | h1, h2, h3, h4, h5, p { 91 | padding: 0; 92 | margin: 0; 93 | } 94 | h1 { 95 | margin-bottom: 12px; 96 | text-align: center; 97 | font-size: 30px; 98 | font-weight: 400; 99 | } 100 | h2 { 101 | text-transform: uppercase; 102 | font-size: 76px; 103 | font-weight: 700; 104 | color: $text-color; 105 | } 106 | h3 { 107 | font-size: 25px; 108 | font-weight: 600; 109 | color: $text-color; 110 | } 111 | 112 | // ---------------------------------------------------------------------------- 113 | // Base widget styles 114 | // ---------------------------------------------------------------------------- 115 | .gridster { 116 | margin: 0px auto; 117 | } 118 | 119 | .icon-background { 120 | width: 100%!important; 121 | height: 100%; 122 | position: absolute; 123 | left: 0; 124 | top: 0; 125 | opacity: 0.1; 126 | font-size: 275px; 127 | text-align: center; 128 | margin-top: 82px; 129 | } 130 | 131 | .list-nostyle { 132 | list-style: none; 133 | } 134 | 135 | .gridster ul { 136 | list-style: none; 137 | } 138 | 139 | .gs-w { 140 | width: 100%; 141 | display: table; 142 | cursor: pointer; 143 | } 144 | 145 | .widget { 146 | padding: 25px 12px; 147 | text-align: center; 148 | width: 100%; 149 | display: table-cell; 150 | vertical-align: middle; 151 | } 152 | 153 | .widget.status-warning { 154 | background-color: $background-warning-color-1; 155 | @include animation(status-warning-background, 2s, ease, infinite); 156 | 157 | .icon-warning-sign { 158 | display: inline-block; 159 | } 160 | 161 | .title, .more-info { 162 | color: $text-warning-color; 163 | } 164 | } 165 | 166 | .widget.status-danger { 167 | color: $text-danger-color; 168 | background-color: $background-danger-color-1; 169 | @include animation(status-danger-background, 2s, ease, infinite); 170 | 171 | .icon-warning-sign { 172 | display: inline-block; 173 | } 174 | 175 | .title, .more-info { 176 | color: $text-danger-color; 177 | } 178 | } 179 | 180 | .more-info { 181 | font-size: 15px; 182 | position: absolute; 183 | bottom: 32px; 184 | left: 0; 185 | right: 0; 186 | } 187 | 188 | .updated-at { 189 | font-size: 15px; 190 | position: absolute; 191 | bottom: 12px; 192 | left: 0; 193 | right: 0; 194 | } 195 | 196 | #save-gridster { 197 | display: none; 198 | position: fixed; 199 | top: 0; 200 | margin: 0px auto; 201 | left: 50%; 202 | z-index: 1000; 203 | background: black; 204 | width: 190px; 205 | text-align: center; 206 | border: 1px solid white; 207 | border-top: 0px; 208 | margin-left: -95px; 209 | padding: 15px; 210 | } 211 | 212 | #save-gridster:hover { 213 | padding-top: 25px; 214 | } 215 | 216 | #saving-instructions { 217 | display: none; 218 | padding: 10px; 219 | width: 500px; 220 | height: 122px; 221 | z-index: 1000; 222 | background: white; 223 | top: 100px; 224 | color: black; 225 | font-size: 15px; 226 | padding-bottom: 4px; 227 | 228 | textarea { 229 | white-space: nowrap; 230 | width: 494px; 231 | height: 80px; 232 | } 233 | } 234 | 235 | #lean_overlay { 236 | position: fixed; 237 | z-index:100; 238 | top: 0px; 239 | left: 0px; 240 | height:100%; 241 | width:100%; 242 | background: #000; 243 | display: none; 244 | } 245 | 246 | #container { 247 | padding-top: 5px; 248 | } 249 | 250 | 251 | // ---------------------------------------------------------------------------- 252 | // Clearfix 253 | // ---------------------------------------------------------------------------- 254 | .clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; } 255 | .clearfix:after { clear: both; } 256 | .clearfix { zoom: 1; } 257 | -------------------------------------------------------------------------------- /app/templates/bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /app/templates/dashboards/layout.gerb: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 |Paste the following at the top of <%= dashboard %>.gerb
27 | 28 |You may have mistyped the address or the page may have moved.
24 |6 | 7 |
8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/templates/widgets/number/number.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #47bbb3; 5 | $value-color: #fff; 6 | 7 | $title-color: rgba(255, 255, 255, 0.7); 8 | $moreinfo-color: rgba(255, 255, 255, 0.7); 9 | 10 | // ---------------------------------------------------------------------------- 11 | // Widget-number styles 12 | // ---------------------------------------------------------------------------- 13 | .widget-number { 14 | 15 | background-color: $background-color; 16 | 17 | .title { 18 | color: $title-color; 19 | } 20 | 21 | .value { 22 | color: $value-color; 23 | } 24 | 25 | .change-rate { 26 | font-weight: 500; 27 | font-size: 30px; 28 | color: $value-color; 29 | } 30 | 31 | .more-info { 32 | color: $moreinfo-color; 33 | } 34 | 35 | .updated-at { 36 | color: rgba(0, 0, 0, 0.3); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/templates/widgets/text/text.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Text extends Dashing.Widget 2 | -------------------------------------------------------------------------------- /app/templates/widgets/text/text.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/templates/widgets/text/text.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #ec663c; 5 | 6 | $title-color: rgba(255, 255, 255, 0.7); 7 | $moreinfo-color: rgba(255, 255, 255, 0.7); 8 | 9 | // ---------------------------------------------------------------------------- 10 | // Widget-text styles 11 | // ---------------------------------------------------------------------------- 12 | .widget-text { 13 | 14 | background-color: $background-color; 15 | 16 | .title { 17 | color: $title-color; 18 | } 19 | 20 | .more-info { 21 | color: $moreinfo-color; 22 | } 23 | 24 | .updated-at { 25 | color: rgba(255, 255, 255, 0.7); 26 | } 27 | 28 | 29 | &.large h3 { 30 | font-size: 65px; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dashboard/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var yeoman = require('yeoman-generator'); 5 | 6 | var DashboardGenerator = yeoman.generators.NamedBase.extend({ 7 | init: function () { 8 | this.name = this._.slugify(this._.humanize(this.name)); 9 | this.log('Generating the ' + this.name + ' dashboard...'); 10 | }, 11 | 12 | files: function () { 13 | this.mkdir('dashboards'); 14 | this.copy('dashboard.gerb', 'dashboards/' + this.name + '.gerb'); 15 | } 16 | }); 17 | 18 | module.exports = DashboardGenerator; 19 | -------------------------------------------------------------------------------- /dashboard/templates/dashboard.gerb: -------------------------------------------------------------------------------- 1 | <% content "title" { %>My super sweet dashboard<% } %> 2 |
![]()
4 | 5 |