elasticsearch-paramedic-rack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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);