pulse_meter-dygraphs_visualizer 0.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. data/.gitignore +19 -0
  2. data/.rbenv-version +1 -0
  3. data/.rspec +1 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +9 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +22 -0
  8. data/Procfile +3 -0
  9. data/README.md +40 -0
  10. data/Rakefile +54 -0
  11. data/examples/basic.ru +125 -0
  12. data/examples/basic_sensor_data.rb +97 -0
  13. data/lib/pulse_meter/dygraphs_visualize/app.rb +78 -0
  14. data/lib/pulse_meter/dygraphs_visualize/base.rb +15 -0
  15. data/lib/pulse_meter/dygraphs_visualize/coffee/application.coffee +37 -0
  16. data/lib/pulse_meter/dygraphs_visualize/coffee/collections/page_info_list.coffee +17 -0
  17. data/lib/pulse_meter/dygraphs_visualize/coffee/collections/sensor_info_list.coffee +4 -0
  18. data/lib/pulse_meter/dygraphs_visualize/coffee/collections/widget_list.coffee +14 -0
  19. data/lib/pulse_meter/dygraphs_visualize/coffee/extensions.coffee +26 -0
  20. data/lib/pulse_meter/dygraphs_visualize/coffee/models/dinamic_widget.coffee +34 -0
  21. data/lib/pulse_meter/dygraphs_visualize/coffee/models/page_info.coffee +2 -0
  22. data/lib/pulse_meter/dygraphs_visualize/coffee/models/sensor_info.coffee +2 -0
  23. data/lib/pulse_meter/dygraphs_visualize/coffee/models/widget.coffee +63 -0
  24. data/lib/pulse_meter/dygraphs_visualize/coffee/presenters/line.coffee +1 -0
  25. data/lib/pulse_meter/dygraphs_visualize/coffee/presenters/series.coffee +26 -0
  26. data/lib/pulse_meter/dygraphs_visualize/coffee/presenters/stack.coffee +1 -0
  27. data/lib/pulse_meter/dygraphs_visualize/coffee/presenters/timeline.coffee +13 -0
  28. data/lib/pulse_meter/dygraphs_visualize/coffee/presenters/widget.coffee +48 -0
  29. data/lib/pulse_meter/dygraphs_visualize/coffee/router.coffee +21 -0
  30. data/lib/pulse_meter/dygraphs_visualize/coffee/views/dynamic_chart.coffee +92 -0
  31. data/lib/pulse_meter/dygraphs_visualize/coffee/views/dynamic_widget.coffee +58 -0
  32. data/lib/pulse_meter/dygraphs_visualize/coffee/views/page_title.coffee +17 -0
  33. data/lib/pulse_meter/dygraphs_visualize/coffee/views/page_titles.coffee +15 -0
  34. data/lib/pulse_meter/dygraphs_visualize/coffee/views/sensor_info_list.coffee +19 -0
  35. data/lib/pulse_meter/dygraphs_visualize/coffee/views/widget.coffee +91 -0
  36. data/lib/pulse_meter/dygraphs_visualize/coffee/views/widget_chart.coffee +15 -0
  37. data/lib/pulse_meter/dygraphs_visualize/coffee/views/widget_list.coffee +15 -0
  38. data/lib/pulse_meter/dygraphs_visualize/dsl/base.rb +131 -0
  39. data/lib/pulse_meter/dygraphs_visualize/dsl/errors.rb +40 -0
  40. data/lib/pulse_meter/dygraphs_visualize/dsl/layout.rb +27 -0
  41. data/lib/pulse_meter/dygraphs_visualize/dsl/page.rb +26 -0
  42. data/lib/pulse_meter/dygraphs_visualize/dsl/sensor.rb +20 -0
  43. data/lib/pulse_meter/dygraphs_visualize/dsl/widget.rb +37 -0
  44. data/lib/pulse_meter/dygraphs_visualize/dsl/widgets/line.rb +22 -0
  45. data/lib/pulse_meter/dygraphs_visualize/dsl/widgets/stack.rb +29 -0
  46. data/lib/pulse_meter/dygraphs_visualize/layout.rb +79 -0
  47. data/lib/pulse_meter/dygraphs_visualize/page.rb +25 -0
  48. data/lib/pulse_meter/dygraphs_visualize/public/css/application.css +61 -0
  49. data/lib/pulse_meter/dygraphs_visualize/public/css/bootstrap.css +4883 -0
  50. data/lib/pulse_meter/dygraphs_visualize/public/css/bootstrap.min.css +729 -0
  51. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  52. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  53. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  54. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  55. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  56. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-bg_glass_75_ffffff_1x400.png +0 -0
  57. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  58. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
  59. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-icons_222222_256x240.png +0 -0
  60. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-icons_2e83ff_256x240.png +0 -0
  61. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-icons_454545_256x240.png +0 -0
  62. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-icons_888888_256x240.png +0 -0
  63. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-icons_cd0a0a_256x240.png +0 -0
  64. data/lib/pulse_meter/dygraphs_visualize/public/css/images/ui-icons_f6cf3b_256x240.png +0 -0
  65. data/lib/pulse_meter/dygraphs_visualize/public/css/jquery-ui-1.8.16.bootstrap.css +1320 -0
  66. data/lib/pulse_meter/dygraphs_visualize/public/favicon.ico +208 -0
  67. data/lib/pulse_meter/dygraphs_visualize/public/img/glyphicons-halflings-white.png +0 -0
  68. data/lib/pulse_meter/dygraphs_visualize/public/img/glyphicons-halflings.png +0 -0
  69. data/lib/pulse_meter/dygraphs_visualize/public/js/application.js +833 -0
  70. data/lib/pulse_meter/dygraphs_visualize/public/js/backbone-min.js +38 -0
  71. data/lib/pulse_meter/dygraphs_visualize/public/js/bootstrap.js +1835 -0
  72. data/lib/pulse_meter/dygraphs_visualize/public/js/dygraph-combined.js +2 -0
  73. data/lib/pulse_meter/dygraphs_visualize/public/js/jquery-1.7.2.min.js +4 -0
  74. data/lib/pulse_meter/dygraphs_visualize/public/js/jquery-ui-1.8.16.bootstrap.min.js +791 -0
  75. data/lib/pulse_meter/dygraphs_visualize/public/js/jquery-ui-1.8.23.custom.min.js +21 -0
  76. data/lib/pulse_meter/dygraphs_visualize/public/js/jquery-ui-timepicker-addon.js +1687 -0
  77. data/lib/pulse_meter/dygraphs_visualize/public/js/json2.js +487 -0
  78. data/lib/pulse_meter/dygraphs_visualize/public/js/underscore-min.js +32 -0
  79. data/lib/pulse_meter/dygraphs_visualize/sensor.rb +63 -0
  80. data/lib/pulse_meter/dygraphs_visualize/series_color.rb +39 -0
  81. data/lib/pulse_meter/dygraphs_visualize/series_extractor.rb +107 -0
  82. data/lib/pulse_meter/dygraphs_visualize/views/main.haml +30 -0
  83. data/lib/pulse_meter/dygraphs_visualize/views/sensors.haml +75 -0
  84. data/lib/pulse_meter/dygraphs_visualize/views/widgets/extend_options.haml +11 -0
  85. data/lib/pulse_meter/dygraphs_visualize/views/widgets/line.haml +43 -0
  86. data/lib/pulse_meter/dygraphs_visualize/views/widgets/stack.haml +43 -0
  87. data/lib/pulse_meter/dygraphs_visualize/widget.rb +38 -0
  88. data/lib/pulse_meter/dygraphs_visualize/widgets/timeline.rb +107 -0
  89. data/lib/pulse_meter/dygraphs_visualizer.rb +34 -0
  90. data/lib/pulse_meter_dygraphs_visualizer.rb +2 -0
  91. data/pulse_meter-dygraphs_visualizer.gemspec +41 -0
  92. data/spec/pulse_meter/dygraphs_visualize/app_spec.rb +27 -0
  93. data/spec/pulse_meter/dygraphs_visualize/dsl/layout_spec.rb +64 -0
  94. data/spec/pulse_meter/dygraphs_visualize/dsl/page_spec.rb +62 -0
  95. data/spec/pulse_meter/dygraphs_visualize/dsl/sensor_spec.rb +30 -0
  96. data/spec/pulse_meter/dygraphs_visualize/dsl/widget_spec.rb +6 -0
  97. data/spec/pulse_meter/dygraphs_visualize/dsl/widgets/line_spec.rb +44 -0
  98. data/spec/pulse_meter/dygraphs_visualize/dsl/widgets/stack_spec.rb +55 -0
  99. data/spec/pulse_meter/dygraphs_visualize/layout_spec.rb +54 -0
  100. data/spec/pulse_meter/dygraphs_visualize/page_spec.rb +145 -0
  101. data/spec/pulse_meter/dygraphs_visualize/sensor_spec.rb +120 -0
  102. data/spec/pulse_meter/dygraphs_visualize/series_extractor_spec.rb +80 -0
  103. data/spec/pulse_meter/dygraphs_visualize/widgets/line_spec.rb +6 -0
  104. data/spec/pulse_meter/dygraphs_visualize/widgets/stack_spec.rb +6 -0
  105. data/spec/pulse_meter/dygraphs_visualizer_spec.rb +42 -0
  106. data/spec/shared_examples/dsl_widget.rb +106 -0
  107. data/spec/shared_examples/widget.rb +97 -0
  108. data/spec/spec_helper.rb +36 -0
  109. metadata +497 -0
