From cdd8ff258582f5eba7e3941a5a18007e7aabbbfa Mon Sep 17 00:00:00 2001 From: Daniel Beauchamp Date: Thu, 2 Aug 2012 13:38:19 -0400 Subject: Better generators, sample widgets, and more! --- allthethings.gemspec | 2 +- bin/att | 66 --- bin/things | 81 +++ lib/allthethings.rb | 4 +- templates/dashboard/%name%.erb.tt | 5 + templates/job/%name%.rb | 4 + templates/project/dashboards/layout.erb | 25 +- templates/project/dashboards/main.erb | 13 +- templates/project/jobs/sample.rb | 13 +- .../project/public/fonts/fontawesome-webfont.eot | Bin 0 -> 38708 bytes .../project/public/fonts/fontawesome-webfont.svg | 255 ++++++++ .../project/public/fonts/fontawesome-webfont.ttf | Bin 0 -> 68476 bytes .../project/public/fonts/fontawesome-webfont.woff | Bin 0 -> 41752 bytes templates/project/public/images/favicon.ico | Bin 0 -> 5430 bytes .../project/public/javascripts/jquery.knob.js | 646 +++++++++++++++++++++ .../public/javascripts/jquery.masonry.min.js | 10 + .../project/public/stylesheets/.empty_directory | 1 - .../project/public/stylesheets/application.scss | 238 ++++++++ .../project/public/stylesheets/font-awesome.css | 303 ++++++++++ templates/project/widgets/.empty_directory | 1 - templates/project/widgets/list/list.coffee | 15 + templates/project/widgets/list/list.html | 15 + templates/project/widgets/list/list.scss | 50 ++ templates/project/widgets/meter/meter.coffee | 16 + templates/project/widgets/meter/meter.html | 5 + templates/project/widgets/meter/meter.scss | 35 ++ templates/project/widgets/number/number.coffee | 29 + templates/project/widgets/number/number.html | 9 + templates/project/widgets/number/number.scss | 35 ++ templates/project/widgets/table/table.coffee | 2 + templates/project/widgets/table/table.html | 8 + templates/project/widgets/table/table.scss | 42 ++ templates/project/widgets/text/text.coffee | 2 + templates/project/widgets/text/text.html | 5 + templates/project/widgets/text/text.scss | 25 + .../widgets/widget_sample/widget_sample.coffee | 2 - .../widgets/widget_sample/widget_sample.html | 4 - .../widgets/widget_sample/widget_sample.scss | 0 templates/widget/%name%.coffee.tt | 2 - templates/widget/%name%.html | 0 templates/widget/%name%.scss | 0 templates/widget/%name%/%name%.coffee.tt | 11 + templates/widget/%name%/%name%.html | 0 templates/widget/%name%/%name%.scss.tt | 3 + vendor/javascripts/application.coffee | 17 +- vendor/javascripts/widget.coffee | 2 +- 46 files changed, 1903 insertions(+), 98 deletions(-) delete mode 100755 bin/att create mode 100755 bin/things create mode 100644 templates/dashboard/%name%.erb.tt create mode 100644 templates/job/%name%.rb create mode 100755 templates/project/public/fonts/fontawesome-webfont.eot create mode 100755 templates/project/public/fonts/fontawesome-webfont.svg create mode 100755 templates/project/public/fonts/fontawesome-webfont.ttf create mode 100755 templates/project/public/fonts/fontawesome-webfont.woff create mode 100644 templates/project/public/images/favicon.ico create mode 100644 templates/project/public/javascripts/jquery.knob.js create mode 100644 templates/project/public/javascripts/jquery.masonry.min.js delete mode 100644 templates/project/public/stylesheets/.empty_directory create mode 100644 templates/project/public/stylesheets/application.scss create mode 100644 templates/project/public/stylesheets/font-awesome.css delete mode 100644 templates/project/widgets/.empty_directory create mode 100644 templates/project/widgets/list/list.coffee create mode 100644 templates/project/widgets/list/list.html create mode 100644 templates/project/widgets/list/list.scss create mode 100644 templates/project/widgets/meter/meter.coffee create mode 100644 templates/project/widgets/meter/meter.html create mode 100644 templates/project/widgets/meter/meter.scss create mode 100644 templates/project/widgets/number/number.coffee create mode 100644 templates/project/widgets/number/number.html create mode 100644 templates/project/widgets/number/number.scss create mode 100644 templates/project/widgets/table/table.coffee create mode 100644 templates/project/widgets/table/table.html create mode 100644 templates/project/widgets/table/table.scss create mode 100644 templates/project/widgets/text/text.coffee create mode 100644 templates/project/widgets/text/text.html create mode 100644 templates/project/widgets/text/text.scss delete mode 100644 templates/project/widgets/widget_sample/widget_sample.coffee delete mode 100644 templates/project/widgets/widget_sample/widget_sample.html delete mode 100644 templates/project/widgets/widget_sample/widget_sample.scss delete mode 100644 templates/widget/%name%.coffee.tt delete mode 100644 templates/widget/%name%.html delete mode 100644 templates/widget/%name%.scss create mode 100644 templates/widget/%name%/%name%.coffee.tt create mode 100644 templates/widget/%name%/%name%.html create mode 100644 templates/widget/%name%/%name%.scss.tt diff --git a/allthethings.gemspec b/allthethings.gemspec index 7209073..859d999 100644 --- a/allthethings.gemspec +++ b/allthethings.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = 'allthethings' s.version = '0.1.0' s.date = '2012-07-24' - s.executables << 'att' + s.executables << 'things' s.summary = "A simple & flexible framework for creating dashboards." diff --git a/bin/att b/bin/att deleted file mode 100755 index c90fc48..0000000 --- a/bin/att +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env ruby - -require 'thor' -require 'net/http' -require 'json' - -class MockScheduler - def method_missing(*args) - yield - end -end - -def send_event(id, data) - req = Net::HTTP::Post.new("/widgets/#{id}") - req["content-type"] = "application/json" - req.body = JSON.unparse(data.merge(:auth_token => AllTheThings::CLI.auth_token)) - res = Net::HTTP.new('localhost', 3000).start { |http| http.request(req) } - puts "Data Sent to #{id}: #{data}" -end - -SCHEDULER = MockScheduler.new - -module AllTheThings - - class CLI < Thor - include Thor::Actions - - class << self - attr_accessor :auth_token - end - - attr_accessor :name - - def self.source_root - File.expand_path('../../templates', __FILE__) - end - - desc "install PROJECT_NAME", "Sets up ALL THE THINGS needed for your dashboard project structure." - def install(name) - @name = Thor::Util.snake_case(name) - directory :project, @name - end - - desc "new_widget WIDGET_NAME", "Creates a new widget with all the fixins'" - def new_widget(name) - @name = "widget_#{Thor::Util.snake_case(name)}" - directory :widget, File.join('widgets', @name) - end - - desc "start", "Starts the server in style!" - def start(*args) - args = args.join(" ") - system("bundle exec thin -R config.ru start #{args}") - end - - desc "job JOB_NAME AUTH_TOKEN(optional)", "Runs the specified job." - def job(name, auth_token = "") - self.class.auth_token = auth_token - f = File.join(Dir.pwd, "jobs", "#{name}.rb") - require f - end - - end -end - -AllTheThings::CLI.start \ No newline at end of file diff --git a/bin/things b/bin/things new file mode 100755 index 0000000..0ad9eb3 --- /dev/null +++ b/bin/things @@ -0,0 +1,81 @@ +#!/usr/bin/env ruby + +require 'thor' +require 'net/http' +require 'json' + +class MockScheduler + def method_missing(*args) + yield + end +end + +def send_event(id, data) + req = Net::HTTP::Post.new("/widgets/#{id}") + req["content-type"] = "application/json" + req.body = JSON.unparse(data.merge(:auth_token => AllTheThings::CLI.auth_token)) + res = Net::HTTP.new('localhost', 3000).start { |http| http.request(req) } + puts "Data Sent to #{id}: #{data}" +end + +SCHEDULER = MockScheduler.new + +module AllTheThings + + class CLI < Thor + include Thor::Actions + + class << self + attr_accessor :auth_token + end + + attr_accessor :name + + no_tasks do + ['widget', 'dashboard', 'job'].each do |type| + define_method "generate_#{type}" do |name| + @name = Thor::Util.snake_case(name) + directory type.to_sym, File.join("#{type}s") + end + end + end + + def self.source_root + File.expand_path('../../templates', __FILE__) + end + + desc "new PROJECT_NAME", "Sets up ALL THE THINGS needed for your dashboard project structure." + def new(name) + @name = Thor::Util.snake_case(name) + directory :project, @name + end + + desc "generate GENERATOR NAME", "Creates a new widget with all the fixins'" + def generate(type, name) + send("generate_#{type}".to_sym, name) + rescue NoMethodError => e + puts "Invalid generator. Either use widget, dashboard, or job" + end + map "g" => :generate + + desc "start", "Starts the server in style!" + method_option :job_path, :desc => "Specify the directory where jobs are stored" + def start(*args) + args = args.join(" ") + command = "bundle exec thin -R config.ru start #{args}" + command.prepend "export JOB_PATH=#{options[:job_path]}; " if options[:job_path] + system(command) + end + + desc "job JOB_NAME AUTH_TOKEN(optional)", "Runs the specified job." + def job(name, auth_token = "") + Dir[File.join(Dir.pwd, 'lib/**/*.rb')].each {|file| require file } + self.class.auth_token = auth_token + f = File.join(Dir.pwd, "jobs", "#{name}.rb") + require f + end + + end +end + +AllTheThings::CLI.start \ No newline at end of file diff --git a/lib/allthethings.rb b/lib/allthethings.rb index 904a991..8fbef07 100644 --- a/lib/allthethings.rb +++ b/lib/allthethings.rb @@ -54,7 +54,6 @@ get '/views/:widget?.html' do end post '/widgets/:id' do - protected! request.body.rewind body = JSON.parse(request.body.read) auth_token = body.delete("auth_token") @@ -125,5 +124,6 @@ def first_dashboard files.first end -files = Dir[Dir.pwd + '/jobs/*.rb'] +job_path = ENV["JOB_PATH"] || 'jobs' +files = Dir[Dir.pwd + "/#{job_path}/*.rb"] files.each { |job| require(job) } \ No newline at end of file diff --git a/templates/dashboard/%name%.erb.tt b/templates/dashboard/%name%.erb.tt new file mode 100644 index 0000000..b095d46 --- /dev/null +++ b/templates/dashboard/%name%.erb.tt @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/templates/job/%name%.rb b/templates/job/%name%.rb new file mode 100644 index 0000000..76f221a --- /dev/null +++ b/templates/job/%name%.rb @@ -0,0 +1,4 @@ +# :first_in sets how long it takes before the job is first run. In this case, it is run immediately +SCHEDULER.every '1m', :first_in => 0 do |job| + send_event('widget_id', { }) +end \ No newline at end of file diff --git a/templates/project/dashboards/layout.erb b/templates/project/dashboards/layout.erb index 66bf1e0..cb1a455 100644 --- a/templates/project/dashboards/layout.erb +++ b/templates/project/dashboards/layout.erb @@ -1,21 +1,38 @@ - + + My Awesome Dashboard + + <%= yield_content(:title) %> - + + - + + + + -
+
<%= yield %>
+ + \ No newline at end of file diff --git a/templates/project/dashboards/main.erb b/templates/project/dashboards/main.erb index 3638940..71acaf2 100644 --- a/templates/project/dashboards/main.erb +++ b/templates/project/dashboards/main.erb @@ -1 +1,12 @@ -
\ No newline at end of file +<% content_for(:title) { "My super sweet dashboard" } %> + \ No newline at end of file diff --git a/templates/project/jobs/sample.rb b/templates/project/jobs/sample.rb index 3d7bfcd..54a0e60 100644 --- a/templates/project/jobs/sample.rb +++ b/templates/project/jobs/sample.rb @@ -1,12 +1,5 @@ SCHEDULER.every '5s' do - sayings = [ - "That's one trouble with dual identities, Robin. Dual responsibilities.", - "You know your neosauruses well, Robin. Peanut butter sandwiches it is.", - "You're far from mod, Robin. And many hippies are older than you are.", - "We're still over land, Robin, and a seal is an aquatic, marine mammal.", - "True. You owe your life to dental hygiene.", - "This money goes to building better roads. We all must do our part." - ] - - send_event('sample', { quote: sayings.sample }) + send_event('synergy', { value: (rand * 1024).floor }) + send_event('convergence', { value: (rand * 1024).floor }) + send_event('paradigm_shift', { value: (rand * 1024).floor }) end \ No newline at end of file diff --git a/templates/project/public/fonts/fontawesome-webfont.eot b/templates/project/public/fonts/fontawesome-webfont.eot new file mode 100755 index 0000000..89070c1 Binary files /dev/null and b/templates/project/public/fonts/fontawesome-webfont.eot differ diff --git a/templates/project/public/fonts/fontawesome-webfont.svg b/templates/project/public/fonts/fontawesome-webfont.svg new file mode 100755 index 0000000..1245f92 --- /dev/null +++ b/templates/project/public/fonts/fontawesome-webfont.svg @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/project/public/fonts/fontawesome-webfont.ttf b/templates/project/public/fonts/fontawesome-webfont.ttf new file mode 100755 index 0000000..c17e9f8 Binary files /dev/null and b/templates/project/public/fonts/fontawesome-webfont.ttf differ diff --git a/templates/project/public/fonts/fontawesome-webfont.woff b/templates/project/public/fonts/fontawesome-webfont.woff new file mode 100755 index 0000000..09f2469 Binary files /dev/null and b/templates/project/public/fonts/fontawesome-webfont.woff differ diff --git a/templates/project/public/images/favicon.ico b/templates/project/public/images/favicon.ico new file mode 100644 index 0000000..29a408f Binary files /dev/null and b/templates/project/public/images/favicon.ico differ diff --git a/templates/project/public/javascripts/jquery.knob.js b/templates/project/public/javascripts/jquery.knob.js new file mode 100644 index 0000000..3224638 --- /dev/null +++ b/templates/project/public/javascripts/jquery.knob.js @@ -0,0 +1,646 @@ +/*!jQuery Knob*/ +/** + * Downward compatible, touchable dial + * + * Version: 1.2.0 (15/07/2012) + * Requires: jQuery v1.7+ + * + * Copyright (c) 2012 Anthony Terrien + * Under MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Thanks to vor, eskimoblood, spiffistan, FabrizioC + */ +$(function () { + + /** + * Kontrol library + */ + "use strict"; + + /** + * Definition of globals and core + */ + var k = {}, // kontrol + max = Math.max, + min = Math.min; + + k.c = {}; + k.c.d = $(document); + k.c.t = function (e) { + return e.originalEvent.touches.length - 1; + }; + + /** + * Kontrol Object + * + * Definition of an abstract UI control + * + * Each concrete component must call this one. + * + * k.o.call(this); + * + */ + k.o = function () { + var s = this; + + this.o = null; // array of options + this.$ = null; // jQuery wrapped element + this.i = null; // mixed HTMLInputElement or array of HTMLInputElement + this.g = null; // 2D graphics context for 'pre-rendering' + this.v = null; // value ; mixed array or integer + this.cv = null; // change value ; not commited value + this.x = 0; // canvas x position + this.y = 0; // canvas y position + this.$c = null; // jQuery canvas element + this.c = null; // rendered canvas context + this.t = 0; // touches index + this.isInit = false; + this.fgColor = null; // main color + this.pColor = null; // previous color + this.dH = null; // draw hook + this.cH = null; // change hook + this.eH = null; // cancel hook + this.rH = null; // release hook + + this.run = function () { + var cf = function (e, conf) { + var k; + for (k in conf) { + s.o[k] = conf[k]; + } + s.init(); + s._configure() + ._draw(); + }; + + if(this.$.data('kontroled')) return; + this.$.data('kontroled', true); + + this.extend(); + this.o = $.extend( + { + // Config + min : this.$.data('min') || 0, + max : this.$.data('max') || 100, + stopper : true, + readOnly : this.$.data('readonly'), + + // UI + cursor : (this.$.data('cursor') === true && 30) + || this.$.data('cursor') + || 0, + thickness : this.$.data('thickness') || 0.35, + width : this.$.data('width') || 200, + height : this.$.data('height') || 200, + displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'), + displayPrevious : this.$.data('displayprevious'), + fgColor : this.$.data('fgcolor') || '#87CEEB', + inline : false, + + // Hooks + draw : null, // function () {} + change : null, // function (value) {} + cancel : null, // function () {} + release : null // function (value) {} + }, this.o + ); + + // routing value + if(this.$.is('fieldset')) { + + // fieldset = array of integer + this.v = {}; + this.i = this.$.find('input') + this.i.each(function(k) { + var $this = $(this); + s.i[k] = $this; + s.v[k] = $this.val(); + + $this.bind( + 'change' + , function () { + var val = {}; + val[k] = $this.val(); + s.val(val); + } + ); + }); + this.$.find('legend').remove(); + + } else { + // input = integer + this.i = this.$; + this.v = this.$.val(); + (this.v == '') && (this.v = this.o.min); + + this.$.bind( + 'change' + , function () { + s.val(s.$.val()); + } + ); + } + + (!this.o.displayInput) && this.$.hide(); + + this.$c = $(''); + this.c = this.$c[0].getContext("2d"); + + this.$ + .wrap($('
')) + .before(this.$c); + + if (this.v instanceof Object) { + this.cv = {}; + this.copy(this.v, this.cv); + } else { + this.cv = this.v; + } + + this.$ + .bind("configure", cf) + .parent() + .bind("configure", cf); + + this._listen() + ._configure() + ._xy() + .init(); + + this.isInit = true; + + this._draw(); + + return this; + }; + + this._draw = function () { + + // canvas pre-rendering + var d = true, + c = document.createElement('canvas'); + + c.width = s.o.width; + c.height = s.o.height; + s.g = c.getContext('2d'); + + s.clear(); + + s.dH + && (d = s.dH()); + + (d !== false) && s.draw(); + + s.c.drawImage(c, 0, 0); + c = null; + }; + + this._touch = function (e) { + + var touchMove = function (e) { + + var v = s.xy2val( + e.originalEvent.touches[s.t].pageX, + e.originalEvent.touches[s.t].pageY + ); + + if (v == s.cv) return; + + if ( + s.cH + && (s.cH(v) === false) + ) return; + + + s.change(v); + s._draw(); + }; + + // get touches index + this.t = k.c.t(e); + + // First touch + touchMove(e); + + // Touch events listeners + k.c.d + .bind("touchmove.k", touchMove) + .bind( + "touchend.k" + , function () { + k.c.d.unbind('touchmove.k touchend.k'); + + if ( + s.rH + && (s.rH(s.cv) === false) + ) return; + + s.val(s.cv); + } + ); + + return this; + }; + + this._mouse = function (e) { + + var mouseMove = function (e) { + var v = s.xy2val(e.pageX, e.pageY); + if (v == s.cv) return; + + if ( + s.cH + && (s.cH(v) === false) + ) return; + + s.change(v); + s._draw(); + }; + + // First click + mouseMove(e); + + // Mouse events listeners + k.c.d + .bind("mousemove.k", mouseMove) + .bind( + // Escape key cancel current change + "keyup.k" + , function (e) { + if (e.keyCode === 27) { + k.c.d.unbind("mouseup.k mousemove.k keyup.k"); + + if ( + s.eH + && (s.eH() === false) + ) return; + + s.cancel(); + } + } + ) + .bind( + "mouseup.k" + , function (e) { + k.c.d.unbind('mousemove.k mouseup.k keyup.k'); + + if ( + s.rH + && (s.rH(s.cv) === false) + ) return; + + s.val(s.cv); + } + ); + + return this; + }; + + this._xy = function () { + var o = this.$c.offset(); + this.x = o.left; + this.y = o.top; + return this; + }; + + this._listen = function () { + + if (!this.o.readOnly) { + this.$c + .bind( + "mousedown" + , function (e) { + e.preventDefault(); + s._xy()._mouse(e); + } + ) + .bind( + "touchstart" + , function (e) { + e.preventDefault(); + s._xy()._touch(e); + } + ); + this.listen(); + } else { + this.$.attr('readonly', 'readonly'); + } + + return this; + }; + + this._configure = function () { + + // Hooks + if (this.o.draw) this.dH = this.o.draw; + if (this.o.change) this.cH = this.o.change; + if (this.o.cancel) this.eH = this.o.cancel; + if (this.o.release) this.rH = this.o.release; + + if (this.o.displayPrevious) { + this.pColor = this.h2rgba(this.o.fgColor, "0.4"); + this.fgColor = this.h2rgba(this.o.fgColor, "0.6"); + } else { + this.fgColor = this.o.fgColor; + } + + return this; + }; + + this._clear = function () { + this.$c[0].width = this.$c[0].width; + }; + + // Abstract methods + this.listen = function () {}; // on start, one time + this.extend = function () {}; // each time configure triggered + this.init = function () {}; // each time configure triggered + this.change = function (v) {}; // on change + this.val = function (v) {}; // on release + this.xy2val = function (x, y) {}; // + this.draw = function () {}; // on change / on release + this.clear = function () { this._clear(); }; + + // Utils + this.h2rgba = function (h, a) { + var rgb; + h = h.substring(1,7) + rgb = [parseInt(h.substring(0,2),16) + ,parseInt(h.substring(2,4),16) + ,parseInt(h.substring(4,6),16)]; + return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")"; + }; + + this.copy = function (f, t) { + for (var i in f) { t[i] = f[i]; } + }; + }; + + + /** + * k.Dial + */ + k.Dial = function () { + k.o.call(this); + + this.startAngle = null; + this.xy = null; + this.radius = null; + this.lineWidth = null; + this.cursorExt = null; + this.w2 = null; + this.PI2 = 2*Math.PI; + + this.extend = function () { + this.o = $.extend( + { + bgColor : this.$.data('bgcolor') || '#EEEEEE', + angleOffset : this.$.data('angleoffset') || 0, + angleArc : this.$.data('anglearc') || 360, + inline : true + }, this.o + ); + }; + + this.val = function (v) { + if (null != v) { + this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v; + this.v = this.cv; + this.$.val(this.v); + this._draw(); + } else { + return this.v; + } + }; + + this.xy2val = function (x, y) { + var a, ret; + + a = Math.atan2( + x - (this.x + this.w2) + , - (y - this.y - this.w2) + ) - this.angleOffset; + + if(this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) { + // if isset angleArc option, set to min if .5 under min + a = 0; + } else if (a < 0) { + a += this.PI2; + } + + ret = ~~ (0.5 + (a * (this.o.max - this.o.min) / this.angleArc)) + + this.o.min; + + this.o.stopper + && (ret = max(min(ret, this.o.max), this.o.min)); + + return ret; + }; + + this.listen = function () { + // bind MouseWheel + var s = this, + mw = function (e) { + e.preventDefault(); + + var ori = e.originalEvent + ,deltaX = ori.detail || ori.wheelDeltaX + ,deltaY = ori.detail || ori.wheelDeltaY + ,v = parseInt(s.$.val()) + (deltaX>0 || deltaY>0 ? 1 : deltaX<0 || deltaY<0 ? -1 : 0); + + if ( + s.cH + && (s.cH(v) === false) + ) return; + + s.val(v); + } + , kval, to, m = 1, kv = {37:-1, 38:1, 39:1, 40:-1}; + + this.$ + .bind( + "keydown" + ,function (e) { + var kc = e.keyCode; + kval = parseInt(String.fromCharCode(kc)); + + if (isNaN(kval)) { + + (kc !== 13) // enter + && (kc !== 8) // bs + && (kc !== 9) // tab + && (kc !== 189) // - + && e.preventDefault(); + + // arrows + if ($.inArray(kc,[37,38,39,40]) > -1) { + e.preventDefault(); + + var v = parseInt(s.$.val()) + kv[kc] * m; + + s.o.stopper + && (v = max(min(v, s.o.max), s.o.min)); + + s.change(v); + s._draw(); + + // long time keydown speed-up + to = window.setTimeout( + function () { m*=2; } + ,30 + ); + } + } + } + ) + .bind( + "keyup" + ,function (e) { + if (isNaN(kval)) { + if (to) { + window.clearTimeout(to); + to = null; + m = 1; + s.val(s.$.val()); + } + } else { + // kval postcond + (s.$.val() > s.o.max && s.$.val(s.o.max)) + || (s.$.val() < s.o.min && s.$.val(s.o.min)); + } + + } + ); + + this.$c.bind("mousewheel DOMMouseScroll", mw); + this.$.bind("mousewheel DOMMouseScroll", mw) + }; + + this.init = function () { + + if ( + this.v < this.o.min + || this.v > this.o.max + ) this.v = this.o.min; + + this.$.val(this.v); + this.w2 = this.o.width / 2; + this.cursorExt = this.o.cursor / 100; + this.xy = this.w2; + this.lineWidth = this.xy * this.o.thickness; + this.radius = this.xy - this.lineWidth / 2; + + this.o.angleOffset + && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset); + + this.o.angleArc + && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc); + + // deg to rad + this.angleOffset = this.o.angleOffset * Math.PI / 180; + this.angleArc = this.o.angleArc * Math.PI / 180; + + // compute start and end angles + this.startAngle = 1.5 * Math.PI + this.angleOffset; + this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc; + + var s = max( + String(Math.abs(this.o.max)).length + , String(Math.abs(this.o.min)).length + , 2 + ) + 2; + + this.o.displayInput + && this.i.css({ + 'width' : ((this.o.width / 2 + 4) >> 0) + 'px' + ,'height' : ((this.o.width / 3) >> 0) + 'px' + ,'position' : 'absolute' + ,'vertical-align' : 'middle' + ,'margin-top' : ((this.o.width / 3) >> 0) + 'px' + ,'margin-left' : '-' + ((this.o.width * 3 / 4 + 2) >> 0) + 'px' + ,'border' : 0 + ,'background' : 'none' + ,'font' : 'bold ' + ((this.o.width / s) >> 0) + 'px Arial' + ,'text-align' : 'center' + ,'color' : this.o.fgColor + ,'padding' : '0px' + ,'-webkit-appearance': 'none' + }) + || this.i.css({ + 'width' : '0px' + ,'visibility' : 'hidden' + }); + }; + + this.change = function (v) { + this.cv = v; + this.$.val(v); + }; + + this.angle = function (v) { + return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min); + }; + + this.draw = function () { + + var c = this.g, // context + a = this.angle(this.cv) // Angle + , sat = this.startAngle // Start angle + , eat = sat + a // End angle + , sa, ea // Previous angles + , r = 1; + + c.lineWidth = this.lineWidth; + + this.o.cursor + && (sat = eat - this.cursorExt) + && (eat = eat + this.cursorExt); + + c.beginPath(); + c.strokeStyle = this.o.bgColor; + c.arc(this.xy, this.xy, this.radius, this.endAngle, this.startAngle, true); + c.stroke(); + + if (this.o.displayPrevious) { + ea = this.startAngle + this.angle(this.v); + sa = this.startAngle; + this.o.cursor + && (sa = ea - this.cursorExt) + && (ea = ea + this.cursorExt); + + c.beginPath(); + c.strokeStyle = this.pColor; + c.arc(this.xy, this.xy, this.radius, sa, ea, false); + c.stroke(); + r = (this.cv == this.v); + } + + c.beginPath(); + c.strokeStyle = r ? this.o.fgColor : this.fgColor ; + c.arc(this.xy, this.xy, this.radius, sat, eat, false); + c.stroke(); + }; + + this.cancel = function () { + this.val(this.v); + }; + }; + + $.fn.dial = $.fn.knob = function (o) { + return this.each( + function () { + var d = new k.Dial(); + d.o = o; + d.$ = $(this); + d.run(); + } + ).parent(); + }; + +}); \ No newline at end of file diff --git a/templates/project/public/javascripts/jquery.masonry.min.js b/templates/project/public/javascripts/jquery.masonry.min.js new file mode 100644 index 0000000..67be988 --- /dev/null +++ b/templates/project/public/javascripts/jquery.masonry.min.js @@ -0,0 +1,10 @@ +/** + * jQuery Masonry v2.1.05 + * A dynamic layout plugin for jQuery + * The flip-side of CSS Floats + * http://masonry.desandro.com + * + * Licensed under the MIT license. + * Copyright 2012 David DeSandro + */ +(function(a,b,c){"use strict";var d=b.event,e;d.special.smartresize={setup:function(){b(this).bind("resize",d.special.smartresize.handler)},teardown:function(){b(this).unbind("resize",d.special.smartresize.handler)},handler:function(a,c){var d=this,f=arguments;a.type="smartresize",e&&clearTimeout(e),e=setTimeout(function(){b.event.handle.apply(d,f)},c==="execAsap"?0:100)}},b.fn.smartresize=function(a){return a?this.bind("smartresize",a):this.trigger("smartresize",["execAsap"])},b.Mason=function(a,c){this.element=b(c),this._create(a),this._init()},b.Mason.settings={isResizable:!0,isAnimated:!1,animationOptions:{queue:!1,duration:500},gutterWidth:0,isRTL:!1,isFitWidth:!1,containerStyle:{position:"relative"}},b.Mason.prototype={_filterFindBricks:function(a){var b=this.options.itemSelector;return b?a.filter(b).add(a.find(b)):a},_getBricks:function(a){var b=this._filterFindBricks(a).css({position:"absolute"}).addClass("masonry-brick");return b},_create:function(c){this.options=b.extend(!0,{},b.Mason.settings,c),this.styleQueue=[];var d=this.element[0].style;this.originalStyle={height:d.height||""};var e=this.options.containerStyle;for(var f in e)this.originalStyle[f]=d[f]||"";this.element.css(e),this.horizontalDirection=this.options.isRTL?"right":"left",this.offset={x:parseInt(this.element.css("padding-"+this.horizontalDirection),10),y:parseInt(this.element.css("padding-top"),10)},this.isFluid=this.options.columnWidth&&typeof this.options.columnWidth=="function";var g=this;setTimeout(function(){g.element.addClass("masonry")},0),this.options.isResizable&&b(a).bind("smartresize.masonry",function(){g.resize()}),this.reloadItems()},_init:function(a){this._getColumns(),this._reLayout(a)},option:function(a,c){b.isPlainObject(a)&&(this.options=b.extend(!0,this.options,a))},layout:function(a,b){for(var c=0,d=a.length;c + if @get('last') + if parseInt(@get('current')) > parseInt(@get('last')) then 'arrow-up' else 'arrow-down' + + ready: -> + Batman.setImmediate => + if @get('unordered') + $(@node).find('ol').remove() + else + $(@node).find('ul').remove() \ No newline at end of file diff --git a/templates/project/widgets/list/list.html b/templates/project/widgets/list/list.html new file mode 100644 index 0000000..cc90d42 --- /dev/null +++ b/templates/project/widgets/list/list.html @@ -0,0 +1,15 @@ +

+ +
    +
  1. + + +
  2. +
+ +
    +
  • + + +
  • +
\ No newline at end of file diff --git a/templates/project/widgets/list/list.scss b/templates/project/widgets/list/list.scss new file mode 100644 index 0000000..248cef8 --- /dev/null +++ b/templates/project/widgets/list/list.scss @@ -0,0 +1,50 @@ +// ---------------------------------------------------------------------------- +// Sass declarations +// ---------------------------------------------------------------------------- +$widget-list_background-color: #12b0c5; +$widget-list_value-color: #fff; + +$widget-list_title-color: lighten($widget-list_background-color, 45%); +$widget-list_label-color: lighten($widget-list_background-color, 45%); + +// ---------------------------------------------------------------------------- +// Widget-list styles +// ---------------------------------------------------------------------------- +.widget-list { + + background-color: $widget-list_background-color; + + .title { + color: $widget-list_title-color; + } + + ol, ul { + margin: 0 15px; + text-align: left; + color: $widget-list_label-color; + } + + ol { + list-style-position: inside; + } + + li { + margin-bottom: 5px; + } + + .list-nostyle { + list-style: none; + } + + .label { + color: $widget-list_label-color; + } + + .value { + float: right; + margin-left: 12px; + font-weight: 600; + color: $widget-list_value-color; + } + +} \ No newline at end of file diff --git a/templates/project/widgets/meter/meter.coffee b/templates/project/widgets/meter/meter.coffee new file mode 100644 index 0000000..4dbc2da --- /dev/null +++ b/templates/project/widgets/meter/meter.coffee @@ -0,0 +1,16 @@ +class AllTheThings.Meter extends AllTheThings.Widget + source: 'meter' + + @accessor 'value', Batman.Property.EasingSetter + + constructor: -> + super + @observe 'value', (value) -> + $(@node).find(".meter").val(value).trigger('change') + + ready: -> + Batman.setImmediate => + meter = $(@node).find(".meter") + meter.attr("data-bgcolor", meter.css("background-color")) + meter.attr("data-fgcolor", meter.css("color")) + meter.knob() \ No newline at end of file diff --git a/templates/project/widgets/meter/meter.html b/templates/project/widgets/meter/meter.html new file mode 100644 index 0000000..1476ba1 --- /dev/null +++ b/templates/project/widgets/meter/meter.html @@ -0,0 +1,5 @@ +

+ + + +

\ No newline at end of file diff --git a/templates/project/widgets/meter/meter.scss b/templates/project/widgets/meter/meter.scss new file mode 100644 index 0000000..b134361 --- /dev/null +++ b/templates/project/widgets/meter/meter.scss @@ -0,0 +1,35 @@ +// ---------------------------------------------------------------------------- +// Sass declarations +// ---------------------------------------------------------------------------- +$widget-meter_background-color: #9c4274; +$widget-meter_value-color: #fff; + +$widget-meter_title-color: lighten($widget-meter_background-color, 45%); +$widget-text_moreinfo-color: lighten($widget-meter_background-color, 45%); + +$widget-meter-bg: darken($widget-meter_background-color, 15%); +$widget-meter-fg: lighten($widget-meter_background-color, 75%); + +// ---------------------------------------------------------------------------- +// Widget-meter styles +// ---------------------------------------------------------------------------- +.widget-meter { + + background-color: $widget-meter_background-color; + + input.meter { + background-color: $widget-meter-bg; + color: $widget-meter-fg; + } + + .title { + color: $widget-meter_title-color; + } + + .text-moreinfo { + font-weight: 600; + font-size: 20px; + margin-top: 0; + } + +} \ No newline at end of file diff --git a/templates/project/widgets/number/number.coffee b/templates/project/widgets/number/number.coffee new file mode 100644 index 0000000..aa7cf8b --- /dev/null +++ b/templates/project/widgets/number/number.coffee @@ -0,0 +1,29 @@ +class AllTheThings.Number extends AllTheThings.Widget + source: 'number' + + @accessor 'current', Batman.Property.EasingSetter + + ready: -> + + @accessor 'difference', -> + if @get('last') + last = parseInt(@get('last')) + current = parseInt(@get('current')) + if last != 0 + diff = Math.abs(Math.round((current - last) / last * 100)) + "#{diff}%" + + @accessor 'arrow', -> + if @get('last') + if parseInt(@get('current')) > parseInt(@get('last')) then 'icon-arrow-up' else 'icon-arrow-down' + + @accessor 'statusStyle', -> + "status-#{@get('status')}" + + @accessor 'needsAttention', -> + @get('status') == 'warning' || @get('status') == 'danger' + + onData: (data) -> + super + if data.status + $(@get('node')).addClass("status-#{data.status}") \ No newline at end of file diff --git a/templates/project/widgets/number/number.html b/templates/project/widgets/number/number.html new file mode 100644 index 0000000..6e9a03e --- /dev/null +++ b/templates/project/widgets/number/number.html @@ -0,0 +1,9 @@ +

+ +

+ +

+ 50% +

+ +

2012-07-26 10:59AM

\ No newline at end of file diff --git a/templates/project/widgets/number/number.scss b/templates/project/widgets/number/number.scss new file mode 100644 index 0000000..6ae7f95 --- /dev/null +++ b/templates/project/widgets/number/number.scss @@ -0,0 +1,35 @@ +// ---------------------------------------------------------------------------- +// Sass declarations +// ---------------------------------------------------------------------------- +$widget-number_background-color: #00b1a4; +$widget-number_value-color: #fff; + +$widget-number_title-color: lighten($widget-number_background-color, 45%); +$widget-number_moreinfo-color: lighten($widget-number_background-color, 45%); + +// ---------------------------------------------------------------------------- +// Widget-number styles +// ---------------------------------------------------------------------------- +.widget-number { + + background-color: $widget-number_background-color; + + .title { + color: $widget-number_title-color; + } + + .value { + color: $widget-number_value-color; + } + + .change-rate { + font-weight: 300; + font-size: 30px; + color: $widget-number_value-color; + } + + .text-moreinfo { + color: $widget-number_moreinfo-color; + } + +} \ No newline at end of file diff --git a/templates/project/widgets/table/table.coffee b/templates/project/widgets/table/table.coffee new file mode 100644 index 0000000..5735316 --- /dev/null +++ b/templates/project/widgets/table/table.coffee @@ -0,0 +1,2 @@ +class AllTheThings.Table extends AllTheThings.Widget + source: 'table' \ No newline at end of file diff --git a/templates/project/widgets/table/table.html b/templates/project/widgets/table/table.html new file mode 100644 index 0000000..e5aeb99 --- /dev/null +++ b/templates/project/widgets/table/table.html @@ -0,0 +1,8 @@ +

+ + + + + + +
\ No newline at end of file diff --git a/templates/project/widgets/table/table.scss b/templates/project/widgets/table/table.scss new file mode 100644 index 0000000..0d38164 --- /dev/null +++ b/templates/project/widgets/table/table.scss @@ -0,0 +1,42 @@ +// ---------------------------------------------------------------------------- +// Sass declarations +// ---------------------------------------------------------------------------- +$widget-table_background-color: #82b53d; +$widget-table_value-color: #fff; + +$widget-table_title-color: lighten($widget-table_background-color, 45%); +$widget-table_label-color: lighten($widget-table_background-color, 45%); + +// ---------------------------------------------------------------------------- +// Widget-table styles +// ---------------------------------------------------------------------------- +.widget-table { + + background-color: $widget-table_background-color; + + th { + font-weight: 400; + font-size: 16px; + } + + td.label { + vertical-align: middle; + text-align: left; + font-size: 23px; + } + + .title { + color: $widget-table_title-color; + } + + .label { + color: $widget-table_label-color; + } + + .value { + font-weight: 700; + font-size: 54px; + color: $widget-table_value-color; + } + +} \ No newline at end of file diff --git a/templates/project/widgets/text/text.coffee b/templates/project/widgets/text/text.coffee new file mode 100644 index 0000000..754644e --- /dev/null +++ b/templates/project/widgets/text/text.coffee @@ -0,0 +1,2 @@ +class AllTheThings.Text extends AllTheThings.Widget + source: 'text' \ No newline at end of file diff --git a/templates/project/widgets/text/text.html b/templates/project/widgets/text/text.html new file mode 100644 index 0000000..02bd0f7 --- /dev/null +++ b/templates/project/widgets/text/text.html @@ -0,0 +1,5 @@ +

+ +

+ +

2012-07-26 10:59AM

\ No newline at end of file diff --git a/templates/project/widgets/text/text.scss b/templates/project/widgets/text/text.scss new file mode 100644 index 0000000..591c32e --- /dev/null +++ b/templates/project/widgets/text/text.scss @@ -0,0 +1,25 @@ +// ---------------------------------------------------------------------------- +// Sass declarations +// ---------------------------------------------------------------------------- +$widget-text_background-color: #ec663c; +$widget-text_value-color: #fff; + +$widget-text_title-color: lighten($widget-text_background-color, 30%); +$widget-text_moreinfo-color: lighten($widget-text_background-color, 30%); + +// ---------------------------------------------------------------------------- +// Widget-text styles +// ---------------------------------------------------------------------------- +.widget-text { + + background-color: $widget-text_background-color; + + .title { + color: $widget-text_title-color; + } + + .text-moreinfo { + color: $widget-text_moreinfo-color; + } + +} \ No newline at end of file diff --git a/templates/project/widgets/widget_sample/widget_sample.coffee b/templates/project/widgets/widget_sample/widget_sample.coffee deleted file mode 100644 index a1458df..0000000 --- a/templates/project/widgets/widget_sample/widget_sample.coffee +++ /dev/null @@ -1,2 +0,0 @@ -class AllTheThings.WidgetSample extends AllTheThings.Widget - source: 'widget_sample' \ No newline at end of file diff --git a/templates/project/widgets/widget_sample/widget_sample.html b/templates/project/widgets/widget_sample/widget_sample.html deleted file mode 100644 index eea0f1c..0000000 --- a/templates/project/widgets/widget_sample/widget_sample.html +++ /dev/null @@ -1,4 +0,0 @@ -

w00t! w00t! You've setup your first dashboard!

-

To celebrate, enjoy some of these fun Batman quotes!

- - diff --git a/templates/project/widgets/widget_sample/widget_sample.scss b/templates/project/widgets/widget_sample/widget_sample.scss deleted file mode 100644 index e69de29..0000000 diff --git a/templates/widget/%name%.coffee.tt b/templates/widget/%name%.coffee.tt deleted file mode 100644 index e3e5938..0000000 --- a/templates/widget/%name%.coffee.tt +++ /dev/null @@ -1,2 +0,0 @@ -class AllTheThings.<%= Thor::Util.camel_case(name) %> extends AllTheThings.Widget - source: '<%= name %>' \ No newline at end of file diff --git a/templates/widget/%name%.html b/templates/widget/%name%.html deleted file mode 100644 index e69de29..0000000 diff --git a/templates/widget/%name%.scss b/templates/widget/%name%.scss deleted file mode 100644 index e69de29..0000000 diff --git a/templates/widget/%name%/%name%.coffee.tt b/templates/widget/%name%/%name%.coffee.tt new file mode 100644 index 0000000..5ddfb1b --- /dev/null +++ b/templates/widget/%name%/%name%.coffee.tt @@ -0,0 +1,11 @@ +class AllTheThings.<%= Thor::Util.camel_case(name) %> extends AllTheThings.Widget + source: '<%= name %>' + + ready: -> + # This is fired when the widget is done being rendered + + onData: (data) -> + # Handle incoming data + # You can access the html node of this widget with `@node` + # Example: $(@node).effect("pulsate") will make the node flash each time data comes in. + super \ No newline at end of file diff --git a/templates/widget/%name%/%name%.html b/templates/widget/%name%/%name%.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/widget/%name%/%name%.scss.tt b/templates/widget/%name%/%name%.scss.tt new file mode 100644 index 0000000..0da5c49 --- /dev/null +++ b/templates/widget/%name%/%name%.scss.tt @@ -0,0 +1,3 @@ +.widget-<%= name %> { + +} \ No newline at end of file diff --git a/vendor/javascripts/application.coffee b/vendor/javascripts/application.coffee index d311687..6704ee5 100644 --- a/vendor/javascripts/application.coffee +++ b/vendor/javascripts/application.coffee @@ -1,11 +1,22 @@ -Batman.Filters.PrettyNumber = (num) -> +Batman.Filters.prettyNumber = (num) -> num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") unless isNaN(num) Batman.Filters.dashize = (str) -> dashes_rx1 = /([A-Z]+)([A-Z][a-z])/g; dashes_rx2 = /([a-z\d])([A-Z])/g; - return str.replace(dashes_rx1, '$1_$2').replace(dashes_rx2, '$1_$2').replace('_', '-').toLowerCase(); + return str.replace(dashes_rx1, '$1_$2').replace(dashes_rx2, '$1_$2').replace('_', '-').toLowerCase() + +Batman.Filters.shortenedNumber = (num) -> + return if isNaN(num) + if num >= 1000000000 + (num / 1000000000).toFixed(1) + 'B' + else if num >= 1000000 + (num / 1000000).toFixed(1) + 'M' + else if num >= 1000 + (num / 1000).toFixed(1) + 'K' + else + num class window.AllTheThings extends Batman.App @root -> @@ -37,7 +48,7 @@ AllTheThings.widgets = widgets = {} AllTheThings.lastEvents = lastEvents = {} source = new EventSource('/events') -source.addEventListener 'open', (e)-> +source.addEventListener 'open', (e) -> console.log("Connection opened") source.addEventListener 'error', (e)-> diff --git a/vendor/javascripts/widget.coffee b/vendor/javascripts/widget.coffee index f87b9fb..5c5c1a3 100644 --- a/vendor/javascripts/widget.coffee +++ b/vendor/javascripts/widget.coffee @@ -8,7 +8,7 @@ class AllTheThings.Widget extends Batman.View @mixin(AllTheThings.lastEvents[@id]) # in case the events from the server came before the widget was rendered type = Batman.Filters.dashize(@view) - $(@node).addClass("widget #{type} #{@id}") + $(@node).addClass("widget widget-#{type} #{@id}") onData: (data) => @mixin(data) \ No newline at end of file -- cgit v1.2.3