elasticsearch-paramedic-rack 0.1.0

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 (34) hide show
  1. data/.gitignore +17 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +38 -0
  6. data/Rakefile +24 -0
  7. data/elasticsearch-paramedic-rack.gemspec +24 -0
  8. data/lib/elasticsearch-paramedic-rack.rb +10 -0
  9. data/lib/elasticsearch-paramedic-rack/middelware.rb +23 -0
  10. data/lib/elasticsearch-paramedic-rack/version.rb +7 -0
  11. data/public/elasticsearch-paramedic/audio/alert-green.mp3 +0 -0
  12. data/public/elasticsearch-paramedic/audio/alert-red.mp3 +0 -0
  13. data/public/elasticsearch-paramedic/audio/alert-yellow.mp3 +0 -0
  14. data/public/elasticsearch-paramedic/css/app.css +312 -0
  15. data/public/elasticsearch-paramedic/css/libs/cubism.css +57 -0
  16. data/public/elasticsearch-paramedic/css/libs/style.css +499 -0
  17. data/public/elasticsearch-paramedic/img/apple-touch-icon.png +0 -0
  18. data/public/elasticsearch-paramedic/img/glyphicons-halflings-white.png +0 -0
  19. data/public/elasticsearch-paramedic/img/glyphicons-halflings.png +0 -0
  20. data/public/elasticsearch-paramedic/img/spinner.gif +0 -0
  21. data/public/elasticsearch-paramedic/index.html +196 -0
  22. data/public/elasticsearch-paramedic/js/app.js +474 -0
  23. data/public/elasticsearch-paramedic/js/cubism.js +109 -0
  24. data/public/elasticsearch-paramedic/js/libs/colorbrewer.min.js +1 -0
  25. data/public/elasticsearch-paramedic/js/libs/cubism.elasticsearch.js +273 -0
  26. data/public/elasticsearch-paramedic/js/libs/cubism.v1.js +986 -0
  27. data/public/elasticsearch-paramedic/js/libs/cubism.v1.min.js +1 -0
  28. data/public/elasticsearch-paramedic/js/libs/d3.v2.min.js +4 -0
  29. data/public/elasticsearch-paramedic/js/libs/ember-0.9.8.js +20150 -0
  30. data/public/elasticsearch-paramedic/js/libs/ember-0.9.8.min.js +14 -0
  31. data/public/elasticsearch-paramedic/js/libs/jquery-1.7.2.min.js +4 -0
  32. data/test/middelware_test.rb +63 -0
  33. data/test/test_helper.rb +12 -0
  34. metadata +134 -0
