Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

started to move to rickshaw

  • Loading branch information...
commit c56d19544c419f32460e29e6d1eabb06ee828d37 1 parent c7c22f8
Tarek Ziade authored February 27, 2013
2  .gitignore
@@ -8,7 +8,7 @@ build
8 8
 docs/_gh-pages
9 9
 elasticsearch
10 10
 include
11  
-lib
  11
+^lib
12 12
 man
13 13
 pyramid.log
14 14
 .DS_Store
2  monolith.ini
... ...
@@ -1,6 +1,6 @@
1 1
 [app:main]
2 2
 use = egg:monolith
3  
-elasticsearch.hosts = localhost:9200
  3
+elasticsearch.hosts = 0.0.0.0:9200
4 4
 cors.origins = *
5 5
 
6 6
 [server:main]
18  monolith/media/app.js
@@ -65,11 +65,21 @@ app.directive('chart', function() {
65 65
     // XXX can this be externalized as a template ?
66 66
     // not an ugly string
67 67
     template:
68  
-    '<div class="droppable chart"><div class="draggable">' +
69  
-    '<div id="chart-{{id}}" style="height:300px; margin: 0 auto">' +
  68
+    '<div><div class="chart span12" >' +
  69
+    
  70
+    // y axis legend 
  71
+    '<div id="y_axis-{{id}}"/>' +
  72
+
  73
+    // actual chart
  74
+    '<div id="chart-{{id}}" style="height:300px; margin: 0 auto"/>' +
  75
+
  76
+    // legend and change button
  77
+    '<div class="span12">' +
  78
+    '<a href="#modal-{{id}}" role="button" class="span2 btn btn-primary" data-toggle="modal">Change</a>' +
  79
+    '<div class="span3 offset7" id="legend-{{id}}"></div>' +
70 80
     '</div>' +
71  
-    '<a href="#modal-{{id}}" role="button" class="span2 offset1 btn btn-primary" data-toggle="modal">Change</a>' +
72  
-    '<div style="clear:both"/>' +
  81
+
  82
+    // modal box 
73 83
     '<div id="modal-{{id}}" class="modal hide fade">' +
74 84
     '<div class="modal-header">' +
75 85
     '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>' +
73  monolith/media/detail.css
... ...
@@ -0,0 +1,73 @@
  1
+.rickshaw_graph .detail {
  2
+	pointer-events: none;
  3
+	position: absolute;
  4
+	top: 0;
  5
+	z-index: 2;
  6
+	background: rgba(0, 0, 0, 0.1);
  7
+	bottom: 0;
  8
+	width: 1px;
  9
+	transition: opacity 0.25s linear;
  10
+	-moz-transition: opacity 0.25s linear;
  11
+	-o-transition: opacity 0.25s linear;
  12
+	-webkit-transition: opacity 0.25s linear;
  13
+}
  14
+.rickshaw_graph .detail.inactive {
  15
+	opacity: 0;
  16
+}
  17
+.rickshaw_graph .detail .item.active {
  18
+	opacity: 1;
  19
+}
  20
+.rickshaw_graph .detail .x_label {
  21
+	font-family: Arial, sans-serif;
  22
+	border-radius: 3px;
  23
+	padding: 6px;
  24
+	opacity: 0.5;
  25
+	border: 1px solid #e0e0e0;
  26
+	font-size: 12px;
  27
+	position: absolute;
  28
+	background: white;
  29
+	white-space: nowrap;
  30
+}
  31
+.rickshaw_graph .detail .item {
  32
+	position: absolute;
  33
+	z-index: 2;
  34
+	border-radius: 3px;
  35
+	padding: 0.25em;
  36
+	font-size: 12px;
  37
+	font-family: Arial, sans-serif;
  38
+	opacity: 0;
  39
+	background: rgba(0, 0, 0, 0.4);
  40
+	color: white;
  41
+	border: 1px solid rgba(0, 0, 0, 0.4);
  42
+	margin-left: 1em;
  43
+	margin-top: -1em;
  44
+	white-space: nowrap;
  45
+}
  46
+.rickshaw_graph .detail .item.active {
  47
+	opacity: 1;
  48
+	background: rgba(0, 0, 0, 0.8);
  49
+}
  50
+.rickshaw_graph .detail .item:before {
  51
+	content: "\25c2";
  52
+	position: absolute;
  53
+	left: -0.5em;
  54
+	color: rgba(0, 0, 0, 0.7);
  55
+	width: 0;
  56
+}
  57
+.rickshaw_graph .detail .dot {
  58
+	width: 4px;
  59
+	height: 4px;
  60
+	margin-left: -4px;
  61
+	margin-top: -3px;
  62
+	border-radius: 5px;
  63
+	position: absolute;
  64
+	box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
  65
+	background: white;
  66
+	border-width: 2px;
  67
+	border-style: solid;
  68
+	display: none;
  69
+	background-clip: padding-box;
  70
+}
  71
+.rickshaw_graph .detail .dot.active {
  72
+	display: block;
  73
+}
175  monolith/media/graph.css
... ...
@@ -0,0 +1,175 @@
  1
+/* graph */
  2
+
  3
+.rickshaw_graph {
  4
+	position: relative;
  5
+}
  6
+.rickshaw_graph svg {
  7
+	display: block;	
  8
+	overflow: hidden;
  9
+}
  10
+
  11
+/* ticks */
  12
+
  13
+.rickshaw_graph .x_tick {
  14
+	position: absolute;
  15
+	top: 0;
  16
+	bottom: 0;
  17
+	width: 0px;
  18
+	border-left: 1px dotted rgba(0, 0, 0, 0.2);
  19
+	pointer-events: none;
  20
+}
  21
+.rickshaw_graph .x_tick .title {
  22
+	position: absolute;
  23
+	font-size: 12px;
  24
+	font-family: Arial, sans-serif;
  25
+	opacity: 0.5;
  26
+	white-space: nowrap;
  27
+	margin-left: 3px;
  28
+	bottom: 1px;
  29
+}
  30
+
  31
+/* annotations */
  32
+
  33
+.rickshaw_annotation_timeline {
  34
+	height: 1px;
  35
+	border-top: 1px solid #e0e0e0;
  36
+	margin-top: 10px;
  37
+	position: relative;
  38
+}
  39
+.rickshaw_annotation_timeline .annotation {
  40
+	position: absolute;
  41
+	height: 6px;
  42
+	width: 6px;
  43
+	margin-left: -2px;
  44
+	top: -3px;
  45
+	border-radius: 5px;
  46
+	background-color: rgba(0, 0, 0, 0.25);
  47
+}
  48
+.rickshaw_graph .annotation_line {
  49
+	position: absolute;
  50
+	top: 0;
  51
+	bottom: -6px;
  52
+	width: 0px;
  53
+	border-left: 2px solid rgba(0, 0, 0, 0.3);
  54
+	display: none;
  55
+}
  56
+.rickshaw_graph .annotation_line.active {
  57
+	display: block;
  58
+}
  59
+
  60
+.rickshaw_graph .annotation_range {
  61
+        background: rgba(0, 0, 0, 0.1);
  62
+        display: none;
  63
+        position: absolute;
  64
+        top: 0;
  65
+        bottom: -6px;
  66
+        z-index: -10;
  67
+}
  68
+.rickshaw_graph .annotation_range.active {
  69
+        display: block;
  70
+}
  71
+.rickshaw_graph .annotation_range.active.offscreen {
  72
+        display: none;
  73
+}
  74
+
  75
+.rickshaw_annotation_timeline .annotation .content {
  76
+	background: white;
  77
+	color: black;
  78
+	opacity: 0.9;
  79
+	padding: 5px 5px;
  80
+	box-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
  81
+	border-radius: 3px;
  82
+	position: relative;
  83
+	z-index: 20;
  84
+	font-size: 12px;
  85
+	padding: 6px 8px 8px;
  86
+	top: 18px;
  87
+	left: -11px;
  88
+	width: 160px;
  89
+	display: none;
  90
+	cursor: pointer;
  91
+}
  92
+.rickshaw_annotation_timeline .annotation .content:before {
  93
+	content: "\25b2";
  94
+	position: absolute;
  95
+	top: -11px;
  96
+	color: white;
  97
+	text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.8);
  98
+}
  99
+.rickshaw_annotation_timeline .annotation.active,
  100
+.rickshaw_annotation_timeline .annotation:hover {
  101
+	background-color: rgba(0, 0, 0, 0.8);
  102
+	cursor: none;
  103
+}
  104
+.rickshaw_annotation_timeline .annotation .content:hover {
  105
+	z-index: 50;
  106
+}
  107
+.rickshaw_annotation_timeline .annotation.active .content {
  108
+	display: block;
  109
+}
  110
+.rickshaw_annotation_timeline .annotation:hover .content {
  111
+	display: block;
  112
+	z-index: 50;
  113
+}
  114
+.rickshaw_graph .y_axis,
  115
+.rickshaw_graph  .x_axis_d3 {
  116
+	fill: none;
  117
+}
  118
+.rickshaw_graph .y_ticks .tick,
  119
+.rickshaw_graph .x_ticks_d3 .tick {
  120
+	stroke: rgba(0, 0, 0, 0.16);
  121
+	stroke-width: 2px;
  122
+	shape-rendering: crisp-edges;
  123
+	pointer-events: none;
  124
+}
  125
+.rickshaw_graph .y_grid .tick,
  126
+.rickshaw_graph .x_grid_d3 .tick {
  127
+	z-index: -1;
  128
+	stroke: rgba(0, 0, 0, 0.20);
  129
+	stroke-width: 1px;
  130
+	stroke-dasharray: 1 1;
  131
+}
  132
+.rickshaw_graph .y_grid path,
  133
+.rickshaw_graph .x_grid_d3 path  {
  134
+	fill: none;
  135
+	stroke: none;
  136
+}
  137
+.rickshaw_graph .y_ticks path,
  138
+.rickshaw_graph .x_ticks_d3 path {
  139
+	fill: none;
  140
+	stroke: #808080;
  141
+}
  142
+.rickshaw_graph .y_ticks text,
  143
+.rickshaw_graph .x_ticks_d3 text {
  144
+	opacity: 0.5;
  145
+	font-size: 12px;
  146
+	pointer-events: none;
  147
+}
  148
+.rickshaw_graph .x_tick.glow .title,
  149
+.rickshaw_graph .y_ticks.glow text {
  150
+	fill: black;
  151
+	color: black;
  152
+	text-shadow: 
  153
+		-1px 1px 0 rgba(255, 255, 255, 0.1),
  154
+		1px -1px 0 rgba(255, 255, 255, 0.1),
  155
+		1px 1px 0 rgba(255, 255, 255, 0.1),
  156
+		0px 1px 0 rgba(255, 255, 255, 0.1),
  157
+		0px -1px 0 rgba(255, 255, 255, 0.1),
  158
+		1px 0px 0 rgba(255, 255, 255, 0.1),
  159
+		-1px 0px 0 rgba(255, 255, 255, 0.1),
  160
+		-1px -1px 0 rgba(255, 255, 255, 0.1);
  161
+}
  162
+.rickshaw_graph .x_tick.inverse .title,
  163
+.rickshaw_graph .y_ticks.inverse text {
  164
+	fill: white;
  165
+	color: white;
  166
+	text-shadow: 
  167
+		-1px 1px 0 rgba(0, 0, 0, 0.8),
  168
+		1px -1px 0 rgba(0, 0, 0, 0.8),
  169
+		1px 1px 0 rgba(0, 0, 0, 0.8),
  170
+		0px 1px 0 rgba(0, 0, 0, 0.8),
  171
+		0px -1px 0 rgba(0, 0, 0, 0.8),
  172
+		1px 0px 0 rgba(0, 0, 0, 0.8),
  173
+		-1px 0px 0 rgba(0, 0, 0, 0.8),
  174
+		-1px -1px 0 rgba(0, 0, 0, 0.8);
  175
+}
40  monolith/media/index.html
@@ -3,25 +3,45 @@
3 3
   <head>
4 4
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5 5
     <title>Monolith Dashboard</title>
6  
-    <link rel="stylesheet" href="jquery-ui.css" />
7  
-    <link rel="stylesheet" href="bootstrap.css" />
8  
-    <link rel="stylesheet" href="bootstrap-responsive.css" />
9  
-    <link rel="stylesheet" href="monolith.css" />
10  
-    <link rel="stylesheet" href="datepicker.css" />
  6
+    <link type="text/css"rel="stylesheet" href="jquery-ui.css" />
  7
+    <link type="text/css" rel="stylesheet" href="bootstrap.css" />
  8
+    <link type="text/css" rel="stylesheet" href="bootstrap-responsive.css" />
  9
+    <link type="text/css" rel="stylesheet" href="monolith.css" />
  10
+    <link type="text/css" rel="stylesheet" href="datepicker.css" />
  11
+    <link type="text/css" rel="stylesheet" href="graph.css">
  12
+    <link type="text/css" rel="stylesheet" href="detail.css">
  13
+    <link type="text/css" rel="stylesheet" href="legend.css">
  14
+    <link type="text/css" rel="stylesheet" href="lines.css">
11 15
 
12 16
     <script type="text/javascript" src="lib/jquery.min.js"></script>
13 17
     <script type="text/javascript" src="lib/jquery-ui.js"></script>
14 18
     <script type="text/javascript" src="lib/jquery.class.js"></script>
15 19
     <script type="text/javascript" src="lib/angular.min.js"></script>
16  
-    <script type="text/javascript" src="http://code.highcharts.com/highcharts.js"></script>
17  
-    <script type="text/javascript"
18  
-      src="http://code.highcharts.com/modules/exporting.js"></script>
  20
+    <script type="text/javascript" src="lib/d3.v2.js"></script>
  21
+    <script type="text/javascript" src="lib/rickshaw.js"></script>
19 22
     <script type="text/javascript" src="lib/bootstrap.js"></script>
20 23
     <script type="text/javascript" src="lib/bootstrap-datepicker.js"></script>
21 24
     <script type="text/javascript" src="lib/monolith.js"></script>
22 25
 
23 26
     <script type="text/javascript" src="app.js"></script>
24 27
 
  28
+  <style>
  29
+    .rickshaw_graph .detail .x_label { display: none }
  30
+    .rickshaw_graph .detail .item { line-height: 1.4; padding: 0.5em }
  31
+    .detail_swatch { float: right; display: inline-block; width: 10px; height: 10px; margin: 0 4px 0 0 }
  32
+    .rickshaw_graph .detail .date { color: #a0a0a0 }
  33
+
  34
+#y_axis-chart2 {
  35
+  position: relative;
  36
+  width: 40px;
  37
+  height: 100%;
  38
+  float: right;
  39
+}
  40
+.rickshaw_legend {
  41
+/*  float: left;*/
  42
+}
  43
+  </style>
  44
+
25 45
   </head>
26 46
   <body ng-app="components" class="preview" data-spy="scroll" data-target=".subnav" data-offset="80">
27 47
     <div class="navbar navbar-fixed-top">
@@ -39,14 +59,14 @@
39 59
    </div> <!-- end navbar-->
40 60
 
41 61
 
42  
-<dashboard server="http://166.78.8.5:6543">
  62
+<dashboard server="http://0.0.0.0:6543">
43 63
 <div class="container-fluid">
44 64
  <div class="row-fluid">
45 65
    <div class="span12">
46 66
        <div class="span6"> 
47 67
          <chart title="Daily Page views" 
48 68
                 id="chart2"
49  
-		type="aggregate"
  69
+                type="aggregate"
50 70
                 field="pageviews"
51 71
                 interval="day"/>
52 72
        </div>
73  monolith/media/legend.css
... ...
@@ -0,0 +1,73 @@
  1
+.rickshaw_legend {
  2
+	font-family: Arial;
  3
+	font-size: 12px;
  4
+	color: white;
  5
+	background: #404040;
  6
+	display: inline-block;
  7
+	padding: 12px 5px; 
  8
+	border-radius: 2px;
  9
+	position: relative;
  10
+}
  11
+.rickshaw_legend:hover {
  12
+	z-index: 10;
  13
+}
  14
+.rickshaw_legend .swatch {
  15
+	width: 10px;
  16
+	height: 10px;
  17
+	border: 1px solid rgba(0, 0, 0, 0.2);
  18
+}
  19
+.rickshaw_legend .line {
  20
+	clear: both;
  21
+	line-height: 140%;
  22
+	padding-right: 15px;
  23
+}
  24
+.rickshaw_legend .line .swatch {
  25
+	display: inline-block;
  26
+	margin-right: 3px;
  27
+	border-radius: 2px;
  28
+}
  29
+.rickshaw_legend .label {
  30
+	margin: 0;
  31
+	white-space: nowrap;
  32
+	display: inline;
  33
+	font-size: inherit;
  34
+	background-color: transparent;
  35
+	color: inherit;
  36
+	font-weight: normal;
  37
+	line-height: normal;
  38
+	padding: 0px;
  39
+	text-shadow: none;
  40
+}
  41
+.rickshaw_legend .action:hover {
  42
+	opacity: 0.6;
  43
+}
  44
+.rickshaw_legend .action {
  45
+	margin-right: 0.2em;
  46
+	font-size: 10px;
  47
+	opacity: 0.2;
  48
+	cursor: pointer;
  49
+	font-size: 14px;
  50
+}
  51
+.rickshaw_legend .line.disabled {
  52
+	opacity: 0.4;
  53
+}
  54
+.rickshaw_legend ul {
  55
+	list-style-type: none;
  56
+	margin: 0;
  57
+	padding: 0;
  58
+	margin: 2px;
  59
+	cursor: pointer;
  60
+}
  61
+.rickshaw_legend li {
  62
+	padding: 0 0 0 2px;
  63
+	min-width: 80px;
  64
+	white-space: nowrap;
  65
+}
  66
+.rickshaw_legend li:hover {
  67
+	background: rgba(255, 255, 255, 0.08);
  68
+	border-radius: 3px;
  69
+}
  70
+.rickshaw_legend li:active {
  71
+	background: rgba(255, 255, 255, 0.2);
  72
+	border-radius: 3px;
  73
+}
7,026  monolith/media/lib/d3.v2.js
7026 additions, 0 deletions not shown
127  monolith/media/lib/monolith.js
... ...
@@ -1,8 +1,8 @@
1 1
 /*
2 2
    monolith.js
3 3
 
4  
-   Provides a MonolithSeries & MonolithAggregate class that will draw a
5  
-   HighCharts diagram by querying Elastic Search
  4
+   Provides a MonolithSeries & MonolithAggregate class that will draw 
  5
+   a Rickshaw chart y querying Elastic Search
6 6
 
7 7
    common options:
8 8
 
@@ -25,13 +25,6 @@
25 25
 */
26 26
 var minute = 60000;
27 27
 
28  
-Highcharts.setOptions({
29  
-    global: {
30  
-        useUTC: false
31  
-    }
32  
-});
33  
-
34  
-
35 28
 
36 29
 function queryES(server, query) {
37 30
   var result;
@@ -149,44 +142,58 @@ $.Class.extend("MonolithBase", {},
149 142
    },
150 143
 
151 144
      _getChart: function () {
152  
-          var chart = new Highcharts.Chart({
153  
-          chart: {
154  
-            renderTo: this.container,
155  
-            type: this.type,
156  
-            marginRight: 30,
157  
-            renderer: 'SVG'
158  
-            },
159  
-            title: {
160  
-                text: this.title
161  
-            },
162  
-            tooltip: {
163  
-              shared : true,
164  
-              crosshairs : true,
165  
-            },
166  
-
167  
-            plotOptions: {
168  
-                line: {
169  
-                    dataLabels: {
170  
-                        enabled: true
171  
-                    },
172  
-            enableMouseTracking: true
173  
-                }
174  
-            },
175  
-            xAxis: {
176  
-                type: 'datetime',
177  
-                tickPixelInterval: 150
178  
-            },
179  
-            yAxis: this.yAxis,
180  
-            legend: {
181  
-                enabled: true
182  
-            },
183  
-            exporting: {
184  
-                enabled: false
185  
-            },
186  
-            series: this.series
  145
+var series = [
  146
+    {
  147
+      data: [{x: 0, y: 0 }],
  148
+      color: "#c05020",
  149
+      name: this.title
  150
+    }  ];
  151
+
  152
+        var chart = new Rickshaw.Graph ({
  153
+            element: document.getElementById(this.container),
  154
+            renderer: 'line',
  155
+            series: series,
187 156
         });
188  
-     return chart;
  157
+
  158
+
  159
+var legend = new Rickshaw.Graph.Legend( {
  160
+  graph: chart,
  161
+  element: document.getElementById('legend-chart2')
  162
+
  163
+} );
  164
+
  165
+var hoverDetail = new Rickshaw.Graph.HoverDetail( {
  166
+  graph: chart,
  167
+  formatter: function(series, x, y) {
  168
+    var date = series.data[x]['date'];
  169
+    var date = '<span class="date">' + date.toUTCString() + '</span>';
  170
+    var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';
  171
+    var content = swatch + parseInt(y) + '<br>' + date;
  172
+    return content;
189 173
   }
  174
+} );
  175
+
  176
+       var y_ticks = new Rickshaw.Graph.Axis.Y( {
  177
+          graph: chart,
  178
+          orientation: 'left',
  179
+          tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
  180
+          element: document.getElementById('y_axis-chart2' ),
  181
+        } );
  182
+
  183
+     // DOES NOT WORK XXXX
  184
+     container =  $('#' + this.container);
  185
+
  186
+     $(window).resize(function() {
  187
+        var svg = container.find('svg')[0];
  188
+        console.log(svg);
  189
+        svg.setAttribute('width', '100%');
  190
+        svg.setAttribute('height', '100%');
  191
+        svg.setAttribute('viewBox', '0 0 ' + container.width() + ' ' + 
  192
+                                     container.height());
  193
+        chart.update();
  194
+   });
  195
+       return chart;
  196
+     }
190 197
   }
191 198
 );
192 199
 
@@ -230,7 +237,7 @@ MonolithBase.extend("MonolithSeries",
230 237
             var delta = end_date.getTime() - start_date.getTime();
231 238
             var one_day = 1000 * 60 * 60 * 24;
232 239
             delta = Math.round(delta / one_day);
233  
-            this.chart.showLoading();
  240
+            //this.chart.showLoading();
234 241
             var i, x, y;
235 242
             var query = {"query": {"field": {"add_on": app_id}},
236 243
                          "filter": {"range": {"date": {"gte": start_date_str, "lte": end_date_str}}},
@@ -239,7 +246,7 @@ MonolithBase.extend("MonolithSeries",
239 246
 
240 247
             query = JSON.stringify(query);
241 248
             this._async(query);
242  
-            this.chart.hideLoading();
  249
+            //this.chart.hideLoading();
243 250
         },
244 251
 
245 252
         _async_receive: function(json, chart, fields) {
@@ -265,7 +272,7 @@ MonolithBase.extend("MonolithSeries",
265 272
                       series[i].setData(dataSeries[i]);
266 273
                     }
267 274
 
268  
-                    chart.redraw();
  275
+                    chart.render();
269 276
                 }
270 277
     }
271 278
 )
@@ -297,7 +304,7 @@ MonolithBase.extend("MonolithAggregate",
297 304
             var delta = end_date.getTime() - start_date.getTime();
298 305
             var one_day = 1000 * 60 * 60 * 24;
299 306
             delta = Math.round(delta / one_day);
300  
-            this.chart.showLoading();
  307
+            //this.chart.showLoading();
301 308
             var i, x, y;
302 309
             //var match = {"field": {"add_on": app_id}};
303 310
             var match = {'match_all': {}};
@@ -305,11 +312,11 @@ MonolithBase.extend("MonolithAggregate",
305 312
             var query = {"query": match,
306 313
                 "facets": {
307 314
                    "facet_histo" : {"date_histogram" : {
308  
-                      		            "key_field" : "date",
309  
-                                	    "value_field": this.field,
310  
-	                                    "interval": this.interval},
  315
+                                    "key_field" : "date",
  316
+                                    "value_field": this.field,
  317
+                                    "interval": this.interval},
311 318
                                     "facet_filter": {
312  
-  				             "range": 
  319
+                                        "range":
313 320
                                                      {"date": {"gte": start_date_str, 
314 321
                                                        "lte": end_date_str}
315 322
                                                      }
@@ -321,21 +328,25 @@ MonolithBase.extend("MonolithAggregate",
321 328
             };
322 329
             query = JSON.stringify(query);
323 330
             this._async(query);
324  
-            this.chart.hideLoading();
  331
+            //this.chart.hideLoading();
325 332
         },
326 333
 
327 334
         _async_receive: function(json, chart, fields) {
328 335
            var name;
329 336
            var data = [];
330 337
            var series = chart.series;
  338
+           var x = 0;
331 339
 
332 340
            // XXX display the day, week or month in the label...
333 341
            $.each(json.facets.facet_histo.entries, function(i, item) {
334  
-             data.push({x: new Date(item.time),
335  
-                        y: item.total});
  342
+
  343
+             //new Date(item.time)
  344
+             var line = {x: x, y: item.total, date: new Date(item.time)};
  345
+             data.push(line);
  346
+             x += 1;
336 347
              });
337  
-            series[0].setData(data) ;
338  
-            chart.redraw();
  348
+            series[0].data = data;
  349
+            chart.render();
339 350
         },
340 351
     }
341 352
 )
2,843  monolith/media/lib/rickshaw.js
... ...
@@ -0,0 +1,2843 @@
  1
+var Rickshaw = {
  2
+
  3
+	namespace: function(namespace, obj) {
  4
+
  5
+		var parts = namespace.split('.');
  6
+
  7
+		var parent = Rickshaw;
  8
+
  9
+		for(var i = 1, length = parts.length; i < length; i++) {
  10
+			var currentPart = parts[i];
  11
+			parent[currentPart] = parent[currentPart] || {};
  12
+			parent = parent[currentPart];
  13
+		}
  14
+		return parent;
  15
+	},
  16
+
  17
+	keys: function(obj) {
  18
+		var keys = [];
  19
+		for (var key in obj) keys.push(key);
  20
+		return keys;
  21
+	},
  22
+
  23
+	extend: function(destination, source) {
  24
+
  25
+		for (var property in source) {
  26
+			destination[property] = source[property];
  27
+		}
  28
+		return destination;
  29
+	}
  30
+};
  31
+
  32
+if (typeof module !== 'undefined' && module.exports) {
  33
+	var d3 = require('d3');
  34
+	module.exports = Rickshaw;
  35
+}
  36
+
  37
+/* Adapted from https://github.com/Jakobo/PTClass */
  38
+
  39
+/*
  40
+Copyright (c) 2005-2010 Sam Stephenson
  41
+
  42
+Permission is hereby granted, free of charge, to any person obtaining a copy
  43
+of this software and associated documentation files (the "Software"), to deal
  44
+in the Software without restriction, including without limitation the rights
  45
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  46
+copies of the Software, and to permit persons to whom the Software is
  47
+furnished to do so, subject to the following conditions:
  48
+
  49
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  50
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  51
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  52
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  53
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  54
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  55
+SOFTWARE.
  56
+*/
  57
+/* Based on Alex Arnell's inheritance implementation. */
  58
+/** section: Language
  59
+ * class Class
  60
+ *
  61
+ *  Manages Prototype's class-based OOP system.
  62
+ *
  63
+ *  Refer to Prototype's web site for a [tutorial on classes and
  64
+ *  inheritance](http://prototypejs.org/learn/class-inheritance).
  65
+**/
  66
+(function(globalContext) {
  67
+/* ------------------------------------ */
  68
+/* Import from object.js                */
  69
+/* ------------------------------------ */
  70
+var _toString = Object.prototype.toString,
  71
+    NULL_TYPE = 'Null',
  72
+    UNDEFINED_TYPE = 'Undefined',
  73
+    BOOLEAN_TYPE = 'Boolean',
  74
+    NUMBER_TYPE = 'Number',
  75
+    STRING_TYPE = 'String',
  76
+    OBJECT_TYPE = 'Object',
  77
+    FUNCTION_CLASS = '[object Function]';
  78
+function isFunction(object) {
  79
+  return _toString.call(object) === FUNCTION_CLASS;
  80
+}
  81
+function extend(destination, source) {
  82
+  for (var property in source) if (source.hasOwnProperty(property)) // modify protect primitive slaughter
  83
+    destination[property] = source[property];
  84
+  return destination;
  85
+}
  86
+function keys(object) {
  87
+  if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
  88
+  var results = [];
  89
+  for (var property in object) {
  90
+    if (object.hasOwnProperty(property)) {
  91
+      results.push(property);
  92
+    }
  93
+  }
  94
+  return results;
  95
+}
  96
+function Type(o) {
  97
+  switch(o) {
  98
+    case null: return NULL_TYPE;
  99
+    case (void 0): return UNDEFINED_TYPE;
  100
+  }
  101
+  var type = typeof o;
  102
+  switch(type) {
  103
+    case 'boolean': return BOOLEAN_TYPE;
  104
+    case 'number':  return NUMBER_TYPE;
  105
+    case 'string':  return STRING_TYPE;
  106
+  }
  107
+  return OBJECT_TYPE;
  108
+}
  109
+function isUndefined(object) {
  110
+  return typeof object === "undefined";
  111
+}
  112
+/* ------------------------------------ */
  113
+/* Import from Function.js              */
  114
+/* ------------------------------------ */
  115
+var slice = Array.prototype.slice;
  116
+function argumentNames(fn) {
  117
+  var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
  118
+    .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
  119
+    .replace(/\s+/g, '').split(',');
  120
+  return names.length == 1 && !names[0] ? [] : names;
  121
+}
  122
+function wrap(fn, wrapper) {
  123
+  var __method = fn;
  124
+  return function() {
  125
+    var a = update([bind(__method, this)], arguments);
  126
+    return wrapper.apply(this, a);
  127
+  }
  128
+}
  129
+function update(array, args) {
  130
+  var arrayLength = array.length, length = args.length;
  131
+  while (length--) array[arrayLength + length] = args[length];
  132
+  return array;
  133
+}
  134
+function merge(array, args) {
  135
+  array = slice.call(array, 0);
  136
+  return update(array, args);
  137
+}
  138
+function bind(fn, context) {
  139
+  if (arguments.length < 2 && isUndefined(arguments[0])) return this;
  140
+  var __method = fn, args = slice.call(arguments, 2);
  141
+  return function() {
  142
+    var a = merge(args, arguments);
  143
+    return __method.apply(context, a);
  144
+  }
  145
+}
  146
+
  147
+/* ------------------------------------ */
  148
+/* Import from Prototype.js             */
  149
+/* ------------------------------------ */
  150
+var emptyFunction = function(){};
  151
+
  152
+var Class = (function() {
  153
+  
  154
+  // Some versions of JScript fail to enumerate over properties, names of which 
  155
+  // correspond to non-enumerable properties in the prototype chain
  156
+  var IS_DONTENUM_BUGGY = (function(){
  157
+    for (var p in { toString: 1 }) {
  158
+      // check actual property name, so that it works with augmented Object.prototype
  159
+      if (p === 'toString') return false;
  160
+    }
  161
+    return true;
  162
+  })();
  163
+  
  164
+  function subclass() {};
  165
+  function create() {
  166
+    var parent = null, properties = [].slice.apply(arguments);
  167
+    if (isFunction(properties[0]))
  168
+      parent = properties.shift();
  169
+
  170
+    function klass() {
  171
+      this.initialize.apply(this, arguments);
  172
+    }
  173
+
  174
+    extend(klass, Class.Methods);
  175
+    klass.superclass = parent;
  176
+    klass.subclasses = [];
  177
+
  178
+    if (parent) {
  179
+      subclass.prototype = parent.prototype;
  180
+      klass.prototype = new subclass;
  181
+      try { parent.subclasses.push(klass) } catch(e) {}
  182
+    }
  183
+
  184
+    for (var i = 0, length = properties.length; i < length; i++)
  185
+      klass.addMethods(properties[i]);
  186
+
  187
+    if (!klass.prototype.initialize)
  188
+      klass.prototype.initialize = emptyFunction;
  189
+
  190
+    klass.prototype.constructor = klass;
  191
+    return klass;
  192
+  }
  193
+
  194
+  function addMethods(source) {
  195
+    var ancestor   = this.superclass && this.superclass.prototype,
  196
+        properties = keys(source);
  197
+
  198
+    // IE6 doesn't enumerate `toString` and `valueOf` (among other built-in `Object.prototype`) properties,
  199
+    // Force copy if they're not Object.prototype ones.
  200
+    // Do not copy other Object.prototype.* for performance reasons
  201
+    if (IS_DONTENUM_BUGGY) {
  202
+      if (source.toString != Object.prototype.toString)
  203
+        properties.push("toString");
  204
+      if (source.valueOf != Object.prototype.valueOf)
  205
+        properties.push("valueOf");
  206
+    }
  207
+
  208
+    for (var i = 0, length = properties.length; i < length; i++) {
  209
+      var property = properties[i], value = source[property];
  210
+      if (ancestor && isFunction(value) &&
  211
+          argumentNames(value)[0] == "$super") {
  212
+        var method = value;
  213
+        value = wrap((function(m) {
  214
+          return function() { return ancestor[m].apply(this, arguments); };
  215
+        })(property), method);
  216
+
  217
+        value.valueOf = bind(method.valueOf, method);
  218
+        value.toString = bind(method.toString, method);
  219
+      }
  220
+      this.prototype[property] = value;
  221
+    }
  222
+
  223
+    return this;
  224
+  }
  225
+
  226
+  return {
  227
+    create: create,
  228
+    Methods: {
  229
+      addMethods: addMethods
  230
+    }
  231
+  };
  232
+})();
  233
+
  234
+if (globalContext.exports) {
  235
+  globalContext.exports.Class = Class;
  236
+}
  237
+else {
  238
+  globalContext.Class = Class;
  239
+}
  240
+})(Rickshaw);
  241
+Rickshaw.namespace('Rickshaw.Compat.ClassList');
  242
+
  243
+Rickshaw.Compat.ClassList = function() {
  244
+
  245
+	/* adapted from http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
  246
+
  247
+	if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
  248
+
  249
+	(function (view) {
  250
+
  251
+	"use strict";
  252
+
  253
+	var
  254
+		  classListProp = "classList"
  255
+		, protoProp = "prototype"
  256
+		, elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
  257
+		, objCtr = Object
  258
+		, strTrim = String[protoProp].trim || function () {
  259
+			return this.replace(/^\s+|\s+$/g, "");
  260
+		}
  261
+		, arrIndexOf = Array[protoProp].indexOf || function (item) {
  262
+			var
  263
+				  i = 0
  264
+				, len = this.length
  265
+			;
  266
+			for (; i < len; i++) {
  267
+				if (i in this && this[i] === item) {
  268
+					return i;
  269
+				}
  270
+			}
  271
+			return -1;
  272
+		}
  273
+		// Vendors: please allow content code to instantiate DOMExceptions
  274
+		, DOMEx = function (type, message) {
  275
+			this.name = type;
  276
+			this.code = DOMException[type];
  277
+			this.message = message;
  278
+		}
  279
+		, checkTokenAndGetIndex = function (classList, token) {
  280
+			if (token === "") {
  281
+				throw new DOMEx(
  282
+					  "SYNTAX_ERR"
  283
+					, "An invalid or illegal string was specified"
  284
+				);
  285
+			}
  286
+			if (/\s/.test(token)) {
  287
+				throw new DOMEx(
  288
+					  "INVALID_CHARACTER_ERR"
  289
+					, "String contains an invalid character"
  290
+				);
  291
+			}
  292
+			return arrIndexOf.call(classList, token);
  293
+		}
  294
+		, ClassList = function (elem) {
  295
+			var
  296
+				  trimmedClasses = strTrim.call(elem.className)
  297
+				, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
  298
+				, i = 0
  299
+				, len = classes.length
  300
+			;
  301
+			for (; i < len; i++) {
  302
+				this.push(classes[i]);
  303
+			}
  304
+			this._updateClassName = function () {
  305
+				elem.className = this.toString();
  306
+			};
  307
+		}
  308
+		, classListProto = ClassList[protoProp] = []
  309
+		, classListGetter = function () {
  310
+			return new ClassList(this);
  311
+		}
  312
+	;
  313
+	// Most DOMException implementations don't allow calling DOMException's toString()
  314
+	// on non-DOMExceptions. Error's toString() is sufficient here.
  315
+	DOMEx[protoProp] = Error[protoProp];
  316
+	classListProto.item = function (i) {
  317
+		return this[i] || null;
  318
+	};
  319
+	classListProto.contains = function (token) {
  320
+		token += "";
  321
+		return checkTokenAndGetIndex(this, token) !== -1;
  322
+	};
  323
+	classListProto.add = function (token) {
  324
+		token += "";
  325
+		if (checkTokenAndGetIndex(this, token) === -1) {
  326
+			this.push(token);
  327
+			this._updateClassName();
  328
+		}
  329
+	};
  330
+	classListProto.remove = function (token) {
  331
+		token += "";
  332
+		var index = checkTokenAndGetIndex(this, token);
  333
+		if (index !== -1) {
  334
+			this.splice(index, 1);
  335
+			this._updateClassName();
  336
+		}
  337
+	};
  338
+	classListProto.toggle = function (token) {
  339
+		token += "";
  340
+		if (checkTokenAndGetIndex(this, token) === -1) {
  341
+			this.add(token);
  342
+		} else {
  343
+			this.remove(token);
  344
+		}
  345
+	};
  346
+	classListProto.toString = function () {
  347
+		return this.join(" ");
  348
+	};
  349
+
  350
+	if (objCtr.defineProperty) {
  351
+		var classListPropDesc = {
  352
+			  get: classListGetter
  353
+			, enumerable: true
  354
+			, configurable: true
  355
+		};
  356
+		try {
  357
+			objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
  358
+		} catch (ex) { // IE 8 doesn't support enumerable:true
  359
+			if (ex.number === -0x7FF5EC54) {
  360
+				classListPropDesc.enumerable = false;
  361
+				objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
  362
+			}
  363
+		}
  364
+	} else if (objCtr[protoProp].__defineGetter__) {
  365
+		elemCtrProto.__defineGetter__(classListProp, classListGetter);
  366
+	}
  367
+
  368
+	}(window));
  369
+
  370
+	}
  371
+};
  372
+
  373
+if ( (typeof RICKSHAW_NO_COMPAT !== "undefined" && !RICKSHAW_NO_COMPAT) || typeof RICKSHAW_NO_COMPAT === "undefined") {
  374
+	new Rickshaw.Compat.ClassList();
  375
+}
  376
+Rickshaw.namespace('Rickshaw.Graph');
  377
+
  378
+Rickshaw.Graph = function(args) {
  379
+
  380
+	if (!args.element) throw "Rickshaw.Graph needs a reference to an element";
  381
+
  382
+	this.element = args.element;
  383
+	this.series = args.series;
  384
+
  385
+	this.defaults = {
  386
+		interpolation: 'cardinal',
  387
+		offset: 'zero',
  388
+		min: undefined,
  389
+		max: undefined
  390
+	};
  391
+
  392
+	Rickshaw.keys(this.defaults).forEach( function(k) {
  393
+		this[k] = args[k] || this.defaults[k];
  394
+	}, this );
  395
+
  396
+	this.window = {};
  397
+
  398
+	this.updateCallbacks = [];
  399
+
  400
+	var self = this;
  401
+
  402
+	this.initialize = function(args) {
  403
+
  404
+		this.validateSeries(args.series);
  405
+
  406
+		this.series.active = function() { return self.series.filter( function(s) { return !s.disabled } ) };
  407
+
  408
+		this.setSize({ width: args.width, height: args.height });
  409
+
  410
+		this.element.classList.add('rickshaw_graph');
  411
+		this.vis = d3.select(this.element)
  412
+			.append("svg:svg")
  413
+			.attr('width', this.width)
  414
+			.attr('height', this.height);
  415
+
  416
+		var renderers = [
  417
+			Rickshaw.Graph.Renderer.Stack,
  418
+			Rickshaw.Graph.Renderer.Line,
  419
+			Rickshaw.Graph.Renderer.Bar,
  420
+			Rickshaw.Graph.Renderer.Area,
  421
+			Rickshaw.Graph.Renderer.ScatterPlot
  422
+		];
  423
+
  424
+		renderers.forEach( function(r) {
  425
+			if (!r) return;
  426
+			self.registerRenderer(new r( { graph: self } ));
  427
+		} );
  428
+
  429
+		this.setRenderer(args.renderer || 'stack', args);
  430
+		this.discoverRange();
  431
+	};
  432
+
  433
+	this.validateSeries = function(series) {
  434
+
  435
+		if (!(series instanceof Array) && !(series instanceof Rickshaw.Series)) {
  436
+			var seriesSignature = Object.prototype.toString.apply(series);
  437
+			throw "series is not an array: " + seriesSignature;
  438
+		}
  439
+
  440
+		var pointsCount;
  441
+
  442
+		series.forEach( function(s) {
  443
+
  444
+			if (!(s instanceof Object)) {
  445
+				throw "series element is not an object: " + s;
  446
+			}
  447
+			if (!(s.data)) {
  448
+				throw "series has no data: " + JSON.stringify(s);
  449
+			}
  450
+			if (!(s.data instanceof Array)) {
  451
+				throw "series data is not an array: " + JSON.stringify(s.data);
  452
+			}
  453
+
  454
+			var x = s.data[0].x;
  455
+			var y = s.data[0].y;
  456
+
  457
+			if (typeof x != 'number' || ( typeof y != 'number' && y !== null ) ) {
  458
+				throw "x and y properties of points should be numbers instead of " +
  459
+					(typeof x) + " and " + (typeof y)
  460
+			}
  461
+
  462
+		}, this );
  463
+	};
  464
+
  465
+	this.dataDomain = function() {
  466
+
  467
+		// take from the first series
  468
+		var data = this.series[0].data;
  469
+
  470
+		return [ data[0].x, data.slice(-1).shift().x ];
  471
+
  472
+	};
  473
+
  474
+	this.discoverRange = function() {
  475
+
  476
+		var domain = this.renderer.domain();
  477
+
  478
+		this.x = d3.scale.linear().domain(domain.x).range([0, this.width]);
  479
+
  480
+		this.y = d3.scale.linear().domain(domain.y).range([this.height, 0]);
  481
+
  482
+		this.y.magnitude = d3.scale.linear()
  483
+			.domain([domain.y[0] - domain.y[0], domain.y[1] - domain.y[0]])
  484
+			.range([0, this.height]);
  485
+	};
  486
+
  487
+	this.render = function() {
  488
+
  489
+		var stackedData = this.stackData();
  490
+		this.discoverRange();
  491
+
  492
+		this.renderer.render();
  493
+
  494
+		this.updateCallbacks.forEach( function(callback) {
  495
+			callback();
  496
+		} );
  497
+	};
  498
+
  499
+	this.update = this.render;
  500
+
  501
+	this.stackData = function() {
  502
+
  503
+		var data = this.series.active()
  504
+			.map( function(d) { return d.data } )
  505
+			.map( function(d) { return d.filter( function(d) { return this._slice(d) }, this ) }, this);
  506
+
  507
+		this.stackData.hooks.data.forEach( function(entry) {
  508
+			data = entry.f.apply(self, [data]);