pulse_meter_visualizer 0.4.11

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 (124) 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 +8 -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 +72 -0
  11. data/examples/basic.ru +145 -0
  12. data/examples/basic_sensor_data.rb +96 -0
  13. data/lib/pulse_meter/visualize/app.rb +78 -0
  14. data/lib/pulse_meter/visualize/base.rb +15 -0
  15. data/lib/pulse_meter/visualize/coffee/application.coffee +40 -0
  16. data/lib/pulse_meter/visualize/coffee/collections/page_info_list.coffee +17 -0
  17. data/lib/pulse_meter/visualize/coffee/collections/sensor_info_list.coffee +4 -0
  18. data/lib/pulse_meter/visualize/coffee/collections/widget_list.coffee +14 -0
  19. data/lib/pulse_meter/visualize/coffee/extensions.coffee +26 -0
  20. data/lib/pulse_meter/visualize/coffee/models/dinamic_widget.coffee +34 -0
  21. data/lib/pulse_meter/visualize/coffee/models/page_info.coffee +2 -0
  22. data/lib/pulse_meter/visualize/coffee/models/sensor_info.coffee +2 -0
  23. data/lib/pulse_meter/visualize/coffee/models/widget.coffee +54 -0
  24. data/lib/pulse_meter/visualize/coffee/presenters/area.coffee +2 -0
  25. data/lib/pulse_meter/visualize/coffee/presenters/gauge.coffee +11 -0
  26. data/lib/pulse_meter/visualize/coffee/presenters/line.coffee +2 -0
  27. data/lib/pulse_meter/visualize/coffee/presenters/pie.coffee +20 -0
  28. data/lib/pulse_meter/visualize/coffee/presenters/series.coffee +44 -0
  29. data/lib/pulse_meter/visualize/coffee/presenters/table.coffee +10 -0
  30. data/lib/pulse_meter/visualize/coffee/presenters/timeline.coffee +13 -0
  31. data/lib/pulse_meter/visualize/coffee/presenters/widget.coffee +65 -0
  32. data/lib/pulse_meter/visualize/coffee/router.coffee +21 -0
  33. data/lib/pulse_meter/visualize/coffee/views/dynamic_chart.coffee +91 -0
  34. data/lib/pulse_meter/visualize/coffee/views/dynamic_widget.coffee +58 -0
  35. data/lib/pulse_meter/visualize/coffee/views/page_title.coffee +17 -0
  36. data/lib/pulse_meter/visualize/coffee/views/page_titles.coffee +15 -0
  37. data/lib/pulse_meter/visualize/coffee/views/sensor_info_list.coffee +19 -0
  38. data/lib/pulse_meter/visualize/coffee/views/widget.coffee +99 -0
  39. data/lib/pulse_meter/visualize/coffee/views/widget_chart.coffee +13 -0
  40. data/lib/pulse_meter/visualize/coffee/views/widget_list.coffee +15 -0
  41. data/lib/pulse_meter/visualize/dsl/base.rb +131 -0
  42. data/lib/pulse_meter/visualize/dsl/errors.rb +40 -0
  43. data/lib/pulse_meter/visualize/dsl/layout.rb +27 -0
  44. data/lib/pulse_meter/visualize/dsl/page.rb +33 -0
  45. data/lib/pulse_meter/visualize/dsl/sensor.rb +20 -0
  46. data/lib/pulse_meter/visualize/dsl/widget.rb +37 -0
  47. data/lib/pulse_meter/visualize/dsl/widgets/area.rb +20 -0
  48. data/lib/pulse_meter/visualize/dsl/widgets/gauge.rb +12 -0
  49. data/lib/pulse_meter/visualize/dsl/widgets/line.rb +21 -0
  50. data/lib/pulse_meter/visualize/dsl/widgets/pie.rb +16 -0
  51. data/lib/pulse_meter/visualize/dsl/widgets/table.rb +19 -0
  52. data/lib/pulse_meter/visualize/layout.rb +79 -0
  53. data/lib/pulse_meter/visualize/page.rb +25 -0
  54. data/lib/pulse_meter/visualize/public/css/application.css +56 -0
  55. data/lib/pulse_meter/visualize/public/css/bootstrap.css +4883 -0
  56. data/lib/pulse_meter/visualize/public/css/bootstrap.min.css +729 -0
  57. data/lib/pulse_meter/visualize/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  58. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  59. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  60. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  61. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  62. data/lib/pulse_meter/visualize/public/css/images/ui-bg_glass_75_ffffff_1x400.png +0 -0
  63. data/lib/pulse_meter/visualize/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  64. data/lib/pulse_meter/visualize/public/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
  65. data/lib/pulse_meter/visualize/public/css/images/ui-icons_222222_256x240.png +0 -0
  66. data/lib/pulse_meter/visualize/public/css/images/ui-icons_2e83ff_256x240.png +0 -0
  67. data/lib/pulse_meter/visualize/public/css/images/ui-icons_454545_256x240.png +0 -0
  68. data/lib/pulse_meter/visualize/public/css/images/ui-icons_888888_256x240.png +0 -0
  69. data/lib/pulse_meter/visualize/public/css/images/ui-icons_cd0a0a_256x240.png +0 -0
  70. data/lib/pulse_meter/visualize/public/css/images/ui-icons_f6cf3b_256x240.png +0 -0
  71. data/lib/pulse_meter/visualize/public/css/jquery-ui-1.8.16.bootstrap.css +1320 -0
  72. data/lib/pulse_meter/visualize/public/favicon.ico +208 -0
  73. data/lib/pulse_meter/visualize/public/img/glyphicons-halflings-white.png +0 -0
  74. data/lib/pulse_meter/visualize/public/img/glyphicons-halflings.png +0 -0
  75. data/lib/pulse_meter/visualize/public/js/application.js +973 -0
  76. data/lib/pulse_meter/visualize/public/js/backbone-min.js +38 -0
  77. data/lib/pulse_meter/visualize/public/js/bootstrap.js +1835 -0
  78. data/lib/pulse_meter/visualize/public/js/jquery-1.7.2.min.js +4 -0
  79. data/lib/pulse_meter/visualize/public/js/jquery-ui-1.8.16.bootstrap.min.js +791 -0
  80. data/lib/pulse_meter/visualize/public/js/jquery-ui-1.8.23.custom.min.js +21 -0
  81. data/lib/pulse_meter/visualize/public/js/jquery-ui-timepicker-addon.js +1687 -0
  82. data/lib/pulse_meter/visualize/public/js/json2.js +487 -0
  83. data/lib/pulse_meter/visualize/public/js/underscore-min.js +32 -0
  84. data/lib/pulse_meter/visualize/sensor.rb +63 -0
  85. data/lib/pulse_meter/visualize/series_extractor.rb +107 -0
  86. data/lib/pulse_meter/visualize/views/main.haml +30 -0
  87. data/lib/pulse_meter/visualize/views/sensors.haml +76 -0
  88. data/lib/pulse_meter/visualize/views/widgets/area.haml +53 -0
  89. data/lib/pulse_meter/visualize/views/widgets/extend_options.haml +11 -0
  90. data/lib/pulse_meter/visualize/views/widgets/gauge.haml +13 -0
  91. data/lib/pulse_meter/visualize/views/widgets/line.haml +54 -0
  92. data/lib/pulse_meter/visualize/views/widgets/pie.haml +13 -0
  93. data/lib/pulse_meter/visualize/views/widgets/table.haml +45 -0
  94. data/lib/pulse_meter/visualize/widget.rb +38 -0
  95. data/lib/pulse_meter/visualize/widgets/gauge.rb +47 -0
  96. data/lib/pulse_meter/visualize/widgets/pie.rb +36 -0
  97. data/lib/pulse_meter/visualize/widgets/timeline.rb +114 -0
  98. data/lib/pulse_meter/visualizer.rb +38 -0
  99. data/lib/pulse_meter_visualizer.rb +2 -0
  100. data/pulse_meter_visualizer.gemspec +41 -0
  101. data/spec/pulse_meter/visualize/app_spec.rb +27 -0
  102. data/spec/pulse_meter/visualize/dsl/layout_spec.rb +64 -0
  103. data/spec/pulse_meter/visualize/dsl/page_spec.rb +62 -0
  104. data/spec/pulse_meter/visualize/dsl/sensor_spec.rb +30 -0
  105. data/spec/pulse_meter/visualize/dsl/widget_spec.rb +6 -0
  106. data/spec/pulse_meter/visualize/dsl/widgets/area_spec.rb +44 -0
  107. data/spec/pulse_meter/visualize/dsl/widgets/gauge_spec.rb +22 -0
  108. data/spec/pulse_meter/visualize/dsl/widgets/line_spec.rb +44 -0
  109. data/spec/pulse_meter/visualize/dsl/widgets/pie_spec.rb +35 -0
  110. data/spec/pulse_meter/visualize/dsl/widgets/table_spec.rb +36 -0
  111. data/spec/pulse_meter/visualize/layout_spec.rb +54 -0
  112. data/spec/pulse_meter/visualize/page_spec.rb +153 -0
  113. data/spec/pulse_meter/visualize/sensor_spec.rb +120 -0
  114. data/spec/pulse_meter/visualize/series_extractor_spec.rb +80 -0
  115. data/spec/pulse_meter/visualize/widgets/area_spec.rb +6 -0
  116. data/spec/pulse_meter/visualize/widgets/gauge_spec.rb +63 -0
  117. data/spec/pulse_meter/visualize/widgets/line_spec.rb +6 -0
  118. data/spec/pulse_meter/visualize/widgets/pie_spec.rb +73 -0
  119. data/spec/pulse_meter/visualize/widgets/table_spec.rb +6 -0
  120. data/spec/pulse_meter/visualizer_spec.rb +42 -0
  121. data/spec/shared_examples/dsl_widget.rb +106 -0
  122. data/spec/shared_examples/widget.rb +97 -0
  123. data/spec/spec_helper.rb +36 -0
  124. metadata +518 -0