@@ -0,0 +1,109 @@
1
+ var App = App || Em.Application.create({});
2
+
3
+ App.Cubism = Ember.Object.create({
4
+
5
+ // ElasticSearch extension instance
6
+ elasticsearch: {},
7
+
8
+ // Cubism.js context
9
+ context: {},
10
+
11
+ // Chart canvas
12
+ chart: d3.select("#chart").append("div").attr("id", "chart-inner"),
13
+
14
+ // Function to add new horizon chart
15
+ add_chart: function(metrics, options) {
16
+ var options = options || {colors: 'Greens'}
17
+
18
+ this.chart.selectAll(".horizon")
19
+ .data(metrics, function(d) { return d.toString(); })
20
+ .enter().append("div")
21
+ .attr("class", "horizon")
22
+ .call(this.context.horizon()
23
+ .height(25)
24
+ .colors(function() { return colorbrewer[options['colors']][8] })
25
+ )
26
+ return this.chart;
27
+ },
28
+
29
+ // Setup the whole chart
30
+ setup: function() {
31
+ var self = this
32
+
33
+ // Setup context
34
+ self.__setup_context()
35
+
36
+ // Top axis
37
+ if ( d3.select("#chart .axis.top").empty() )
38
+ self.chart.append("div")
39
+ .attr("class", "axis top")
40
+ .call(self.context.axis().orient("top"));
41
+
42
+ // Rule
43
+ if ( d3.select("#chart .rule").empty() )
44
+ self.chart.append("div")
45
+ .attr("class", "rule")
46
+ .call(self.context.rule());
47
+
48
+ // Move the rule with mouse
49
+ self.context.on("focus", function(i) { d3.selectAll(".value").style("right", i == null ? null : self.context.size() - i + "px"); })
50
+
51
+ // Draw the chart(s)
52
+ self.__draw()
53
+
54
+ return self
55
+ },
56
+
57
+ // Remove the chart and re-draw it
58
+ reset: function() {
59
+ var self = this
60
+
61
+ // Re-insert the chart element
62
+ d3.select("#chart-inner").remove()
63
+ self.chart = d3.select("#chart").append("div").attr("id", "chart-inner")
64
+
65
+ // Re-initialize everything
66
+ self.setup()
67
+ return self
68
+ },
69
+
70
+ // Start the polling/animation
71
+ start: function() {
72
+ if (this.context.start) this.context.start()
73
+ },
74
+
75
+ // Stop the polling/animation
76
+ stop: function() {
77
+ if (this.context.stop) this.context.stop()
78
+ },
79
+
80
+ // Draw the metrics
81
+ __draw: function() {
82
+ var self = this
83
+
84
+ self.elasticsearch = cubism.elasticsearch(self.context, {host: App.elasticsearch_url}, function() {
85
+ [
86
+ { metrics: this.metrics("os.cpu.user"), colors: 'Greens' },
87
+ { metrics: this.metrics("process.cpu.percent"), colors: 'Greens' },
88
+ { metrics: this.metrics("jvm.mem.heap_used_in_bytes"), colors: 'Blues' },
89
+ { metrics: this.metrics("http.current_open"), colors: 'Oranges' },
90
+ { metrics: this.metrics("indices.indexing.index_current"), colors: 'Spectral' },
91
+ { metrics: this.metrics("indices.search.query_current"), colors: 'YlOrRd' }
92
+ ].forEach(
93
+ function(group) { self.add_chart(group.metrics, {colors: group.colors}) }
94
+ )
95
+
96
+ return self
97
+ });
98
+ },
99
+
100
+ __setup_context: function() {
101
+ this.context = cubism.context()
102
+ .serverDelay(0)
103
+ .clientDelay(0)
104
+ .step(1000)
105
+ .size($("#chart").width())
106
+ }
107
+
108
+ });
109
+
@@ -0,0 +1 @@
1
+ var colorbrewer={YlGn:{3:["rgb(247,252,185)","rgb(173,221,142)","rgb(49,163,84)"],4:["rgb(255,255,204)","rgb(194,230,153)","rgb(120,198,121)","rgb(35,132,67)"],5:["rgb(255,255,204)","rgb(194,230,153)","rgb(120,198,121)","rgb(49,163,84)","rgb(0,104,55)"],6:["rgb(255,255,204)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(49,163,84)","rgb(0,104,55)"],7:["rgb(255,255,204)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,90,50)"],8:["rgb(255,255,229)","rgb(247,252,185)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,90,50)"],9:["rgb(255,255,229)","rgb(247,252,185)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,104,55)","rgb(0,69,41)"]},YlGnBu:{3:["rgb(237,248,177)","rgb(127,205,187)","rgb(44,127,184)"],4:["rgb(255,255,204)","rgb(161,218,180)","rgb(65,182,196)","rgb(34,94,168)"],5:["rgb(255,255,204)","rgb(161,218,180)","rgb(65,182,196)","rgb(44,127,184)","rgb(37,52,148)"],6:["rgb(255,255,204)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(44,127,184)","rgb(37,52,148)"],7:["rgb(255,255,204)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(12,44,132)"],8:["rgb(255,255,217)","rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(12,44,132)"],9:["rgb(255,255,217)","rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(37,52,148)","rgb(8,29,88)"]},GnBu:{3:["rgb(224,243,219)","rgb(168,221,181)","rgb(67,162,202)"],4:["rgb(240,249,232)","rgb(186,228,188)","rgb(123,204,196)","rgb(43,140,190)"],5:["rgb(240,249,232)","rgb(186,228,188)","rgb(123,204,196)","rgb(67,162,202)","rgb(8,104,172)"],6:["rgb(240,249,232)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(67,162,202)","rgb(8,104,172)"],7:["rgb(240,249,232)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,88,158)"],8:["rgb(247,252,240)","rgb(224,243,219)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,88,158)"],9:["rgb(247,252,240)","rgb(224,243,219)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,104,172)","rgb(8,64,129)"]},BuGn:{3:["rgb(229,245,249)","rgb(153,216,201)","rgb(44,162,95)"],4:["rgb(237,248,251)","rgb(178,226,226)","rgb(102,194,164)","rgb(35,139,69)"],5:["rgb(237,248,251)","rgb(178,226,226)","rgb(102,194,164)","rgb(44,162,95)","rgb(0,109,44)"],6:["rgb(237,248,251)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(44,162,95)","rgb(0,109,44)"],7:["rgb(237,248,251)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,88,36)"],8:["rgb(247,252,253)","rgb(229,245,249)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,88,36)"],9:["rgb(247,252,253)","rgb(229,245,249)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,109,44)","rgb(0,68,27)"]},PuBuGn:{3:["rgb(236,226,240)","rgb(166,189,219)","rgb(28,144,153)"],4:["rgb(246,239,247)","rgb(189,201,225)","rgb(103,169,207)","rgb(2,129,138)"],5:["rgb(246,239,247)","rgb(189,201,225)","rgb(103,169,207)","rgb(28,144,153)","rgb(1,108,89)"],6:["rgb(246,239,247)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(28,144,153)","rgb(1,108,89)"],7:["rgb(246,239,247)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,100,80)"],8:["rgb(255,247,251)","rgb(236,226,240)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,100,80)"],9:["rgb(255,247,251)","rgb(236,226,240)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,108,89)","rgb(1,70,54)"]},PuBu:{3:["rgb(236,231,242)","rgb(166,189,219)","rgb(43,140,190)"],4:["rgb(241,238,246)","rgb(189,201,225)","rgb(116,169,207)","rgb(5,112,176)"],5:["rgb(241,238,246)","rgb(189,201,225)","rgb(116,169,207)","rgb(43,140,190)","rgb(4,90,141)"],6:["rgb(241,238,246)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(43,140,190)","rgb(4,90,141)"],7:["rgb(241,238,246)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(3,78,123)"],8:["rgb(255,247,251)","rgb(236,231,242)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(3,78,123)"],9:["rgb(255,247,251)","rgb(236,231,242)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(4,90,141)","rgb(2,56,88)"]},BuPu:{3:["rgb(224,236,244)","rgb(158,188,218)","rgb(136,86,167)"],4:["rgb(237,248,251)","rgb(179,205,227)","rgb(140,150,198)","rgb(136,65,157)"],5:["rgb(237,248,251)","rgb(179,205,227)","rgb(140,150,198)","rgb(136,86,167)","rgb(129,15,124)"],6:["rgb(237,248,251)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(136,86,167)","rgb(129,15,124)"],7:["rgb(237,248,251)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(110,1,107)"],8:["rgb(247,252,253)","rgb(224,236,244)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(110,1,107)"],9:["rgb(247,252,253)","rgb(224,236,244)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(129,15,124)","rgb(77,0,75)"]},RdPu:{3:["rgb(253,224,221)","rgb(250,159,181)","rgb(197,27,138)"],4:["rgb(254,235,226)","rgb(251,180,185)","rgb(247,104,161)","rgb(174,1,126)"],5:["rgb(254,235,226)","rgb(251,180,185)","rgb(247,104,161)","rgb(197,27,138)","rgb(122,1,119)"],6:["rgb(254,235,226)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(197,27,138)","rgb(122,1,119)"],7:["rgb(254,235,226)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)"],8:["rgb(255,247,243)","rgb(253,224,221)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)"],9:["rgb(255,247,243)","rgb(253,224,221)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)","rgb(73,0,106)"]},PuRd:{3:["rgb(231,225,239)","rgb(201,148,199)","rgb(221,28,119)"],4:["rgb(241,238,246)","rgb(215,181,216)","rgb(223,101,176)","rgb(206,18,86)"],5:["rgb(241,238,246)","rgb(215,181,216)","rgb(223,101,176)","rgb(221,28,119)","rgb(152,0,67)"],6:["rgb(241,238,246)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(221,28,119)","rgb(152,0,67)"],7:["rgb(241,238,246)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(145,0,63)"],8:["rgb(247,244,249)","rgb(231,225,239)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(145,0,63)"],9:["rgb(247,244,249)","rgb(231,225,239)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(152,0,67)","rgb(103,0,31)"]},OrRd:{3:["rgb(254,232,200)","rgb(253,187,132)","rgb(227,74,51)"],4:["rgb(254,240,217)","rgb(253,204,138)","rgb(252,141,89)","rgb(215,48,31)"],5:["rgb(254,240,217)","rgb(253,204,138)","rgb(252,141,89)","rgb(227,74,51)","rgb(179,0,0)"],6:["rgb(254,240,217)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(227,74,51)","rgb(179,0,0)"],7:["rgb(254,240,217)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(153,0,0)"],8:["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(153,0,0)"],9:["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(179,0,0)","rgb(127,0,0)"]},YlOrRd:{3:["rgb(255,237,160)","rgb(254,178,76)","rgb(240,59,32)"],4:["rgb(255,255,178)","rgb(254,204,92)","rgb(253,141,60)","rgb(227,26,28)"],5:["rgb(255,255,178)","rgb(254,204,92)","rgb(253,141,60)","rgb(240,59,32)","rgb(189,0,38)"],6:["rgb(255,255,178)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(240,59,32)","rgb(189,0,38)"],7:["rgb(255,255,178)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(177,0,38)"],8:["rgb(255,255,204)","rgb(255,237,160)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(177,0,38)"],9:["rgb(255,255,204)","rgb(255,237,160)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(189,0,38)","rgb(128,0,38)"]},YlOrBr:{3:["rgb(255,247,188)","rgb(254,196,79)","rgb(217,95,14)"],4:["rgb(255,255,212)","rgb(254,217,142)","rgb(254,153,41)","rgb(204,76,2)"],5:["rgb(255,255,212)","rgb(254,217,142)","rgb(254,153,41)","rgb(217,95,14)","rgb(153,52,4)"],6:["rgb(255,255,212)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(217,95,14)","rgb(153,52,4)"],7:["rgb(255,255,212)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(140,45,4)"],8:["rgb(255,255,229)","rgb(255,247,188)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(140,45,4)"],9:["rgb(255,255,229)","rgb(255,247,188)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(153,52,4)","rgb(102,37,6)"]},Purples:{3:["rgb(239,237,245)","rgb(188,189,220)","rgb(117,107,177)"],4:["rgb(242,240,247)","rgb(203,201,226)","rgb(158,154,200)","rgb(106,81,163)"],5:["rgb(242,240,247)","rgb(203,201,226)","rgb(158,154,200)","rgb(117,107,177)","rgb(84,39,143)"],6:["rgb(242,240,247)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(117,107,177)","rgb(84,39,143)"],7:["rgb(242,240,247)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(74,20,134)"],8:["rgb(252,251,253)","rgb(239,237,245)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(74,20,134)"],9:["rgb(252,251,253)","rgb(239,237,245)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(84,39,143)","rgb(63,0,125)"]},Blues:{3:["rgb(222,235,247)","rgb(158,202,225)","rgb(49,130,189)"],4:["rgb(239,243,255)","rgb(189,215,231)","rgb(107,174,214)","rgb(33,113,181)"],5:["rgb(239,243,255)","rgb(189,215,231)","rgb(107,174,214)","rgb(49,130,189)","rgb(8,81,156)"],6:["rgb(239,243,255)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(49,130,189)","rgb(8,81,156)"],7:["rgb(239,243,255)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,69,148)"],8:["rgb(247,251,255)","rgb(222,235,247)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,69,148)"],9:["rgb(247,251,255)","rgb(222,235,247)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,81,156)","rgb(8,48,107)"]},Greens:{3:["rgb(229,245,224)","rgb(161,217,155)","rgb(49,163,84)"],4:["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(35,139,69)"],5:["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"],6:["rgb(237,248,233)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"],7:["rgb(237,248,233)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,90,50)"],8:["rgb(247,252,245)","rgb(229,245,224)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,90,50)"],9:["rgb(247,252,245)","rgb(229,245,224)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,109,44)","rgb(0,68,27)"]},Oranges:{3:["rgb(254,230,206)","rgb(253,174,107)","rgb(230,85,13)"],4:["rgb(254,237,222)","rgb(253,190,133)","rgb(253,141,60)","rgb(217,71,1)"],5:["rgb(254,237,222)","rgb(253,190,133)","rgb(253,141,60)","rgb(230,85,13)","rgb(166,54,3)"],6:["rgb(254,237,222)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(230,85,13)","rgb(166,54,3)"],7:["rgb(254,237,222)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(140,45,4)"],8:["rgb(255,245,235)","rgb(254,230,206)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(140,45,4)"],9:["rgb(255,245,235)","rgb(254,230,206)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(166,54,3)","rgb(127,39,4)"]},Reds:{3:["rgb(254,224,210)","rgb(252,146,114)","rgb(222,45,38)"],4:["rgb(254,229,217)","rgb(252,174,145)","rgb(251,106,74)","rgb(203,24,29)"],5:["rgb(254,229,217)","rgb(252,174,145)","rgb(251,106,74)","rgb(222,45,38)","rgb(165,15,21)"],6:["rgb(254,229,217)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(222,45,38)","rgb(165,15,21)"],7:["rgb(254,229,217)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(153,0,13)"],8:["rgb(255,245,240)","rgb(254,224,210)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(153,0,13)"],9:["rgb(255,245,240)","rgb(254,224,210)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(165,15,21)","rgb(103,0,13)"]},Greys:{3:["rgb(240,240,240)","rgb(189,189,189)","rgb(99,99,99)"],4:["rgb(247,247,247)","rgb(204,204,204)","rgb(150,150,150)","rgb(82,82,82)"],5:["rgb(247,247,247)","rgb(204,204,204)","rgb(150,150,150)","rgb(99,99,99)","rgb(37,37,37)"],6:["rgb(247,247,247)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(99,99,99)","rgb(37,37,37)"],7:["rgb(247,247,247)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)"],8:["rgb(255,255,255)","rgb(240,240,240)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)"],9:["rgb(255,255,255)","rgb(240,240,240)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)","rgb(0,0,0)"]},PuOr:{3:["rgb(241,163,64)","rgb(247,247,247)","rgb(153,142,195)"],4:["rgb(230,97,1)","rgb(253,184,99)","rgb(178,171,210)","rgb(94,60,153)"],5:["rgb(230,97,1)","rgb(253,184,99)","rgb(247,247,247)","rgb(178,171,210)","rgb(94,60,153)"],6:["rgb(179,88,6)","rgb(241,163,64)","rgb(254,224,182)","rgb(216,218,235)","rgb(153,142,195)","rgb(84,39,136)"],7:["rgb(179,88,6)","rgb(241,163,64)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(153,142,195)","rgb(84,39,136)"],8:["rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)"],9:["rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)"],10:["rgb(127,59,8)","rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)","rgb(45,0,75)"],11:["rgb(127,59,8)","rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)","rgb(45,0,75)"]},BrBG:{3:["rgb(216,179,101)","rgb(245,245,245)","rgb(90,180,172)"],4:["rgb(166,97,26)","rgb(223,194,125)","rgb(128,205,193)","rgb(1,133,113)"],5:["rgb(166,97,26)","rgb(223,194,125)","rgb(245,245,245)","rgb(128,205,193)","rgb(1,133,113)"],6:["rgb(140,81,10)","rgb(216,179,101)","rgb(246,232,195)","rgb(199,234,229)","rgb(90,180,172)","rgb(1,102,94)"],7:["rgb(140,81,10)","rgb(216,179,101)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(90,180,172)","rgb(1,102,94)"],8:["rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)"],9:["rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)"],10:["rgb(84,48,5)","rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)","rgb(0,60,48)"],11:["rgb(84,48,5)","rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)","rgb(0,60,48)"]},PRGn:{3:["rgb(175,141,195)","rgb(247,247,247)","rgb(127,191,123)"],4:["rgb(123,50,148)","rgb(194,165,207)","rgb(166,219,160)","rgb(0,136,55)"],5:["rgb(123,50,148)","rgb(194,165,207)","rgb(247,247,247)","rgb(166,219,160)","rgb(0,136,55)"],6:["rgb(118,42,131)","rgb(175,141,195)","rgb(231,212,232)","rgb(217,240,211)","rgb(127,191,123)","rgb(27,120,55)"],7:["rgb(118,42,131)","rgb(175,141,195)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(127,191,123)","rgb(27,120,55)"],8:["rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)"],9:["rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)"],10:["rgb(64,0,75)","rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)","rgb(0,68,27)"],11:["rgb(64,0,75)","rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)","rgb(0,68,27)"]},PiYG:{3:["rgb(233,163,201)","rgb(247,247,247)","rgb(161,215,106)"],4:["rgb(208,28,139)","rgb(241,182,218)","rgb(184,225,134)","rgb(77,172,38)"],5:["rgb(208,28,139)","rgb(241,182,218)","rgb(247,247,247)","rgb(184,225,134)","rgb(77,172,38)"],6:["rgb(197,27,125)","rgb(233,163,201)","rgb(253,224,239)","rgb(230,245,208)","rgb(161,215,106)","rgb(77,146,33)"],7:["rgb(197,27,125)","rgb(233,163,201)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(161,215,106)","rgb(77,146,33)"],8:["rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)"],9:["rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)"],10:["rgb(142,1,82)","rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)","rgb(39,100,25)"],11:["rgb(142,1,82)","rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)","rgb(39,100,25)"]},RdBu:{3:["rgb(239,138,98)","rgb(247,247,247)","rgb(103,169,207)"],4:["rgb(202,0,32)","rgb(244,165,130)","rgb(146,197,222)","rgb(5,113,176)"],5:["rgb(202,0,32)","rgb(244,165,130)","rgb(247,247,247)","rgb(146,197,222)","rgb(5,113,176)"],6:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(209,229,240)","rgb(103,169,207)","rgb(33,102,172)"],7:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(103,169,207)","rgb(33,102,172)"],8:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)"],9:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)"],10:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)","rgb(5,48,97)"],11:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)","rgb(5,48,97)"]},RdGy:{3:["rgb(239,138,98)","rgb(255,255,255)","rgb(153,153,153)"],4:["rgb(202,0,32)","rgb(244,165,130)","rgb(186,186,186)","rgb(64,64,64)"],5:["rgb(202,0,32)","rgb(244,165,130)","rgb(255,255,255)","rgb(186,186,186)","rgb(64,64,64)"],6:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(224,224,224)","rgb(153,153,153)","rgb(77,77,77)"],7:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(153,153,153)","rgb(77,77,77)"],8:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)"],9:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)"],10:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)","rgb(26,26,26)"],11:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)","rgb(26,26,26)"]},RdYlBu:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(145,191,219)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(171,217,233)","rgb(44,123,182)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(171,217,233)","rgb(44,123,182)"],6:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,144)","rgb(224,243,248)","rgb(145,191,219)","rgb(69,117,180)"],7:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(145,191,219)","rgb(69,117,180)"],8:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)"],9:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)"],10:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)","rgb(49,54,149)"],11:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)","rgb(49,54,149)"]},Spectral:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(153,213,148)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(171,221,164)","rgb(43,131,186)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(171,221,164)","rgb(43,131,186)"],6:["rgb(213,62,79)","rgb(252,141,89)","rgb(254,224,139)","rgb(230,245,152)","rgb(153,213,148)","rgb(50,136,189)"],7:["rgb(213,62,79)","rgb(252,141,89)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(153,213,148)","rgb(50,136,189)"],8:["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"],9:["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"],10:["rgb(158,1,66)","rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)","rgb(94,79,162)"],11:["rgb(158,1,66)","rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)","rgb(94,79,162)"]},RdYlGn:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(145,207,96)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(166,217,106)","rgb(26,150,65)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(166,217,106)","rgb(26,150,65)"],6:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,139)","rgb(217,239,139)","rgb(145,207,96)","rgb(26,152,80)"],7:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(145,207,96)","rgb(26,152,80)"],8:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)"],9:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)"],10:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)","rgb(0,104,55)"],11:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)","rgb(0,104,55)"]}};
@@ -0,0 +1,273 @@
1
+ // # Cubism.ElasticSearch #
2
+ //
3
+ // `cubism.elasticsearch` is an extension for the
4
+ // [_Cubism.js_](http://square.github.com/cubism/) visualization platform
5
+ // to display statistics from the _ElasticSearch_
6
+ // ["Nodes Stats API"](http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-stats.html).
7
+ //
8
+ // ## Usage ##
9
+ //
10
+ // Setup a Cubism context, and pass it to the `cubism.elasticsearch` function:
11
+ //
12
+ // var context = cubism.context(),
13
+ // elasticsearch = cubism.elasticsearch(context, {host: "http://localhost:9200"}, function() {...})
14
+ //
15
+ // Use the `metric` function to return a specific metric from a _ElasticSearch_ node in the callback function:
16
+ //
17
+ // this.metric("os.cpu.user")
18
+ //
19
+ // By default, metric is returned from the first node (`0`).
20
+ //
21
+ // You may use the node's ID or a number giving it's position:
22
+ //
23
+ // this.metric("os.cpu.user", "USNEtnCWQW-5oG3Gf9J8Hg")
24
+ // this.metric("os.cpu.user", 0)
25
+ //
26
+ // You can use any valid path in the JSON returned from the _ElasticSearch_:
27
+ //
28
+ // var basic_metrics = [
29
+ // this.metric("os.cpu.user"),
30
+ // this.metric("process.cpu.percent"),
31
+ // this.metric("fs.data[0].disk_reads")
32
+ // // ...
33
+ // ]
34
+ //
35
+ // Pass the returned metrics as the `data` collection to the _horizon_ chart
36
+ // (https://github.com/square/cubism/wiki/Horizon#wiki-_horizon):
37
+ //
38
+ // var context = cubism.context(),
39
+ // elasticsearch = cubism.elasticsearch(context, {host: "http://localhost:9200"}, function() {
40
+ // chart.selectAll(".horizon")
41
+ // .data([elasticsearch.metric("os.cpu.user"]), function(d) { return d.toString(); })
42
+ // .enter().append("div")
43
+ // .attr("class", "horizon")
44
+ // .call(context.horizon())
45
+ // });
46
+ //
47
+ //
48
+ // Use the `metrics` function to return an array with the specified metric from all nodes in the cluster:
49
+ //
50
+ // this.metrics("os.cpu.user")
51
+ //
52
+ // To display metrics from all nodes, simply pass them as the `data` collection:
53
+ //
54
+ // var context = cubism.context(),
55
+ // elasticsearch = cubism.elasticsearch(context, {host: "http://localhost:9200"}, function() {
56
+ // chart.selectAll(".horizon")
57
+ // .data(elasticsearch.metrics("os.cpu.user"), function(d) { return d.toString(); })
58
+ // .enter().append("div")
59
+ // .attr("class", "horizon")
60
+ // .call(context.horizon())
61
+ // chart.selectAll(".horizon")
62
+ // .data(elasticsearch.metrics("process.cpu.percent"), function(d) { return d.toString(); })
63
+ // .enter().append("div")
64
+ // .attr("class", "horizon")
65
+ // .call(context.horizon())
66
+ // });
67
+ //
68
+ // Supposed you have a convenience function to add a metric to the chart, such as,
69
+ //
70
+ // // Function to add new chart
71
+ // //
72
+ // chart.add = function(metrics) {
73
+ // chart.selectAll(".horizon")
74
+ // .data(metrics, function(d) { return d.toString(); })
75
+ // .enter().append("div")
76
+ // .attr("class", "horizon")
77
+ // .call(context.horizon())
78
+ // return chart;
79
+ // };
80
+ //
81
+ // then adding a new metric is simply a matter of pasing it the result of `elasticsearch.metrics()` function:
82
+ //
83
+ // var context = cubism.context(),
84
+ // elasticsearch = cubism.elasticsearch(context, {host: "http://localhost:9200"}, function() {
85
+ // chart.add( elasticsearch.metrics("os.cpu.user") )
86
+ // chart.add( elasticsearch.metrics("process.cpu.percent") )
87
+ // // ...
88
+ // });
89
+ //
90
+
91
+ var cubism = ('undefined' == typeof cubism) ? {} : cubism;
92
+
93
+ // An ElasticSearch extension for Cubism.js
94
+ //
95
+ // Arguments
96
+ // ---------
97
+ //
98
+ // * `context` -- The Cubism.js context [https://github.com/square/cubism/wiki/Context]
99
+ // * `options` -- Options (eg. ElasticSearch URL)
100
+ // * `callback` -- Callback to execute after loading cluster info (eg. adding charts)
101
+ //
102
+ // Usage
103
+ // -----
104
+ //
105
+ // var context = cubism.context(),
106
+ //
107
+ // elasticsearch = cubism.elasticsearch(
108
+ // context,
109
+ //
110
+ // {host: "http://localhost:9200"},
111
+ //
112
+ // function() {
113
+ //
114
+ // chart.selectAll(".horizon")
115
+ //
116
+ // // Overall CPU (user)
117
+ // //
118
+ // .data(elasticsearch.metrics("os.cpu.user"), function(d) { return d.toString(); })
119
+ //
120
+ // .enter().append("div")
121
+ // .attr("class", "horizon")
122
+ // .call(context.horizon())
123
+ //
124
+ // chart.selectAll(".horizon")
125
+ //
126
+ // // CPU consumed by ElasticSearch
127
+ // //
128
+ // .data(elasticsearch.metrics("process.cpu.percent"), function(d) { return d.toString(); })
129
+ //
130
+ // .enter().append("div")
131
+ // .attr("class", "horizon")
132
+ // .call(context.horizon())
133
+ //
134
+ // chart.selectAll(".horizon")
135
+ //
136
+ // // Number of documents on a specific node
137
+ // //
138
+ // .data([elasticsearch.metric("indices.docs.count", "USNEtnCWQW-5oG3Gf9J8Hg"]),
139
+ // function(d) { return d.toString(); })
140
+ //
141
+ // .enter().append("div")
142
+ // .attr("class", "horizon")
143
+ // .call(context.horizon())
144
+ // });
145
+ //
146
+ cubism.elasticsearch = function(context, options, callback) {
147
+ if (!context) throw new Error("Please pass a valid Cubism context instance as the first argument");
148
+
149
+ options = options || {}
150
+ if (!options.host) options.host = "http://localhost:9200";
151
+
152
+ var source = {},
153
+ context = context,
154
+ initialized_metrics = {};
155
+
156
+ source.cluster = {};
157
+ source.node_names = {};
158
+
159
+ // Returns a function, which will return data for the Cubism horizon chart callbacks.
160
+ //
161
+ var __cubism_metric_callback = function(node, expression) {
162
+ return function(start, stop, step, callback) {
163
+ var values = [],
164
+ value = 0,
165
+ start = +start,
166
+ stop = +stop,
167
+ metric_id = node.id+'-'+expression
168
+
169
+ d3.json(source.url(), function(data) {
170
+ if (!data) return callback(new Error("Unable to load data from ElasticSearch!"))
171
+ if (!data.nodes[node.id]) return callback(new Error("Unable to find node " + node.id + "!"))
172
+
173
+ var value = eval("data.nodes['"+node.id+"']." + expression) // data.nodes[<NAME>].os.cpu.user
174
+ // console.log(expression + ': ' + value)
175
+
176
+ // Cubism.js expects a value for every "slot" based on the `start` and `stop` parameters, because
177
+ // it assumes a backend such as [_Graphite_](https://github.com/square/cubism/wiki/Graphite),
178
+ // which is able to return values stored over time.
179
+ //
180
+ // In _ElasticSearch_, we don't have any data stored: we poll the API repeatedly.
181
+ //
182
+ // On first call, Cubism.js calls the metric callback function with a large `start` and `stop` gap,
183
+ // based on the `step` and `size` values of your chart. This would spoil the chart with a useless
184
+ // "thick colored line".
185
+ //
186
+ // So: if we have already initialized this metric, push the same value to all the "slots",
187
+ // because this is what Cubism.js expects...
188
+ if (initialized_metrics[metric_id]) {
189
+ while (start < stop) {
190
+ start += step;
191
+ values.push(value);
192
+ }
193
+ }
194
+ // ... otherwise mark this metric as initialized and fill the empty / "historical" slots with empty values.
195
+ else {
196
+ initialized_metrics[metric_id] = true;
197
+ while (start < (stop - step)) {
198
+ start += step;
199
+ values.push(NaN);
200
+ }
201
+ values.push(value);
202
+ }
203
+
204
+ callback(null, values)
205
+ });
206
+ }
207
+ }
208
+
209
+ // Load information about ElasticSearch nodes from the Nodes Info API
210
+ // [http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-info.html]
211
+ //
212
+ d3.json(options.host + "/_cluster/nodes", function(cluster) {
213
+ source.cluster = cluster
214
+ source.node_names = d3.keys(cluster.nodes)
215
+
216
+ // Returns a metric for specific node
217
+ //
218
+ // Arguments
219
+ // ---------
220
+ //
221
+ // * `expression` -- A valid path in the ElasticSearch JSON response (eg. `os.cpu.user`)
222
+ // * `n` -- A node specification. Can be either node ID (eg. `USNEtnCWQW-5oG3Gf9J8Hg`),
223
+ // or a number giving position in response (eg. `0`)
224
+ //
225
+ // For usage, see documentation for `cubism.elasticsearch`
226
+ //
227
+ source.metric = function(expression, n) {
228
+ var n = n || 0,
229
+ node_id = ("number" == typeof n) ? source.node_names[n] : n,
230
+ node = source.cluster.nodes[node_id];
231
+
232
+ var metric = context.metric(__cubism_metric_callback(node, expression), expression + " [" + node.name + "]");
233
+
234
+ return metric;
235
+ };
236
+
237
+ // Returns a metric across all nodes
238
+ //
239
+ // Arguments
240
+ // ---------
241
+ //
242
+ // * `expression` -- A valid path in the ElasticSearch JSON response (eg. `os.cpu.user`)
243
+ //
244
+ // For usage, see documentation for `cubism.elasticsearch`
245
+ //
246
+ source.metrics = function(expression) {
247
+ var metrics = [],
248
+ ordered_nodes = [];
249
+ for ( var n in source.cluster.nodes )
250
+ { var o = source.cluster.nodes[n]; o.id = n; ordered_nodes.push(o) }
251
+
252
+ ordered_nodes = ordered_nodes.sort(function(a,b) {
253
+ if (a.name < b.name) return -1;
254
+ if (a.name > b.name) return 1;
255
+ return 0;
256
+ });
257
+
258
+ ordered_nodes.forEach( function(node) {
259
+ var metric = context.metric(__cubism_metric_callback(node, expression), expression + " [" + node.name + "]");
260
+ metrics.push(metric)
261
+ })
262
+
263
+ return metrics;
264
+ };
265
+
266
+ callback.call(source)
267
+ })
268
+
269
+ source.toString = function() { return options.host };
270
+ source.url = function() { return options.host + "/_cluster/nodes/stats?all" };
271
+
272
+ return source;
273
+ };
@@ -0,0 +1,986 @@
1
+ (function(exports){
2
+ var cubism = exports.cubism = {version: "1.0.1"};
3
+ var cubism_id = 0;
4
+ function cubism_identity(d) { return d; }
5
+ cubism.option = function(name, defaultValue) {
6
+ var values = cubism.options(name);
7
+ return values.length ? values[0] : defaultValue;
8
+ };
9
+
10
+ cubism.options = function(name, defaultValues) {
11
+ var options = location.search.substring(1).split("&"),
12
+ values = [],
13
+ i = -1,
14
+ n = options.length,
15
+ o;
16
+ while (++i < n) {
17
+ if ((o = options[i].split("="))[0] == name) {
18
+ values.push(decodeURIComponent(o[1]));
19
+ }
20
+ }
21
+ return values.length || arguments.length < 2 ? values : defaultValues;
22
+ };
23
+ cubism.context = function() {
24
+ var context = new cubism_context,
25
+ step = 1e4, // ten seconds, in milliseconds
26
+ size = 1440, // four hours at ten seconds, in pixels
27
+ start0, stop0, // the start and stop for the previous change event
28
+ start1, stop1, // the start and stop for the next prepare event
29
+ serverDelay = 5e3,
30
+ clientDelay = 5e3,
31
+ event = d3.dispatch("prepare", "beforechange", "change", "focus"),
32
+ scale = context.scale = d3.time.scale().range([0, size]),
33
+ timeout,
34
+ focus;
35
+
36
+ function update() {
37
+ var now = Date.now();
38
+ stop0 = new Date(Math.floor((now - serverDelay - clientDelay) / step) * step);
39
+ start0 = new Date(stop0 - size * step);
40
+ stop1 = new Date(Math.floor((now - serverDelay) / step) * step);
41
+ start1 = new Date(stop1 - size * step);
42
+ scale.domain([start0, stop0]);
43
+ return context;
44
+ }
45
+
46
+ context.start = function() {
47
+ if (timeout) clearTimeout(timeout);
48
+ var delay = +stop1 + serverDelay - Date.now();
49
+
50
+ // If we're too late for the first prepare event, skip it.
51
+ if (delay < clientDelay) delay += step;
52
+
53
+ timeout = setTimeout(function prepare() {
54
+ stop1 = new Date(Math.floor((Date.now() - serverDelay) / step) * step);
55
+ start1 = new Date(stop1 - size * step);
56
+ event.prepare.call(context, start1, stop1);
57
+
58
+ setTimeout(function() {
59
+ scale.domain([start0 = start1, stop0 = stop1]);
60
+ event.beforechange.call(context, start1, stop1);
61
+ event.change.call(context, start1, stop1);
62
+ event.focus.call(context, focus);
63
+ }, clientDelay);
64
+
65
+ timeout = setTimeout(prepare, step);
66
+ }, delay);
67
+ return context;
68
+ };
69
+
70
+ context.stop = function() {
71
+ timeout = clearTimeout(timeout);
72
+ return context;
73
+ };
74
+
75
+ timeout = setTimeout(context.start, 10);
76
+
77
+ // Set or get the step interval in milliseconds.
78
+ // Defaults to ten seconds.
79
+ context.step = function(_) {
80
+ if (!arguments.length) return step;
81
+ step = +_;
82
+ return update();
83
+ };
84
+
85
+ // Set or get the context size (the count of metric values).
86
+ // Defaults to 1440 (four hours at ten seconds).
87
+ context.size = function(_) {
88
+ if (!arguments.length) return size;
89
+ scale.range([0, size = +_]);
90
+ return update();
91
+ };
92
+
93
+ // The server delay is the amount of time we wait for the server to compute a
94
+ // metric. This delay may result from clock skew or from delays collecting
95
+ // metrics from various hosts. Defaults to 4 seconds.
96
+ context.serverDelay = function(_) {
97
+ if (!arguments.length) return serverDelay;
98
+ serverDelay = +_;
99
+ return update();
100
+ };
101
+
102
+ // The client delay is the amount of additional time we wait to fetch those
103
+ // metrics from the server. The client and server delay combined represent the
104
+ // age of the most recent displayed metric. Defaults to 1 second.
105
+ context.clientDelay = function(_) {
106
+ if (!arguments.length) return clientDelay;
107
+ clientDelay = +_;
108
+ return update();
109
+ };
110
+
111
+ // Sets the focus to the specified index, and dispatches a "focus" event.
112
+ context.focus = function(i) {
113
+ event.focus.call(context, focus = i);
114
+ return context;
115
+ };
116
+
117
+ // Add, remove or get listeners for events.
118
+ context.on = function(type, listener) {
119
+ if (arguments.length < 2) return event.on(type);
120
+
121
+ event.on(type, listener);
122
+
123
+ // Notify the listener of the current start and stop time, as appropriate.
124
+ // This way, metrics can make requests for data immediately,
125
+ // and likewise the axis can display itself synchronously.
126
+ if (listener != null) {
127
+ if (/^prepare(\.|$)/.test(type)) listener.call(context, start1, stop1);
128
+ if (/^beforechange(\.|$)/.test(type)) listener.call(context, start0, stop0);
129
+ if (/^change(\.|$)/.test(type)) listener.call(context, start0, stop0);
130
+ if (/^focus(\.|$)/.test(type)) listener.call(context, focus);
131
+ }
132
+
133
+ return context;
134
+ };
135
+
136
+ d3.select(window).on("keydown.context-" + ++cubism_id, function() {
137
+ switch (!d3.event.metaKey && d3.event.keyCode) {
138
+ case 37: // left
139
+ if (focus == null) focus = size - 1;
140
+ if (focus > 0) context.focus(--focus);
141
+ break;
142
+ case 39: // right
143
+ if (focus == null) focus = size - 2;
144
+ if (focus < size - 1) context.focus(++focus);
145
+ break;
146
+ default: return;
147
+ }
148
+ d3.event.preventDefault();
149
+ });
150
+
151
+ return update();
152
+ };
153
+
154
+ function cubism_context() {}
155
+
156
+ var cubism_contextPrototype = cubism_context.prototype;
157
+
158
+ cubism_contextPrototype.constant = function(value) {
159
+ return new cubism_metricConstant(this, +value);
160
+ };
161
+ cubism_contextPrototype.cube = function(host) {
162
+ if (!arguments.length) host = "";
163
+ var source = {},
164
+ context = this;
165
+
166
+ source.metric = function(expression) {
167
+ return context.metric(function(start, stop, step, callback) {
168
+ d3.json(host + "/1.0/metric"
169
+ + "?expression=" + encodeURIComponent(expression)
170
+ + "&start=" + cubism_cubeFormatDate(start)
171
+ + "&stop=" + cubism_cubeFormatDate(stop)
172
+ + "&step=" + step, function(data) {
173
+ if (!data) return callback(new Error("unable to load data"));
174
+ callback(null, data.map(function(d) { return d.value; }));
175
+ });
176
+ }, expression += "");
177
+ };
178
+
179
+ // Returns the Cube host.
180
+ source.toString = function() {
181
+ return host;
182
+ };
183
+
184
+ return source;
185
+ };
186
+
187
+ var cubism_cubeFormatDate = d3.time.format.iso;
188
+ cubism_contextPrototype.graphite = function(host) {
189
+ if (!arguments.length) host = "";
190
+ var source = {},
191
+ context = this;
192
+
193
+ source.metric = function(expression) {
194
+ return context.metric(function(start, stop, step, callback) {
195
+ d3.text(host + "/render?format=raw"
196
+ + "&target=" + encodeURIComponent("alias(" + expression + ",'')")
197
+ + "&from=" + cubism_graphiteFormatDate(start - 2 * step) // off-by-two?
198
+ + "&until=" + cubism_graphiteFormatDate(stop - 1000), function(text) {
199
+ if (!text) return callback(new Error("unable to load data"));
200
+ callback(null, cubism_graphiteParse(text));
201
+ });
202
+ }, expression += "");
203
+ };
204
+
205
+ source.find = function(pattern, callback) {
206
+ d3.json(host + "/metrics/find?format=completer"
207
+ + "&query=" + encodeURIComponent(pattern), function(result) {
208
+ if (!result) return callback(new Error("unable to find metrics"));
209
+ callback(null, result.metrics.map(function(d) { return d.path; }));
210
+ });
211
+ };
212
+
213
+ // Returns the graphite host.
214
+ source.toString = function() {
215
+ return host;
216
+ };
217
+
218
+ return source;
219
+ };
220
+
221
+ // Graphite understands seconds since UNIX epoch.
222
+ function cubism_graphiteFormatDate(time) {
223
+ return Math.floor(time / 1000);
224
+ }
225
+
226
+ // Helper method for parsing graphite's raw format.
227
+ function cubism_graphiteParse(text) {
228
+ var i = text.indexOf("|"),
229
+ meta = text.substring(0, i),
230
+ c = meta.lastIndexOf(","),
231
+ b = meta.lastIndexOf(",", c - 1),
232
+ a = meta.lastIndexOf(",", b - 1),
233
+ start = meta.substring(a + 1, b) * 1000,
234
+ step = meta.substring(c + 1) * 1000;
235
+ return text
236
+ .substring(i + 1)
237
+ .split(",")
238
+ .slice(1) // the first value is always None?
239
+ .map(function(d) { return +d; });
240
+ }
241
+ function cubism_metric(context) {
242
+ if (!(context instanceof cubism_context)) throw new Error("invalid context");
243
+ this.context = context;
244
+ }
245
+
246
+ var cubism_metricPrototype = cubism_metric.prototype;
247
+
248
+ cubism_metricPrototype.valueAt = function() {
249
+ return NaN;
250
+ };
251
+
252
+ cubism_metricPrototype.alias = function(name) {
253
+ this.toString = function() { return name; };
254
+ return this;
255
+ };
256
+
257
+ cubism_metricPrototype.extent = function() {
258
+ var i = 0,
259
+ n = this.context.size(),
260
+ value,
261
+ min = Infinity,
262
+ max = -Infinity;
263
+ while (++i < n) {
264
+ value = this.valueAt(i);
265
+ if (value < min) min = value;
266
+ if (value > max) max = value;
267
+ }
268
+ return [min, max];
269
+ };
270
+
271
+ cubism_metricPrototype.on = function(type, listener) {
272
+ return arguments.length < 2 ? null : this;
273
+ };
274
+
275
+ cubism_metricPrototype.shift = function() {
276
+ return this;
277
+ };
278
+
279
+ cubism_metricPrototype.on = function() {
280
+ return arguments.length < 2 ? null : this;
281
+ };
282
+
283
+ cubism_contextPrototype.metric = function(request, name) {
284
+ var context = this,
285
+ metric = new cubism_metric(context),
286
+ id = ".metric-" + ++cubism_id,
287
+ start = -Infinity,
288
+ stop,
289
+ step = context.step(),
290
+ size = context.size(),
291
+ values = [],
292
+ event = d3.dispatch("change"),
293
+ listening = 0,
294
+ fetching;
295
+
296
+ // Prefetch new data into a temporary array.
297
+ function prepare(start1, stop) {
298
+ var steps = Math.min(size, Math.round((start1 - start) / step));
299
+ if (!steps || fetching) return; // already fetched, or fetching!
300
+ fetching = true;
301
+ steps = Math.min(size, steps + cubism_metricOverlap);
302
+ var start0 = new Date(stop - steps * step);
303
+ request(start0, stop, step, function(error, data) {
304
+ fetching = false;
305
+ if (error) return console.warn(error);
306
+ var i = isFinite(start) ? Math.round((start0 - start) / step) : 0;
307
+ for (var j = 0, m = data.length; j < m; ++j) values[j + i] = data[j];
308
+ event.change.call(metric, start, stop);
309
+ });
310
+ }
311
+
312
+ // When the context changes, switch to the new data, ready-or-not!
313
+ function beforechange(start1, stop1) {
314
+ if (!isFinite(start)) start = start1;
315
+ values.splice(0, Math.max(0, Math.min(size, Math.round((start1 - start) / step))));
316
+ start = start1;
317
+ stop = stop1;
318
+ }
319
+
320
+ //
321
+ metric.valueAt = function(i) {
322
+ return values[i];
323
+ };
324
+
325
+ //
326
+ metric.shift = function(offset) {
327
+ return context.metric(cubism_metricShift(request, +offset));
328
+ };
329
+
330
+ //
331
+ metric.on = function(type, listener) {
332
+ if (!arguments.length) return event.on(type);
333
+
334
+ // If there are no listeners, then stop listening to the context,
335
+ // and avoid unnecessary fetches.
336
+ if (listener == null) {
337
+ if (event.on(type) != null && --listening == 0) {
338
+ context.on("prepare" + id, null).on("beforechange" + id, null);
339
+ }
340
+ } else {
341
+ if (event.on(type) == null && ++listening == 1) {
342
+ context.on("prepare" + id, prepare).on("beforechange" + id, beforechange);
343
+ }
344
+ }
345
+
346
+ event.on(type, listener);
347
+
348
+ // Notify the listener of the current start and stop time, as appropriate.
349
+ // This way, charts can display synchronous metrics immediately.
350
+ if (listener != null) {
351
+ if (/^change(\.|$)/.test(type)) listener.call(context, start, stop);
352
+ }
353
+
354
+ return metric;
355
+ };
356
+
357
+ //
358
+ if (arguments.length > 1) metric.toString = function() {
359
+ return name;
360
+ };
361
+
362
+ return metric;
363
+ };
364
+
365
+ // Number of metric to refetch each period, in case of lag.
366
+ var cubism_metricOverlap = 6;
367
+
368
+ // Wraps the specified request implementation, and shifts time by the given offset.
369
+ function cubism_metricShift(request, offset) {
370
+ return function(start, stop, step, callback) {
371
+ request(new Date(+start + offset), new Date(+stop + offset), step, callback);
372
+ };
373
+ }
374
+ function cubism_metricConstant(context, value) {
375
+ cubism_metric.call(this, context);
376
+ value = +value;
377
+ var name = value + "";
378
+ this.valueOf = function() { return value; };
379
+ this.toString = function() { return name; };
380
+ }
381
+
382
+ var cubism_metricConstantPrototype = cubism_metricConstant.prototype = Object.create(cubism_metric.prototype);
383
+
384
+ cubism_metricConstantPrototype.valueAt = function() {
385
+ return +this;
386
+ };
387
+
388
+ cubism_metricConstantPrototype.extent = function() {
389
+ return [+this, +this];
390
+ };
391
+ function cubism_metricOperator(name, operate) {
392
+
393
+ function cubism_metricOperator(left, right) {
394
+ if (!(right instanceof cubism_metric)) right = new cubism_metricConstant(left.context, right);
395
+ else if (left.context !== right.context) throw new Error("mismatch context");
396
+ cubism_metric.call(this, left.context);
397
+ this.left = left;
398
+ this.right = right;
399
+ this.toString = function() { return left + " " + name + " " + right; };
400
+ }
401
+
402
+ var cubism_metricOperatorPrototype = cubism_metricOperator.prototype = Object.create(cubism_metric.prototype);
403
+
404
+ cubism_metricOperatorPrototype.valueAt = function(i) {
405
+ return operate(this.left.valueAt(i), this.right.valueAt(i));
406
+ };
407
+
408
+ cubism_metricOperatorPrototype.shift = function(offset) {
409
+ return new cubism_metricOperator(this.left.shift(offset), this.right.shift(offset));
410
+ };
411
+
412
+ cubism_metricOperatorPrototype.on = function(type, listener) {
413
+ if (arguments.length < 2) return this.left.on(type);
414
+ this.left.on(type, listener);
415
+ this.right.on(type, listener);
416
+ return this;
417
+ };
418
+
419
+ return function(right) {
420
+ return new cubism_metricOperator(this, right);
421
+ };
422
+ }
423
+
424
+ cubism_metricPrototype.add = cubism_metricOperator("+", function(left, right) {
425
+ return left + right;
426
+ });
427
+
428
+ cubism_metricPrototype.subtract = cubism_metricOperator("-", function(left, right) {
429
+ return left - right;
430
+ });
431
+
432
+ cubism_metricPrototype.multiply = cubism_metricOperator("*", function(left, right) {
433
+ return left * right;
434
+ });
435
+
436
+ cubism_metricPrototype.divide = cubism_metricOperator("/", function(left, right) {
437
+ return left / right;
438
+ });
439
+ cubism_contextPrototype.horizon = function() {
440
+ var context = this,
441
+ mode = "offset",
442
+ buffer = document.createElement("canvas"),
443
+ width = buffer.width = context.size(),
444
+ height = buffer.height = 30,
445
+ scale = d3.scale.linear().interpolate(d3.interpolateRound),
446
+ metric = cubism_identity,
447
+ extent = null,
448
+ title = cubism_identity,
449
+ format = d3.format(".2s"),
450
+ colors = ["#08519c","#3182bd","#6baed6","#bdd7e7","#bae4b3","#74c476","#31a354","#006d2c"];
451
+
452
+ function horizon(selection) {
453
+
454
+ selection
455
+ .on("mousemove.horizon", function() { context.focus(d3.mouse(this)[0]); })
456
+ .on("mouseout.horizon", function() { context.focus(null); });
457
+
458
+ selection.append("canvas")
459
+ .attr("width", width)
460
+ .attr("height", height);
461
+
462
+ selection.append("span")
463
+ .attr("class", "title")
464
+ .text(title);
465
+
466
+ selection.append("span")
467
+ .attr("class", "value");
468
+
469
+ selection.each(function(d, i) {
470
+ var that = this,
471
+ id = ++cubism_id,
472
+ metric_ = typeof metric === "function" ? metric.call(that, d, i) : metric,
473
+ colors_ = typeof colors === "function" ? colors.call(that, d, i) : colors,
474
+ extent_ = typeof extent === "function" ? extent.call(that, d, i) : extent,
475
+ start = -Infinity,
476
+ step = context.step(),
477
+ canvas = d3.select(that).select("canvas"),
478
+ span = d3.select(that).select(".value"),
479
+ max_,
480
+ m = colors_.length >> 1,
481
+ ready;
482
+
483
+ canvas.datum({id: id, metric: metric_});
484
+ canvas = canvas.node().getContext("2d");
485
+
486
+ function change(start1, stop) {
487
+ canvas.save();
488
+
489
+ // compute the new extent and ready flag
490
+ var extent = metric_.extent();
491
+ ready = extent.every(isFinite);
492
+ if (extent_ != null) extent = extent_;
493
+
494
+ // if this is an update (with no extent change), copy old values!
495
+ var i0 = 0, max = Math.max(-extent[0], extent[1]);
496
+ if (this === context) {
497
+ if (max == max_) {
498
+ i0 = width - cubism_metricOverlap;
499
+ var dx = (start1 - start) / step;
500
+ if (dx < width) {
501
+ var canvas0 = buffer.getContext("2d");
502
+ canvas0.clearRect(0, 0, width, height);
503
+ canvas0.drawImage(canvas.canvas, dx, 0, width - dx, height, 0, 0, width - dx, height);
504
+ canvas.clearRect(0, 0, width, height);
505
+ canvas.drawImage(canvas0.canvas, 0, 0);
506
+ }
507
+ }
508
+ start = start1;
509
+ }
510
+
511
+ // update the domain
512
+ scale.domain([0, max_ = max]);
513
+
514
+ // clear for the new data
515
+ canvas.clearRect(i0, 0, width - i0, height);
516
+
517
+ // record whether there are negative values to display
518
+ var negative;
519
+
520
+ // positive bands
521
+ for (var j = 0; j < m; ++j) {
522
+ canvas.fillStyle = colors_[m + j];
523
+
524
+ // Adjust the range based on the current band index.
525
+ var y0 = (j - m + 1) * height;
526
+ scale.range([m * height + y0, y0]);
527
+ y0 = scale(0);
528
+
529
+ for (var i = i0, n = width, y1; i < n; ++i) {
530
+ y1 = metric_.valueAt(i);
531
+ if (y1 <= 0) { negative = true; continue; }
532
+ canvas.fillRect(i, y1 = scale(y1), 1, y0 - y1);
533
+ }
534
+ }
535
+
536
+ if (negative) {
537
+ // enable offset mode
538
+ if (mode === "offset") {
539
+ canvas.translate(0, height);
540
+ canvas.scale(1, -1);
541
+ }
542
+
543
+ // negative bands
544
+ for (var j = 0; j < m; ++j) {
545
+ canvas.fillStyle = colors_[m - 1 - j];
546
+
547
+ // Adjust the range based on the current band index.
548
+ var y0 = (j - m + 1) * height;
549
+ scale.range([m * height + y0, y0]);
550
+ y0 = scale(0);
551
+
552
+ for (var i = i0, n = width, y1; i < n; ++i) {
553
+ y1 = metric_.valueAt(i);
554
+ if (y1 >= 0) continue;
555
+ canvas.fillRect(i, scale(-y1), 1, y0 - scale(-y1));
556
+ }
557
+ }
558
+ }
559
+
560
+ canvas.restore();
561
+ }
562
+
563
+ function focus(i) {
564
+ if (i == null) i = width - 1;
565
+ var value = metric_.valueAt(i);
566
+ span.datum(value).text(isNaN(value) ? null : format);
567
+ }
568
+
569
+ // Update the chart when the context changes.
570
+ context.on("change.horizon-" + id, change);
571
+ context.on("focus.horizon-" + id, focus);
572
+
573
+ // Display the first metric change immediately,
574
+ // but defer subsequent updates to the canvas change.
575
+ // Note that someone still needs to listen to the metric,
576
+ // so that it continues to update automatically.
577
+ metric_.on("change.horizon-" + id, function(start, stop) {
578
+ change(start, stop), focus();
579
+ if (ready) metric_.on("change.horizon-" + id, cubism_identity);
580
+ });
581
+ });
582
+ }
583
+
584
+ horizon.remove = function(selection) {
585
+
586
+ selection
587
+ .on("mousemove.horizon", null)
588
+ .on("mouseout.horizon", null);
589
+
590
+ selection.selectAll("canvas")
591
+ .each(remove)
592
+ .remove();
593
+
594
+ selection.selectAll(".title,.value")
595
+ .remove();
596
+
597
+ function remove(d) {
598
+ d.metric.on("change.horizon-" + d.id, null);
599
+ context.on("change.horizon-" + d.id, null);
600
+ context.on("focus.horizon-" + d.id, null);
601
+ }
602
+ };
603
+
604
+ horizon.mode = function(_) {
605
+ if (!arguments.length) return mode;
606
+ mode = _ + "";
607
+ return horizon;
608
+ };
609
+
610
+ horizon.height = function(_) {
611
+ if (!arguments.length) return height;
612
+ buffer.height = height = +_;
613
+ return horizon;
614
+ };
615
+
616
+ horizon.metric = function(_) {
617
+ if (!arguments.length) return metric;
618
+ metric = _;
619
+ return horizon;
620
+ };
621
+
622
+ horizon.scale = function(_) {
623
+ if (!arguments.length) return scale;
624
+ scale = _;
625
+ return horizon;
626
+ };
627
+
628
+ horizon.extent = function(_) {
629
+ if (!arguments.length) return extent;
630
+ extent = _;
631
+ return horizon;
632
+ };
633
+
634
+ horizon.title = function(_) {
635
+ if (!arguments.length) return title;
636
+ title = _;
637
+ return horizon;
638
+ };
639
+
640
+ horizon.format = function(_) {
641
+ if (!arguments.length) return format;
642
+ format = _;
643
+ return horizon;
644
+ };
645
+
646
+ horizon.colors = function(_) {
647
+ if (!arguments.length) return colors;
648
+ colors = _;
649
+ return horizon;
650
+ };
651
+
652
+ return horizon;
653
+ };
654
+ cubism_contextPrototype.comparison = function() {
655
+ var context = this,
656
+ width = context.size(),
657
+ height = 120,
658
+ scale = d3.scale.linear().interpolate(d3.interpolateRound),
659
+ primary = function(d) { return d[0]; },
660
+ secondary = function(d) { return d[1]; },
661
+ extent = null,
662
+ title = cubism_identity,
663
+ formatPrimary = cubism_comparisonPrimaryFormat,
664
+ formatChange = cubism_comparisonChangeFormat,
665
+ colors = ["#9ecae1", "#225b84", "#a1d99b", "#22723a"],
666
+ strokeWidth = 1.5;
667
+
668
+ function comparison(selection) {
669
+
670
+ selection
671
+ .on("mousemove.comparison", function() { context.focus(d3.mouse(this)[0]); })
672
+ .on("mouseout.comparison", function() { context.focus(null); });
673
+
674
+ selection.append("canvas")
675
+ .attr("width", width)
676
+ .attr("height", height);
677
+
678
+ selection.append("span")
679
+ .attr("class", "title")
680
+ .text(title);
681
+
682
+ selection.append("span")
683
+ .attr("class", "value primary");
684
+
685
+ selection.append("span")
686
+ .attr("class", "value change");
687
+
688
+ selection.each(function(d, i) {
689
+ var that = this,
690
+ id = ++cubism_id,
691
+ primary_ = typeof primary === "function" ? primary.call(that, d, i) : primary,
692
+ secondary_ = typeof secondary === "function" ? secondary.call(that, d, i) : secondary,
693
+ extent_ = typeof extent === "function" ? extent.call(that, d, i) : extent,
694
+ div = d3.select(that),
695
+ canvas = div.select("canvas"),
696
+ spanPrimary = div.select(".value.primary"),
697
+ spanChange = div.select(".value.change"),
698
+ ready;
699
+
700
+ canvas.datum({id: id, primary: primary_, secondary: secondary_});
701
+ canvas = canvas.node().getContext("2d");
702
+
703
+ function change(start, stop) {
704
+ canvas.save();
705
+ canvas.clearRect(0, 0, width, height);
706
+
707
+ // update the scale
708
+ var primaryExtent = primary_.extent(),
709
+ secondaryExtent = secondary_.extent(),
710
+ extent = extent_ == null ? primaryExtent : extent_;
711
+ scale.domain(extent).range([height, 0]);
712
+ ready = primaryExtent.concat(secondaryExtent).every(isFinite);
713
+
714
+ // consistent overplotting
715
+ var round = start / context.step() & 1
716
+ ? cubism_comparisonRoundOdd
717
+ : cubism_comparisonRoundEven;
718
+
719
+ // positive changes
720
+ canvas.fillStyle = colors[2];
721
+ for (var i = 0, n = width; i < n; ++i) {
722
+ var y0 = scale(primary_.valueAt(i)),
723
+ y1 = scale(secondary_.valueAt(i));
724
+ if (y0 < y1) canvas.fillRect(round(i), y0, 1, y1 - y0);
725
+ }
726
+
727
+ // negative changes
728
+ canvas.fillStyle = colors[0];
729
+ for (i = 0; i < n; ++i) {
730
+ var y0 = scale(primary_.valueAt(i)),
731
+ y1 = scale(secondary_.valueAt(i));
732
+ if (y0 > y1) canvas.fillRect(round(i), y1, 1, y0 - y1);
733
+ }
734
+
735
+ // positive values
736
+ canvas.fillStyle = colors[3];
737
+ for (i = 0; i < n; ++i) {
738
+ var y0 = scale(primary_.valueAt(i)),
739
+ y1 = scale(secondary_.valueAt(i));
740
+ if (y0 <= y1) canvas.fillRect(round(i), y0, 1, strokeWidth);
741
+ }
742
+
743
+ // negative values
744
+ canvas.fillStyle = colors[1];
745
+ for (i = 0; i < n; ++i) {
746
+ var y0 = scale(primary_.valueAt(i)),
747
+ y1 = scale(secondary_.valueAt(i));
748
+ if (y0 > y1) canvas.fillRect(round(i), y0 - strokeWidth, 1, strokeWidth);
749
+ }
750
+
751
+ canvas.restore();
752
+ }
753
+
754
+ function focus(i) {
755
+ if (i == null) i = width - 1;
756
+ var valuePrimary = primary_.valueAt(i),
757
+ valueSecondary = secondary_.valueAt(i),
758
+ valueChange = (valuePrimary - valueSecondary) / valueSecondary;
759
+
760
+ spanPrimary
761
+ .datum(valuePrimary)
762
+ .text(isNaN(valuePrimary) ? null : formatPrimary);
763
+
764
+ spanChange
765
+ .datum(valueChange)
766
+ .text(isNaN(valueChange) ? null : formatChange)
767
+ .attr("class", "value change " + (valueChange > 0 ? "positive" : valueChange < 0 ? "negative" : ""));
768
+ }
769
+
770
+ // Display the first primary change immediately,
771
+ // but defer subsequent updates to the context change.
772
+ // Note that someone still needs to listen to the metric,
773
+ // so that it continues to update automatically.
774
+ primary_.on("change.comparison-" + id, firstChange);
775
+ secondary_.on("change.comparison-" + id, firstChange);
776
+ function firstChange(start, stop) {
777
+ change(start, stop), focus();
778
+ if (ready) {
779
+ primary_.on("change.comparison-" + id, cubism_identity);
780
+ secondary_.on("change.comparison-" + id, cubism_identity);
781
+ }
782
+ }
783
+
784
+ // Update the chart when the context changes.
785
+ context.on("change.comparison-" + id, change);
786
+ context.on("focus.comparison-" + id, focus);
787
+ });
788
+ }
789
+
790
+ comparison.remove = function(selection) {
791
+
792
+ selection
793
+ .on("mousemove.comparison", null)
794
+ .on("mouseout.comparison", null);
795
+
796
+ selection.selectAll("canvas")
797
+ .each(remove)
798
+ .remove();
799
+
800
+ selection.selectAll(".title,.value")
801
+ .remove();
802
+
803
+ function remove(d) {
804
+ d.primary.on("change.comparison-" + d.id, null);
805
+ d.secondary.on("change.comparison-" + d.id, null);
806
+ context.on("change.comparison-" + d.id, null);
807
+ context.on("focus.comparison-" + d.id, null);
808
+ }
809
+ };
810
+
811
+ comparison.height = function(_) {
812
+ if (!arguments.length) return height;
813
+ height = +_;
814
+ return comparison;
815
+ };
816
+
817
+ comparison.primary = function(_) {
818
+ if (!arguments.length) return primary;
819
+ primary = _;
820
+ return comparison;
821
+ };
822
+
823
+ comparison.secondary = function(_) {
824
+ if (!arguments.length) return secondary;
825
+ secondary = _;
826
+ return comparison;
827
+ };
828
+
829
+ comparison.scale = function(_) {
830
+ if (!arguments.length) return scale;
831
+ scale = _;
832
+ return comparison;
833
+ };
834
+
835
+ comparison.extent = function(_) {
836
+ if (!arguments.length) return extent;
837
+ extent = _;
838
+ return comparison;
839
+ };
840
+
841
+ comparison.title = function(_) {
842
+ if (!arguments.length) return title;
843
+ title = _;
844
+ return comparison;
845
+ };
846
+
847
+ comparison.formatPrimary = function(_) {
848
+ if (!arguments.length) return formatPrimary;
849
+ formatPrimary = _;
850
+ return comparison;
851
+ };
852
+
853
+ comparison.formatChange = function(_) {
854
+ if (!arguments.length) return formatChange;
855
+ formatChange = _;
856
+ return comparison;
857
+ };
858
+
859
+ comparison.colors = function(_) {
860
+ if (!arguments.length) return colors;
861
+ colors = _;
862
+ return comparison;
863
+ };
864
+
865
+ comparison.strokeWidth = function(_) {
866
+ if (!arguments.length) return strokeWidth;
867
+ strokeWidth = _;
868
+ return comparison;
869
+ };
870
+
871
+ return comparison;
872
+ };
873
+
874
+ var cubism_comparisonPrimaryFormat = d3.format(".2s"),
875
+ cubism_comparisonChangeFormat = d3.format("+.0%");
876
+
877
+ function cubism_comparisonRoundEven(i) {
878
+ return i & 0xfffffe;
879
+ }
880
+
881
+ function cubism_comparisonRoundOdd(i) {
882
+ return ((i + 1) & 0xfffffe) - 1;
883
+ }
884
+ cubism_contextPrototype.axis = function() {
885
+ var context = this,
886
+ scale = context.scale,
887
+ axis_ = d3.svg.axis().scale(scale),
888
+ format = context.step() < 6e4 ? cubism_axisFormatSeconds : cubism_axisFormatMinutes;
889
+
890
+ function axis(selection) {
891
+ var id = ++cubism_id,
892
+ tick;
893
+
894
+ var g = selection.append("svg")
895
+ .datum({id: id})
896
+ .attr("width", context.size())
897
+ .attr("height", Math.max(28, -axis.tickSize()))
898
+ .append("g")
899
+ .attr("transform", "translate(0," + (axis_.orient() === "top" ? 27 : 4) + ")")
900
+ .call(axis_);
901
+
902
+ context.on("change.axis-" + id, function() {
903
+ g.call(axis_);
904
+ if (!tick) tick = cloneTick();
905
+ });
906
+
907
+ context.on("focus.axis-" + id, function(i) {
908
+ if (tick) {
909
+ if (i == null) {
910
+ tick.style("display", "none");
911
+ g.selectAll("text").style("fill-opacity", null);
912
+ } else {
913
+ tick.style("display", null).attr("x", i).text(format(scale.invert(i)));
914
+ var dx = tick.node().getComputedTextLength() + 6;
915
+ g.selectAll("text").style("fill-opacity", function(d) { return Math.abs(scale(d) - i) < dx ? 0 : 1; });
916
+ }
917
+ }
918
+ });
919
+
920
+ function cloneTick() {
921
+ return g.select(function() { return this.appendChild(g.select("text").node().cloneNode(true)); })
922
+ .style("display", "none")
923
+ .text(null);
924
+ }
925
+ }
926
+
927
+ axis.remove = function(selection) {
928
+
929
+ selection.selectAll("svg")
930
+ .each(remove)
931
+ .remove();
932
+
933
+ function remove(d) {
934
+ context.on("change.axis-" + d.id, null);
935
+ context.on("focus.axis-" + d.id, null);
936
+ }
937
+ };
938
+
939
+ return d3.rebind(axis, axis_,
940
+ "orient",
941
+ "ticks",
942
+ "tickSubdivide",
943
+ "tickSize",
944
+ "tickPadding",
945
+ "tickFormat");
946
+ };
947
+
948
+ var cubism_axisFormatSeconds = d3.time.format("%I:%M:%S %p"),
949
+ cubism_axisFormatMinutes = d3.time.format("%I:%M %p");
950
+ cubism_contextPrototype.rule = function() {
951
+ var context = this;
952
+
953
+ function rule(selection) {
954
+ var id = ++cubism_id;
955
+
956
+ var line = selection.append("div")
957
+ .datum({id: id})
958
+ .attr("class", "line")
959
+ .style("position", "fixed")
960
+ .style("top", 0)
961
+ .style("right", 0)
962
+ .style("bottom", 0)
963
+ .style("width", "1px")
964
+ .style("pointer-events", "none");
965
+
966
+ context.on("focus.rule-" + id, function(i) {
967
+ line
968
+ .style("display", i == null ? "none" : null)
969
+ .style("left", function() { return this.parentNode.getBoundingClientRect().left + i + "px"; });
970
+ });
971
+ }
972
+
973
+ rule.remove = function(selection) {
974
+
975
+ selection.selectAll(".line")
976
+ .each(remove)
977
+ .remove();
978
+
979
+ function remove(d) {
980
+ context.on("focus.rule-" + d.id, null);
981
+ }
982
+ };
983
+
984
+ return rule;
985
+ };
986
+ })(this);