@@ -0,0 +1,32 @@
1
+ // Underscore.js 1.3.3
2
+ // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
3
+ // Underscore is freely distributable under the MIT license.
4
+ // Portions of Underscore are inspired or borrowed from Prototype,
5
+ // Oliver Steele's Functional, and John Resig's Micro-Templating.
6
+ // For all details and documentation:
7
+ // http://documentcloud.github.com/underscore
8
+ (function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
9
+ c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
10
+ g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
11
+ c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
12
+ a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
13
+ c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
14
+ a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
15
+ function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
16
+ (e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
17
+ j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
18
+ 0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
19
+ e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
20
+ i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
21
+ 1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
22
+ i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
23
+ g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
24
+ return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
25
+ c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
26
+ function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
27
+ b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
28
+ b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
29
+ function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
30
+ u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
31
+ b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
32
+ this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
@@ -0,0 +1,63 @@
1
+ require "pulse_meter/dygraphs_visualize/series_extractor"
2
+
3
+ module PulseMeter
4
+ module DygraphsVisualize
5
+ class Sensor < Base
6
+ def last_value(now, need_incomplete=false)
7
+ sensor = real_sensor
8
+
9
+ sensor_data = if need_incomplete
10
+ sensor.timeline_within(now - sensor.interval, now).first
11
+ else
12
+ sensor.timeline_within(now - sensor.interval * 2, now).first
13
+ end
14
+
15
+ if sensor_data.is_a?(PulseMeter::SensorData)
16
+ sensor_data.value
17
+ else
18
+ nil
19
+ end
20
+ end
21
+
22
+ def last_point_data(now, need_incomplete=false)
23
+ extractor.point_data(last_value(now, need_incomplete))
24
+ end
25
+
26
+ def timeline_data(from, till, need_incomplete = false)
27
+ sensor = real_sensor
28
+ timeline_data = sensor.timeline_within(from, till)
29
+ timeline_data.pop unless need_incomplete
30
+ extractor.series_data(timeline_data)
31
+ end
32
+
33
+ def annotation
34
+ real_sensor.annotation || ''
35
+ end
36
+
37
+ def type
38
+ real_sensor.class
39
+ end
40
+
41
+ def interval
42
+ real_sensor.interval
43
+ end
44
+
45
+ def value
46
+ real_sensor.value
47
+ end
48
+
49
+ def extractor
50
+ PulseMeter::DygraphsVisualize.extractor(self)
51
+ end
52
+
53
+ protected
54
+
55
+ def real_sensor
56
+ # TODO add !temporarily! caching if this will be called too frequently
57
+ PulseMeter::Sensor::Base.restore(name)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+
@@ -0,0 +1,39 @@
1
+ require 'zlib'
2
+ module PulseMeter
3
+ module DygraphsVisualize
4
+ class SeriesColor
5
+ def initialize(options = {})
6
+ @forced_color = options[:color]
7
+ @name = options[:name] || rand.to_s
8
+ end
9
+
10
+ def color
11
+ @color ||= calculate_color
12
+ end
13
+
14
+ private
15
+
16
+ def calculate_color
17
+ @forced_color || calculate_color_by_name
18
+ end
19
+
20
+ def calculate_color_by_name
21
+ '#' + hashed_color_bytes.map{|b| '%02x' % darken(b)}.join
22
+ end
23
+
24
+ def hashed_color_bytes
25
+ n = Zlib::crc32(@name)
26
+ [
27
+ n & 0xFF,
28
+ (n & 0xFF00) >> 8,
29
+ (n & 0xFF0000) >> 16
30
+ ]
31
+ end
32
+
33
+ def darken(byte)
34
+ byte / 2
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,107 @@
1
+ module PulseMeter
2
+ module DygraphsVisualize
3
+ module SeriesExtractor
4
+ class Simple
5
+ def initialize(sensor)
6
+ @sensor = sensor
7
+ end
8
+
9
+ def opts_to_add
10
+ opts = {}
11
+ opts[:color] = @sensor.color if @sensor.color
12
+ opts[:name] = @sensor.annotation
13
+ opts
14
+ end
15
+
16
+ def series_data(timeline_data)
17
+ [{
18
+ data: timeline_data.map{|sd| {x: sd.start_time.to_i*1000, y: to_float(sd.value)}}
19
+ }.merge(opts_to_add)]
20
+ end
21
+
22
+ def point_data(value)
23
+ [{
24
+ y: to_float(value)
25
+ }.merge(opts_to_add)]
26
+ end
27
+
28
+ protected
29
+
30
+ def to_float(val)
31
+ val && val.to_f
32
+ end
33
+ end
34
+
35
+
36
+ class Hashed < Simple
37
+
38
+ def parse_data(value)
39
+ if value
40
+ if value.is_a?(String)
41
+ JSON.parse(value)
42
+ else
43
+ value
44
+ end
45
+ else
46
+ {}
47
+ end
48
+ end
49
+
50
+ def point_data(value)
51
+ data = parse_data(value)
52
+ data.keys.map do |k|
53
+ {
54
+ y: to_float(data[k]),
55
+ name: series_title(k)
56
+ }
57
+ end
58
+ end
59
+
60
+ def series_title(key)
61
+ annotation = @sensor.annotation
62
+ if annotation && !annotation.empty?
63
+ "#{annotation}: #{key}"
64
+ else
65
+ key
66
+ end
67
+ end
68
+
69
+ def series_data(timeline_data)
70
+ series_data = {}
71
+ parsed_data = timeline_data.map do |sd|
72
+ data = parse_data(sd.value)
73
+ data.keys.each{|k| series_data[k] ||= {name: series_title(k), data: []}}
74
+ [sd.start_time.to_i*1000, data]
75
+ end
76
+
77
+ series_names = series_data.keys.sort
78
+ parsed_data.each do |data|
79
+ series_names.each do |k|
80
+ series_data[k][:data] << {x: data[0], y: to_float(data[1][k])}
81
+ end
82
+ end
83
+ series_data.values
84
+ end
85
+ end
86
+ end
87
+
88
+ SPECIAL_SERIES_EXTRACTORS = {
89
+ 'HashedCounter' => SeriesExtractor::Hashed,
90
+ 'HashedIndicator' => SeriesExtractor::Hashed,
91
+ 'MultiPercentile' => SeriesExtractor::Hashed
92
+ }.freeze
93
+
94
+ DEFAULT_SERIES_EXTRACTOR = SeriesExtractor::Simple
95
+
96
+ def extractor(sensor)
97
+ key = sensor.type.to_s.split('::').last
98
+ extractor_class = SPECIAL_SERIES_EXTRACTORS[key] || DEFAULT_SERIES_EXTRACTOR
99
+ extractor_class.new(sensor)
100
+ end
101
+
102
+ module_function :extractor
103
+
104
+ end
105
+ end
106
+
107
+
@@ -0,0 +1,30 @@
1
+ !!!
2
+ %head
3
+ %title= @title
4
+ :javascript
5
+ var ROOT = "#{url('/')}";
6
+ = include_gon
7
+ - %w{dygraph-combined.js jquery-1.7.2.min.js jquery-ui-1.8.16.bootstrap.min.js jquery-ui-timepicker-addon.js json2.js underscore-min.js backbone-min.js application.js bootstrap.js}.each do |jsfile|
8
+ %script{type: 'text/javascript', src: url("/js/#{jsfile}")}
9
+ - %w{bootstrap.min.css application.css jquery-ui-1.8.16.bootstrap.css}.each do |cssfile|
10
+ %link{rel: 'stylesheet', href: url("/css/#{cssfile}"), type: 'text/css', media: 'screen'}
11
+ %script{type: 'text/javascript', src: "https://www.google.com/jsapi"}
12
+ :javascript
13
+ google.load("visualization", "1", {packages:["corechart"]});
14
+ google.setOnLoadCallback(drawChart);
15
+ function drawChart() {
16
+ document.startApp();
17
+ }
18
+ %body
19
+ - %w(line stack).each do |wtype|
20
+ = partial "widgets/#{wtype}"
21
+ = partial "sensors"
22
+
23
+ .navbar.navbar-fixed-top
24
+ .navbar-inner
25
+ .container-fluid
26
+ %a.brand{href: '#/custom'}= @title
27
+ %ul.nav#page-titles
28
+
29
+ .container-fluid#main
30
+ .row-fluid#widgets
@@ -0,0 +1,75 @@
1
+ %script#sensor-list{type: 'text/template'}
2
+ %table#sensors.table.table-striped
3
+ %tr
4
+ %th
5
+ %th Title
6
+ %th Type
7
+ %th Interval
8
+ <% _.each(sensors, function(sensor) { %>
9
+ %tr
10
+ %td
11
+ :plain
12
+ <input class="sensor-box" type="checkbox" id="<%- sensor.id %>" />
13
+ %td
14
+ <%- sensor.annotation %>
15
+ %sub <%- sensor.id %>
16
+ %td <%- sensor.type %>
17
+ %td <%- sensor.interval.humanize() %>
18
+ <% }) %>
19
+
20
+ %script#dynamic-widget-error{type: 'text/template'}
21
+ .alert.alert-error
22
+ .button.close{'data-dismiss' => 'alert'} &times;
23
+ <%- error %>
24
+
25
+ %script#dynamic-widget-plotarea{type: 'text/template'}
26
+ .well
27
+ #chart-widget
28
+ %h3 Choose sensors to plot
29
+ #dynamic-chart-controls
30
+ .form-inline
31
+ %button#refresh-chart.btn.btn-mini
32
+ %i.icon-refresh
33
+ Refresh chart
34
+ .form-inline
35
+ Timespan:
36
+ %select#extend-timespan-val.btn-mini.span3
37
+ = partial "widgets/extend_options"
38
+ %button#extend-timespan.btn.btn-mini
39
+ %i.icon-arrow-left
40
+ Extend
41
+ %button#reset-timespan.btn.btn-mini
42
+ %i.icon-arrow-right
43
+ Reset
44
+ .form-inline
45
+ %label
46
+ From:
47
+ %span#start-time
48
+ %input.datepicker
49
+ %label
50
+ Till:
51
+ %span#end-time
52
+ %input.datepicker
53
+ %button#set-interval.btn.btn-mini
54
+ %i.icon-time
55
+ Set interval
56
+
57
+
58
+ %script#dynamic-widget{type: 'text/template'}
59
+ .span6
60
+ #sensor-controls.form-horizontal
61
+ %p.form-inline
62
+ Chart type
63
+ %select#chart-type{value: 'line'}
64
+ %option{value: 'line'} Line
65
+ %option{value: 'stack'} Stack
66
+ %button#draw.btn.btn-mini.btn-primary
67
+ %i.icon-play
68
+ Draw
69
+ %button#refresh.btn.btn-mini
70
+ %i.icon-refresh
71
+ Refresh sensor list
72
+ #sensor-list-area
73
+ .span6
74
+ #dynamic-plotarea
75
+ #errors
@@ -0,0 +1,11 @@
1
+ %option{value: 60, selected: true} 1 minute
2
+ %option{value: 60 * 5} 5 minutes
3
+ %option{value: 60 * 30} 30 minutes
4
+ %option{value: 60 * 60 * 2} 2 hours
5
+ %option{value: 60 * 60 * 6 } 6 hours
6
+ %option{value: 60 * 60 * 24} 1 day
7
+ %option{value: 60 * 60 * 24 * 2} 2 days
8
+ %option{value: 60 * 60 * 24 * 7} 1 week
9
+ %option{value: 60 * 60 * 24 * 7 * 2} 2 weeks
10
+ %option{value: 60 * 60 * 24 * 30} 1 month
11
+
@@ -0,0 +1,43 @@
1
+ %script.widget-template{type: 'text/template', 'data-widget-type' => 'line'}
2
+ #plotarea
3
+ #chart-controls
4
+ .form-inline
5
+ %button#refresh.btn.btn-mini
6
+ %i.icon-refresh
7
+ Refresh
8
+ %label
9
+ Refresh:
10
+ %input.btn-mini#need-refresh{type: :checkbox, checked: :true}
11
+ %a#configure-button.btn.btn-mini{href: "#configure", role: 'button', 'data-toggle' => 'modal'}
12
+ %i.icon-wrench
13
+ Configure...
14
+ .modal.hide.fade#configure
15
+ .modal-header
16
+ %button.close{type: 'button', 'data-dismiss' => 'modal', 'aria-hidden' => 'true'} &times;
17
+ %h3 Configure
18
+ .modal-body
19
+ %p
20
+ %label
21
+ Timespan:
22
+ %select#extend-timespan-val.btn-mini.span3
23
+ = partial "widgets/extend_options"
24
+ %button#extend-timespan.btn.btn-mini
25
+ %i.icon-arrow-left
26
+ Extend
27
+ %button#reset-timespan.btn.btn-mini
28
+ %i.icon-arrow-right
29
+ Reset
30
+ %hr
31
+ %p
32
+ %label
33
+ From:
34
+ %span#start-time
35
+ %input.datepicker
36
+ %label
37
+ Till:
38
+ %span#end-time
39
+ %input.datepicker
40
+ %button#set-interval.btn.btn-mini
41
+ %i.icon-time
42
+ Set interval
43
+
@@ -0,0 +1,43 @@
1
+ %script.widget-template{type: 'text/template', 'data-widget-type' => 'stack'}
2
+ #plotarea
3
+ #chart-controls
4
+ .form-inline
5
+ %button#refresh.btn.btn-mini
6
+ %i.icon-refresh
7
+ Refresh
8
+ %label
9
+ Refresh:
10
+ %input.btn-mini#need-refresh{type: :checkbox, checked: :true}
11
+ %a#configure-button.btn.btn-mini{href: "#configure", role: 'button', 'data-toggle' => 'modal'}
12
+ %i.icon-wrench
13
+ Configure...
14
+ .modal.hide.fade#configure
15
+ .modal-header
16
+ %button.close{type: 'button', 'data-dismiss' => 'modal', 'aria-hidden' => 'true'} &times;
17
+ %h3 Configure
18
+ .modal-body
19
+ %p
20
+ %label
21
+ Timespan:
22
+ %select#extend-timespan-val.btn-mini.span3
23
+ = partial "widgets/extend_options"
24
+ %button#extend-timespan.btn.btn-mini
25
+ %i.icon-arrow-left
26
+ Extend
27
+ %button#reset-timespan.btn.btn-mini
28
+ %i.icon-arrow-right
29
+ Reset
30
+ %hr
31
+ %p
32
+ %label
33
+ From:
34
+ %span#start-time
35
+ %input.datepicker
36
+ %label
37
+ Till:
38
+ %span#end-time
39
+ %input.datepicker
40
+ %button#set-interval.btn.btn-mini
41
+ %i.icon-time
42
+ Set interval
43
+