@@ -0,0 +1,15 @@
1
+ module PulseMeter
2
+ module Visualize
3
+ class Base
4
+ def initialize(opts)
5
+ @opts = opts
6
+ end
7
+
8
+ def method_missing(name, *args)
9
+ @opts[name.to_sym]
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+
@@ -0,0 +1,40 @@
1
+ #= require extensions
2
+ #= require models/page_info
3
+ #= require models/widget
4
+ #= require models/dinamic_widget
5
+ #= require models/sensor_info
6
+ #= require collections/page_info_list
7
+ #= require collections/sensor_info_list
8
+ #= require collections/widget_list
9
+ #= require presenters/widget
10
+ #= require presenters/pie
11
+ #= require presenters/timeline
12
+ #= require presenters/series
13
+ #= require presenters/line
14
+ #= require presenters/area
15
+ #= require presenters/table
16
+ #= require presenters/gauge
17
+ #= require views/page_title
18
+ #= require views/page_titles
19
+ #= require views/sensor_info_list
20
+ #= require views/dynamic_chart
21
+ #= require views/dynamic_widget
22
+ #= require views/widget_chart
23
+ #= require views/widget
24
+ #= require views/widget_list
25
+ #= require router
26
+
27
+ document.startApp = ->
28
+ pageInfos = new PageInfoList
29
+ pageTitlesApp = new PageTitlesView(pageInfos)
30
+ pageInfos.reset gon.pageInfos
31
+
32
+ widgetList = new WidgetList
33
+ widgetList.setContext(pageInfos)
34
+ widgetList.startPolling()
35
+
36
+ widgetListApp = new WidgetListView {widgetList: widgetList, pageInfos: pageInfos}
37
+
38
+ appRouter = new AppRouter(pageInfos, widgetList)
39
+
40
+ Backbone.history.start()
@@ -0,0 +1,17 @@
1
+ PageInfoList = Backbone.Collection.extend {
2
+ model: PageInfo
3
+ selected: ->
4
+ @find (m) ->
5
+ m.get 'selected'
6
+
7
+ selectFirst: ->
8
+ @at(0).set('selected', true) if @length > 0
9
+
10
+ selectNone: ->
11
+ @each (m) ->
12
+ m.set 'selected', false
13
+
14
+ selectPage: (id) ->
15
+ @each (m) ->
16
+ m.set 'selected', m.id == id
17
+ }
@@ -0,0 +1,4 @@
1
+ SensorInfoList = Backbone.Collection.extend {
2
+ model: SensorInfo
3
+ url: -> ROOT + 'sensors'
4
+ }
@@ -0,0 +1,14 @@
1
+ WidgetList = Backbone.Collection.extend {
2
+ model: Widget
3
+
4
+ setContext: (@pageInfos) ->
5
+
6
+ url: -> ROOT + 'pages/' + @pageInfos.selected().id + '/widgets'
7
+
8
+ startPolling: ->
9
+ setInterval( =>
10
+ if @pageInfos.selected()
11
+ @each (w) ->
12
+ w.refetch()
13
+ , 200)
14
+ }
@@ -0,0 +1,26 @@
1
+ String::capitalize = ->
2
+ @charAt(0).toUpperCase() + @slice(1)
3
+ String::strip = ->
4
+ if String::trim? then @trim() else @replace /^\s+|\s+$/g, ""
5
+
6
+ Number::humanize = ->
7
+ interval = this
8
+ res = ""
9
+ s = interval % 60
10
+ res = "#{s} s" if s > 0
11
+ interval = (interval - s) / 60
12
+ return res unless interval > 0
13
+
14
+ m = interval % 60
15
+ res = "#{m} m #{res}".strip() if m > 0
16
+ interval = (interval - m) / 60
17
+ return res unless interval > 0
18
+
19
+ h = interval % 24
20
+ res = "#{h} h #{res}".strip() if h > 0
21
+ d = (interval - h) / 24
22
+ if d > 0
23
+ "#{d} d #{res}".strip()
24
+ else
25
+ res
26
+
@@ -0,0 +1,34 @@
1
+ DynamicWidget = Backbone.Model.extend {
2
+
3
+ setStartTime: (@startTime) ->
4
+
5
+ setEndTime: (@endTime) ->
6
+
7
+ increaseTimespan: (inc) ->
8
+ @set('timespan', @timespan() + inc)
9
+
10
+ resetTimespan: ->
11
+ @startTime = null
12
+ @endTime = null
13
+ @set('timespan', null)
14
+
15
+ timespan: -> @get('timespan')
16
+
17
+ sensorArgs: ->
18
+ _.map(@get('sensorIds'), (name) -> "sensor[]=#{name}").join('&')
19
+
20
+ url: ->
21
+ timespan = @timespan()
22
+ url = "#{ROOT}dynamic_widget?#{@sensorArgs()}&type=#{@get('type')}"
23
+ url += "&timespan=#{timespan}" if timespan? && !_.isNaN(timespan)
24
+ url += "&startTime=#{@startTime}" if @startTime
25
+ url += "&endTime=#{@endTime}" if @endTime
26
+ url
27
+
28
+
29
+ forceUpdate: ->
30
+ @fetch {
31
+ success: (model, response) ->
32
+ model.trigger('redraw')
33
+ }
34
+ }
@@ -0,0 +1,2 @@
1
+ PageInfo = Backbone.Model.extend {
2
+ }
@@ -0,0 +1,2 @@
1
+ SensorInfo = Backbone.Model.extend {
2
+ }
@@ -0,0 +1,54 @@
1
+ Widget = Backbone.Model.extend {
2
+ initialize: ->
3
+ @needRefresh = true
4
+ @setNextFetch()
5
+ @timespanInc = 0
6
+
7
+ setStartTime: (@startTime) ->
8
+
9
+ setEndTime: (@endTime) ->
10
+
11
+ increaseTimespan: (inc) ->
12
+ @timespanInc = @timespanInc + inc
13
+ @forceUpdate()
14
+
15
+ resetTimespan: ->
16
+ @timespanInc = 0
17
+ @startTime = null
18
+ @endTime = null
19
+ @forceUpdate()
20
+
21
+ timespan: -> @get('timespan') + @timespanInc
22
+
23
+ url: ->
24
+ timespan = @timespan()
25
+ url = "#{@collection.url()}/#{@get('id')}?"
26
+ url += "&timespan=#{timespan}" unless _.isNaN(timespan)
27
+ url += "&startTime=#{@startTime}" if @startTime
28
+ url += "&endTime=#{@endTime}" if @endTime
29
+ url
30
+
31
+ time: -> (new Date()).getTime()
32
+
33
+ setNextFetch: ->
34
+ @nextFetch = @time() + @get('redrawInterval') * 1000
35
+
36
+ setRefresh: (needRefresh) ->
37
+ @needRefresh = needRefresh
38
+
39
+ needFetch: ->
40
+ interval = @get('redrawInterval')
41
+ @time() > @nextFetch && @needRefresh && interval? && interval > 0
42
+
43
+ refetch: ->
44
+ if @needFetch()
45
+ @forceUpdate()
46
+ @setNextFetch()
47
+
48
+ forceUpdate: ->
49
+ @fetch {
50
+ success: (model, response) ->
51
+ model.trigger('redraw')
52
+ }
53
+
54
+ }
@@ -0,0 +1,2 @@
1
+ class AreaPresenter extends SeriesPresenter
2
+ visualization: 'AreaChart'
@@ -0,0 +1,11 @@
1
+ class GaugePresenter extends WidgetPresenter
2
+ visualization: 'Gauge'
3
+
4
+ cutoff: ->
5
+
6
+ data: ->
7
+ data = super()
8
+ data.addColumn('string', 'Label')
9
+ data.addColumn('number', @get('valuesTitle'))
10
+ data.addRows(@get('series'))
11
+ data
@@ -0,0 +1,2 @@
1
+ class LinePresenter extends SeriesPresenter
2
+ visualization: 'LineChart'
@@ -0,0 +1,20 @@
1
+ class PiePresenter extends WidgetPresenter
2
+ visualization: 'PieChart'
3
+
4
+ cutoff: ->
5
+
6
+ data: ->
7
+ data = super()
8
+ data.addColumn('string', 'Title')
9
+ data.addColumn('number', @get('valuesTitle'))
10
+ data.addRows(@get('series').data)
11
+ data
12
+
13
+ options: ->
14
+ $.extend true, super(), {
15
+ slices: @get('series').options
16
+ legend: {
17
+ position: 'bottom'
18
+ }
19
+ }
20
+
@@ -0,0 +1,44 @@
1
+ class SeriesPresenter extends TimelinePresenter
2
+ options: ->
3
+ secondPart = if @get('interval') % 60 == 0 then '' else ':ss'
4
+ format = if @model.timespan() > 24 * 60 * 60
5
+ "yyyy.MM.dd HH:mm#{secondPart}"
6
+ else
7
+ "HH:mm#{secondPart}"
8
+
9
+ $.extend true, super(), {
10
+ lineWidth: 1
11
+ chartArea: {
12
+ width: '100%'
13
+ }
14
+ legend: {
15
+ position: 'bottom'
16
+ }
17
+ vAxis: {
18
+ title: @valuesTitle()
19
+ textPosition: 'in'
20
+ }
21
+ hAxis: {
22
+ format: format
23
+ }
24
+ series: @get('series').options
25
+ axisTitlesPosition: 'in'
26
+ }
27
+
28
+ valuesTitle: ->
29
+ if @get('valuesTitle')
30
+ "#{@get('valuesTitle')} / #{@humanizedInterval()}"
31
+ else
32
+ @humanizedInterval()
33
+
34
+
35
+ humanizedInterval: ->
36
+ @get('interval').humanize()
37
+
38
+ cutoff: (min, max) ->
39
+ _.each(@get('series').rows, (row) =>
40
+ for i in [1 .. row.length - 1]
41
+ value = row[i]
42
+ value = 0 unless value?
43
+ row[i] = @cutoffValue(value, min, max)
44
+ )
@@ -0,0 +1,10 @@
1
+ class TablePresenter extends TimelinePresenter
2
+ visualization: 'Table'
3
+
4
+ cutoff: ->
5
+
6
+ options: ->
7
+ $.extend true, super(), {
8
+ sortColumn: 0
9
+ sortAscending: false
10
+ }
@@ -0,0 +1,13 @@
1
+ class TimelinePresenter extends WidgetPresenter
2
+ data: ->
3
+ data = super()
4
+ data.addColumn('datetime', 'Time')
5
+ dateOffset = @dateOffset() + @get('interval') * 1000
6
+ series = @get('series')
7
+ _.each series.titles, (t) ->
8
+ data.addColumn('number', t)
9
+
10
+ _.each series.rows, (row) ->
11
+ row[0] = new Date(row[0] + dateOffset)
12
+ data.addRow(row)
13
+ data
@@ -0,0 +1,65 @@
1
+ class WidgetPresenter
2
+ constructor: (@pageInfos, @model, el) ->
3
+ chartClass = @chartClass()
4
+ @chart = new chartClass(el)
5
+ @draw()
6
+
7
+ get: (arg) -> @model.get(arg)
8
+
9
+ globalOptions: -> gon.options
10
+
11
+ dateOffset: ->
12
+ if @globalOptions.useUtc
13
+ (new Date).getTimezoneOffset() * 60000
14
+ else
15
+ 0
16
+
17
+ options: ->
18
+ {
19
+ title: @get('title')
20
+ height: 300
21
+ chartArea:
22
+ left: 10
23
+ }
24
+
25
+ mergedOptions: ->
26
+ pageOptions = if @pageInfos.selected()
27
+ @pageInfos.selected().get('gchartOptions')
28
+ else
29
+ {}
30
+
31
+ $.extend(true,
32
+ @options(),
33
+ @globalOptions.gchartOptions,
34
+ pageOptions,
35
+ @get('gchartOptions')
36
+ )
37
+
38
+ data: -> new google.visualization.DataTable
39
+
40
+ chartClass: -> google.visualization[@visualization]
41
+
42
+ cutoff: ->
43
+
44
+ cutoffValue: (v, min, max) ->
45
+ if v?
46
+ if min? && v < min
47
+ min
48
+ else if max? && v > max
49
+ max
50
+ else
51
+ v
52
+ else
53
+ 0
54
+
55
+ draw: (min, max) ->
56
+ @cutoff(min, max)
57
+ @chart.draw(@data(), @mergedOptions())
58
+
59
+ WidgetPresenter.create = (pageInfos, model, el) ->
60
+ type = model.get('type')
61
+ if type? && type.match(/^\w+$/)
62
+ presenterClass = eval("#{type.capitalize()}Presenter")
63
+ new presenterClass(pageInfos, model, el)
64
+ else
65
+ null
@@ -0,0 +1,21 @@
1
+ AppRouter = Backbone.Router.extend {
2
+ initialize: (@pageInfos, @widgetList) ->
3
+ routes: {
4
+ 'pages/:id': 'getPage'
5
+ 'custom': 'custom'
6
+ '*actions': 'defaultRoute'
7
+ }
8
+ getPage: (ids) ->
9
+ id = parseInt(ids)
10
+ @pageInfos.selectPage(id)
11
+ @widgetList.fetch()
12
+ custom: ->
13
+ @pageInfos.selectNone()
14
+ dynamicWidget = new DynamicWidgetView {pageInfos: @pageInfos}
15
+ dynamicWidget.render($('#widgets'))
16
+ defaultRoute: (actions) ->
17
+ if @pageInfos.length > 0
18
+ @navigate('//pages/1')
19
+ else
20
+ @navigate('//custom')
21
+ }
@@ -0,0 +1,91 @@
1
+ DynamicChartView = Backbone.View.extend {
2
+ initialize: (options) ->
3
+ @pageInfos = options['pageInfos']
4
+ @sensors = []
5
+ @type = 'Area'
6
+ @widget = new DynamicWidget
7
+
8
+ @widget.bind('destroy', @remove, this)
9
+ @widget.bind('redraw', @redrawChart, this)
10
+
11
+ tagName: 'div'
12
+
13
+ events: {
14
+ "click #refresh-chart": 'update'
15
+ "click #extend-timespan": 'extendTimespan'
16
+ "click #reset-timespan": 'resetTimespan'
17
+ "change #start-time input": 'maybeEnableStopTime'
18
+ "click #set-interval": 'setTimelineInterval'
19
+ }
20
+
21
+ template: -> _.template($("#dynamic-widget-plotarea").html())
22
+
23
+ render: ->
24
+ @$el.html(@template()())
25
+ @initDatePickers()
26
+
27
+ initDatePickers: ->
28
+ @$el.find(".datepicker").each (i) ->
29
+ $(this).datetimepicker
30
+ showOtherMonths: true
31
+ selectOtherMonths: true
32
+ @$el.find("#end-time input").prop("disabled", true)
33
+
34
+ setTimelineInterval: ->
35
+ start = @unixtimeFromDatepicker("#start-time input")
36
+ end = @unixtimeFromDatepicker("#end-time input")
37
+ @widget.setStartTime(start)
38
+ @widget.setEndTime(end)
39
+ @update()
40
+
41
+ dateFromDatepicker: (id) ->
42
+ @$el.find(id).datetimepicker("getDate")
43
+
44
+ unixtimeFromDatepicker: (id) ->
45
+ date = @dateFromDatepicker(id)
46
+ if date
47
+ date.getTime() / 1000
48
+ else
49
+ null
50
+
51
+ maybeEnableStopTime: ->
52
+ date = @dateFromDatepicker("#start-time input")
53
+ disabled = if date then false else true
54
+ @$el.find("#end-time input").prop("disabled", disabled)
55
+
56
+ extendTimespan: ->
57
+ select = @$el.find("#extend-timespan-val")
58
+ val = select.first().val()
59
+ @widget.increaseTimespan(parseInt(val))
60
+ @update()
61
+
62
+ resetTimespan: ->
63
+ @widget.resetTimespan()
64
+ @update()
65
+
66
+ sensorIds: -> _.map(@sensors, (s) -> s.id)
67
+
68
+ redrawChart: ->
69
+ if @presenter
70
+ @presenter.draw()
71
+ else
72
+ @presenter = WidgetPresenter.create(@pageInfos, @widget, @chartContainer())
73
+
74
+
75
+ chartContainer: ->
76
+ @$el.find('#chart')[0]
77
+
78
+ update: ->
79
+ @widget.forceUpdate() if @sensors.length > 0
80
+
81
+ draw: (sensors, type) ->
82
+ @sensors = sensors
83
+ @type = type
84
+
85
+ @widget.set('sensorIds', @sensorIds())
86
+ @widget.set('type', @type)
87
+
88
+ @presenter = null
89
+ $(@chartContainer()).empty()
90
+ @widget.forceUpdate()
91
+ }