killbill-memoizeit

Rewrite javascript dashboard so: - Use killbill namespace -

4/22/2013 10:14:28 PM

Details

diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/http/AnalyticsServlet.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/http/AnalyticsServlet.java
index 1dd0b1a..7f72951 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/http/AnalyticsServlet.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/http/AnalyticsServlet.java
@@ -18,9 +18,7 @@ package com.ning.billing.osgi.bundles.analytics.http;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.net.URL;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
@@ -39,7 +37,6 @@ import org.osgi.service.log.LogService;
 import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
 import com.ning.billing.osgi.bundles.analytics.api.BusinessSnapshot;
 import com.ning.billing.osgi.bundles.analytics.api.user.AnalyticsUserApi;
-import com.ning.billing.osgi.bundles.analytics.json.NamedTimeSeries;
 import com.ning.billing.osgi.bundles.analytics.json.NamedXYTimeSeries;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
@@ -47,12 +44,11 @@ import com.ning.billing.util.callcontext.UserType;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.base.Objects;
-import com.google.common.io.CharStreams;
 import com.google.common.io.Resources;
 
 public class AnalyticsServlet extends HttpServlet {
 
-    public static DateTimeFormatter DATE_FORMAT =  DateTimeFormat.forPattern("yyyy-MM-dd");
+    public static DateTimeFormatter DATE_FORMAT = DateTimeFormat.forPattern("yyyy-MM-dd");
 
 
     private static final String QUERY_TENANT_ID = "tenantId";
@@ -60,10 +56,6 @@ public class AnalyticsServlet extends HttpServlet {
     private static final String HDR_REASON = "X-Killbill-Reason";
     private static final String HDR_COMMENT = "X-Killbill-Comment";
 
-    private final static String ANALYTICS_DASHBOARD_HTML = "dashboard.html";
-    private final static String JQUERY_JS = "jquery-1.9.0.min.js";
-    private final static String D3_JS = "d3.js";
-    private final static String KILLBILL_JS = "killbill.js";
 
     private final static String STATIC_RESOURCES = "static";
 
@@ -103,7 +95,7 @@ public class AnalyticsServlet extends HttpServlet {
         }
     }
 
-// Access-Control-Allow-Origin: *
+    // Access-Control-Allow-Origin: *
     @Override
     protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
 
@@ -176,26 +168,23 @@ public class AnalyticsServlet extends HttpServlet {
         final String products = req.getParameter(QUERY_PRODUCTS);
 
         logService.log(LogService.LOG_INFO, "PlanTransitionsOverTime " +
-                                            "startDate = " +  Objects.firstNonNull(startDate, "") +
-                                            ", endDate = " +  Objects.firstNonNull(endDate, "") +
-                                            ", products = " +  Objects.firstNonNull(products, ""));
+                                            "startDate = " + Objects.firstNonNull(startDate, "") +
+                                            ", endDate = " + Objects.firstNonNull(endDate, "") +
+                                            ", products = " + Objects.firstNonNull(products, ""));
 
         // TODO STEPH until we plug the DAO to get real data, fake it...
-        final String linesDataDJson  = "[{\"name\": \"new\"," +
-                                       "\"dates\": [\"2013-01-01\", \"2013-01-02\", \"2013-01-03\", \"2013-01-04\", \"2013-01-05\", \"2013-01-06\", \"2013-01-07\", \"2013-01-08\", \"2013-01-09\", \"2013-01-10\", \"2013-01-11\", \"2013-01-12\", \"2013-01-13\", \"2013-01-14\", \"2013-01-15\", \"2013-01-16\", \"2013-01-17\", \"2013-01-18\", \"2013-01-19\", \"2013-01-20\", \"2013-01-21\", \"2013-01-22\", \"2013-01-23\", \"2013-01-24\", \"2013-01-25\", \"2013-01-26\", \"2013-01-27\", \"2013-01-28\", \"2013-01-29\", \"2013-01-30\", \"2013-01-31\", \"2013-02-01\", \"2013-02-02\", \"2013-02-03\", \"2013-02-04\", \"2013-02-05\", \"2013-02-06\", \"2013-02-07\", \"2013-02-08\", \"2013-02-09\", \"2013-02-10\", \"2013-02-11\", \"2013-02-12\", \"2013-02-13\", \"2013-02-14\", \"2013-02-15\", \"2013-02-16\", \"2013-02-17\", \"2013-02-18\", \"2013-02-19\", \"2013-02-20\", \"2013-02-21\", \"2013-02-22\", \"2013-02-23\", \"2013-02-24\", \"2013-02-25\", \"2013-02-26\", \"2013-02-27\", \"2013-02-28\", \"2013-03-01\", \"2013-03-02\", \"2013-03-03\", \"2013-03-04\", \"2013-03-05\", \"2013-03-06\", \"2013-03-07\", \"2013-03-08\", \"2013-03-09\", \"2013-03-10\", \"2013-03-11\"]," +
-                                       "\"values\": [8, 9, 9, 7, 7, 5, 9, 11, 5, 6, 5, 6, 11, 10, 6, 7, 6, 9, 11, 11, 9, 9, 9, 11, 11, 9, 7, 6, 7, 7, 11, 5, 10, 10, 7, 9, 11, 5, 7, 9, 6, 11, 9, 5, 9, 8, 7, 8, 8, 6, 7, 6, 10, 5, 9, 8, 10, 10, 5, 7, 11, 5, 11, 6, 11, 6, 6, 7, 11, 10]}," +
-                                       "{\"name\": \"changed\"," +
-                                       "\"dates\": [\"2013-01-01\", \"2013-01-02\", \"2013-01-03\", \"2013-01-04\", \"2013-01-05\", \"2013-01-06\", \"2013-01-07\", \"2013-01-08\", \"2013-01-09\", \"2013-01-10\", \"2013-01-11\", \"2013-01-12\", \"2013-01-13\", \"2013-01-14\", \"2013-01-15\", \"2013-01-16\", \"2013-01-17\", \"2013-01-18\", \"2013-01-19\", \"2013-01-20\", \"2013-01-21\", \"2013-01-22\", \"2013-01-23\", \"2013-01-24\", \"2013-01-25\", \"2013-01-26\", \"2013-01-27\", \"2013-01-28\", \"2013-01-29\", \"2013-01-30\", \"2013-01-31\", \"2013-02-01\", \"2013-02-02\", \"2013-02-03\", \"2013-02-04\", \"2013-02-05\", \"2013-02-06\", \"2013-02-07\", \"2013-02-08\", \"2013-02-09\", \"2013-02-10\", \"2013-02-11\", \"2013-02-12\", \"2013-02-13\", \"2013-02-14\", \"2013-02-15\", \"2013-02-16\", \"2013-02-17\", \"2013-02-18\", \"2013-02-19\", \"2013-02-20\", \"2013-02-21\", \"2013-02-22\", \"2013-02-23\", \"2013-02-24\", \"2013-02-25\", \"2013-02-26\", \"2013-02-27\", \"2013-02-28\", \"2013-03-01\", \"2013-03-02\", \"2013-03-03\", \"2013-03-04\", \"2013-03-05\", \"2013-03-06\", \"2013-03-07\", \"2013-03-08\", \"2013-03-09\", \"2013-03-10\", \"2013-03-11\"]," +
-                                       "\"values\": [7, 7, 7, 6, 7, 5, 6, 5, 7, 5, 6, 5, 5, 6, 6, 6, 7, 6, 7, 6, 6, 5, 5, 5, 6, 6, 5, 5, 6, 5, 6, 6, 7, 6, 7, 5, 5, 7, 7, 7, 6, 5, 5, 7, 5, 5, 7, 6, 5, 6, 5, 7, 6, 6, 5, 7, 6, 6, 6, 5, 6, 7, 6, 5, 7, 5, 6, 5, 5, 5]}," +
-                                       "{\"name\": \"cancelled\"," +
-                                       "\"dates\": [\"2013-01-01\", \"2013-01-02\", \"2013-01-03\", \"2013-01-04\", \"2013-01-05\", \"2013-01-06\", \"2013-01-07\", \"2013-01-08\", \"2013-01-09\", \"2013-01-10\", \"2013-01-11\", \"2013-01-12\", \"2013-01-13\", \"2013-01-14\", \"2013-01-15\", \"2013-01-16\", \"2013-01-17\", \"2013-01-18\", \"2013-01-19\", \"2013-01-20\", \"2013-01-21\", \"2013-01-22\", \"2013-01-23\", \"2013-01-24\", \"2013-01-25\", \"2013-01-26\", \"2013-01-27\", \"2013-01-28\", \"2013-01-29\", \"2013-01-30\", \"2013-01-31\", \"2013-02-01\", \"2013-02-02\", \"2013-02-03\", \"2013-02-04\", \"2013-02-05\", \"2013-02-06\", \"2013-02-07\", \"2013-02-08\", \"2013-02-09\", \"2013-02-10\", \"2013-02-11\", \"2013-02-12\", \"2013-02-13\", \"2013-02-14\", \"2013-02-15\", \"2013-02-16\", \"2013-02-17\", \"2013-02-18\", \"2013-02-19\", \"2013-02-20\", \"2013-02-21\", \"2013-02-22\", \"2013-02-23\", \"2013-02-24\", \"2013-02-25\", \"2013-02-26\", \"2013-02-27\", \"2013-02-28\", \"2013-03-01\", \"2013-03-02\", \"2013-03-03\", \"2013-03-04\", \"2013-03-05\", \"2013-03-06\", \"2013-03-07\", \"2013-03-08\", \"2013-03-09\", \"2013-03-10\", \"2013-03-11\"]," +
-                                       "\"values\": [7, 6, 8, 6, 6, 6, 5, 8, 5, 5, 4, 3, 6, 4, 6, 6, 3, 8, 6, 8, 7, 4, 5, 4, 6, 6, 5, 7, 5, 8, 6, 3, 6, 4, 4, 4, 4, 3, 4, 3, 7, 5, 4, 5, 4, 4, 3, 3, 4, 6, 5, 7, 7, 7, 3, 7, 7, 6, 3, 8, 7, 7, 6, 7, 4, 7, 6, 4, 3, 5]}," +
-                                       "{\"name\": \"next Phase\"," +
-                                       "\"dates\": [\"2013-01-01\", \"2013-01-02\", \"2013-01-03\", \"2013-01-04\", \"2013-01-05\", \"2013-01-06\", \"2013-01-07\", \"2013-01-08\", \"2013-01-09\", \"2013-01-10\", \"2013-01-11\", \"2013-01-12\", \"2013-01-13\", \"2013-01-14\", \"2013-01-15\", \"2013-01-16\", \"2013-01-17\", \"2013-01-18\", \"2013-01-19\", \"2013-01-20\", \"2013-01-21\", \"2013-01-22\", \"2013-01-23\", \"2013-01-24\", \"2013-01-25\", \"2013-01-26\", \"2013-01-27\", \"2013-01-28\", \"2013-01-29\", \"2013-01-30\", \"2013-01-31\", \"2013-02-01\", \"2013-02-02\", \"2013-02-03\", \"2013-02-04\", \"2013-02-05\", \"2013-02-06\", \"2013-02-07\", \"2013-02-08\", \"2013-02-09\", \"2013-02-10\", \"2013-02-11\", \"2013-02-12\", \"2013-02-13\", \"2013-02-14\", \"2013-02-15\", \"2013-02-16\", \"2013-02-17\", \"2013-02-18\", \"2013-02-19\", \"2013-02-20\", \"2013-02-21\", \"2013-02-22\", \"2013-02-23\", \"2013-02-24\", \"2013-02-25\", \"2013-02-26\", \"2013-02-27\", \"2013-02-28\", \"2013-03-01\", \"2013-03-02\", \"2013-03-03\", \"2013-03-04\", \"2013-03-05\", \"2013-03-06\", \"2013-03-07\", \"2013-03-08\", \"2013-03-09\", \"2013-03-10\", \"2013-03-11\"]," +
-                                       "\"values\": [-4, -4, -5, -5, -5, -3, -4, -3, -3, -4, -5, -3, -3, -4, -3, -4, -4, -5, -4, -5, -4, -4, -4, -4, -3, -5, -3, -3, -5, -5, -3, -3, -3, -4, -4, -5, -5, -3, -4, -4, -4, -4, -5, -4, -5, -4, -4, -3, -5, -4, -3, -5, -4, -3, -4, -5, -5, -3, -3, -3, -3, -5, -4, -5, -3, -4, -4, -3, -3, -5]}" +
-                                       "]";
-
-        List<NamedTimeSeries> result = mapper.readValue(linesDataDJson, List.class);
+        final String linesDataJson = "[" +
+                                     "{\"name\":\"new\"," +
+                                     "\"values\":[{\"x\":\"2013-01-01\", \"y\":6}, {\"x\":\"2013-01-02\", \"y\":5}, {\"x\":\"2013-01-03\", \"y\":10}, {\"x\":\"2013-01-04\", \"y\":5}, {\"x\":\"2013-01-05\", \"y\":7}, {\"x\":\"2013-01-06\", \"y\":11}, {\"x\":\"2013-01-07\", \"y\":9}, {\"x\":\"2013-01-08\", \"y\":5}, {\"x\":\"2013-01-09\", \"y\":5}, {\"x\":\"2013-01-10\", \"y\":6}, {\"x\":\"2013-01-11\", \"y\":8}, {\"x\":\"2013-01-12\", \"y\":11}, {\"x\":\"2013-01-13\", \"y\":8}, {\"x\":\"2013-01-14\", \"y\":10}, {\"x\":\"2013-01-15\", \"y\":10}, {\"x\":\"2013-01-16\", \"y\":9}, {\"x\":\"2013-01-17\", \"y\":9}, {\"x\":\"2013-01-18\", \"y\":7}, {\"x\":\"2013-01-19\", \"y\":8}, {\"x\":\"2013-01-20\", \"y\":5}, {\"x\":\"2013-01-21\", \"y\":5}, {\"x\":\"2013-01-22\", \"y\":7}, {\"x\":\"2013-01-23\", \"y\":6}, {\"x\":\"2013-01-24\", \"y\":10}, {\"x\":\"2013-01-25\", \"y\":7}, {\"x\":\"2013-01-26\", \"y\":9}, {\"x\":\"2013-01-27\", \"y\":10}, {\"x\":\"2013-01-28\", \"y\":6}, {\"x\":\"2013-01-29\", \"y\":5}, {\"x\":\"2013-01-30\", \"y\":9}, {\"x\":\"2013-01-31\", \"y\":6}, {\"x\":\"2013-02-01\", \"y\":9}, {\"x\":\"2013-02-02\", \"y\":7}, {\"x\":\"2013-02-03\", \"y\":11}, {\"x\":\"2013-02-04\", \"y\":10}, {\"x\":\"2013-02-05\", \"y\":11}, {\"x\":\"2013-02-06\", \"y\":9}, {\"x\":\"2013-02-07\", \"y\":10}, {\"x\":\"2013-02-08\", \"y\":8}, {\"x\":\"2013-02-09\", \"y\":10}, {\"x\":\"2013-02-10\", \"y\":8}, {\"x\":\"2013-02-11\", \"y\":7}, {\"x\":\"2013-02-12\", \"y\":9}, {\"x\":\"2013-02-13\", \"y\":9}, {\"x\":\"2013-02-14\", \"y\":7}, {\"x\":\"2013-02-15\", \"y\":5}, {\"x\":\"2013-02-16\", \"y\":8}, {\"x\":\"2013-02-17\", \"y\":7}, {\"x\":\"2013-02-18\", \"y\":7}, {\"x\":\"2013-02-19\", \"y\":6}, {\"x\":\"2013-02-20\", \"y\":5}, {\"x\":\"2013-02-21\", \"y\":6}, {\"x\":\"2013-02-22\", \"y\":6}, {\"x\":\"2013-02-23\", \"y\":11}, {\"x\":\"2013-02-24\", \"y\":10}, {\"x\":\"2013-02-25\", \"y\":11}, {\"x\":\"2013-02-26\", \"y\":10}, {\"x\":\"2013-02-27\", \"y\":6}, {\"x\":\"2013-02-28\", \"y\":10}, {\"x\":\"2013-03-01\", \"y\":9}, {\"x\":\"2013-03-02\", \"y\":10}, {\"x\":\"2013-03-03\", \"y\":9}, {\"x\":\"2013-03-04\", \"y\":7}, {\"x\":\"2013-03-05\", \"y\":8}, {\"x\":\"2013-03-06\", \"y\":8}, {\"x\":\"2013-03-07\", \"y\":6}, {\"x\":\"2013-03-08\", \"y\":5}, {\"x\":\"2013-03-09\", \"y\":6}, {\"x\":\"2013-03-10\", \"y\":6}, {\"x\":\"2013-03-11\", \"y\":7}]}," +
+                                     "{\"name\":\"changed\"," +
+                                     "\"values\":[{\"x\":\"2013-01-01\", \"y\":5}, {\"x\":\"2013-01-02\", \"y\":6}, {\"x\":\"2013-01-03\", \"y\":7}, {\"x\":\"2013-01-04\", \"y\":6}, {\"x\":\"2013-01-05\", \"y\":6}, {\"x\":\"2013-01-06\", \"y\":6}, {\"x\":\"2013-01-07\", \"y\":7}, {\"x\":\"2013-01-08\", \"y\":6}, {\"x\":\"2013-01-09\", \"y\":6}, {\"x\":\"2013-01-10\", \"y\":5}, {\"x\":\"2013-01-11\", \"y\":6}, {\"x\":\"2013-01-12\", \"y\":5}, {\"x\":\"2013-01-13\", \"y\":5}, {\"x\":\"2013-01-14\", \"y\":5}, {\"x\":\"2013-01-15\", \"y\":5}, {\"x\":\"2013-01-16\", \"y\":6}, {\"x\":\"2013-01-17\", \"y\":5}, {\"x\":\"2013-01-18\", \"y\":5}, {\"x\":\"2013-01-19\", \"y\":5}, {\"x\":\"2013-01-20\", \"y\":5}, {\"x\":\"2013-01-21\", \"y\":5}, {\"x\":\"2013-01-22\", \"y\":5}, {\"x\":\"2013-01-23\", \"y\":7}, {\"x\":\"2013-01-24\", \"y\":7}, {\"x\":\"2013-01-25\", \"y\":6}, {\"x\":\"2013-01-26\", \"y\":6}, {\"x\":\"2013-01-27\", \"y\":7}, {\"x\":\"2013-01-28\", \"y\":7}, {\"x\":\"2013-01-29\", \"y\":7}, {\"x\":\"2013-01-30\", \"y\":7}, {\"x\":\"2013-01-31\", \"y\":5}, {\"x\":\"2013-02-01\", \"y\":5}, {\"x\":\"2013-02-02\", \"y\":7}, {\"x\":\"2013-02-03\", \"y\":6}, {\"x\":\"2013-02-04\", \"y\":6}, {\"x\":\"2013-02-05\", \"y\":6}, {\"x\":\"2013-02-06\", \"y\":6}, {\"x\":\"2013-02-07\", \"y\":6}, {\"x\":\"2013-02-08\", \"y\":5}, {\"x\":\"2013-02-09\", \"y\":5}, {\"x\":\"2013-02-10\", \"y\":6}, {\"x\":\"2013-02-11\", \"y\":6}, {\"x\":\"2013-02-12\", \"y\":5}, {\"x\":\"2013-02-13\", \"y\":7}, {\"x\":\"2013-02-14\", \"y\":7}, {\"x\":\"2013-02-15\", \"y\":7}, {\"x\":\"2013-02-16\", \"y\":7}, {\"x\":\"2013-02-17\", \"y\":7}, {\"x\":\"2013-02-18\", \"y\":7}, {\"x\":\"2013-02-19\", \"y\":6}, {\"x\":\"2013-02-20\", \"y\":7}, {\"x\":\"2013-02-21\", \"y\":5}, {\"x\":\"2013-02-22\", \"y\":7}, {\"x\":\"2013-02-23\", \"y\":5}, {\"x\":\"2013-02-24\", \"y\":7}, {\"x\":\"2013-02-25\", \"y\":5}, {\"x\":\"2013-02-26\", \"y\":5}, {\"x\":\"2013-02-27\", \"y\":7}, {\"x\":\"2013-02-28\", \"y\":7}, {\"x\":\"2013-03-01\", \"y\":5}, {\"x\":\"2013-03-02\", \"y\":5}, {\"x\":\"2013-03-03\", \"y\":6}, {\"x\":\"2013-03-04\", \"y\":6}, {\"x\":\"2013-03-05\", \"y\":5}, {\"x\":\"2013-03-06\", \"y\":5}, {\"x\":\"2013-03-07\", \"y\":6}, {\"x\":\"2013-03-08\", \"y\":7}, {\"x\":\"2013-03-09\", \"y\":5}, {\"x\":\"2013-03-10\", \"y\":6}, {\"x\":\"2013-03-11\", \"y\":7}]}," +
+                                     "{\"name\":\"cancelled\"," +
+                                     "\"values\":[{\"x\":\"2013-01-01\", \"y\":5}, {\"x\":\"2013-01-02\", \"y\":4}, {\"x\":\"2013-01-03\", \"y\":5}, {\"x\":\"2013-01-04\", \"y\":5}, {\"x\":\"2013-01-05\", \"y\":4}, {\"x\":\"2013-01-06\", \"y\":6}, {\"x\":\"2013-01-07\", \"y\":6}, {\"x\":\"2013-01-08\", \"y\":7}, {\"x\":\"2013-01-09\", \"y\":8}, {\"x\":\"2013-01-10\", \"y\":5}, {\"x\":\"2013-01-11\", \"y\":6}, {\"x\":\"2013-01-12\", \"y\":6}, {\"x\":\"2013-01-13\", \"y\":7}, {\"x\":\"2013-01-14\", \"y\":8}, {\"x\":\"2013-01-15\", \"y\":6}, {\"x\":\"2013-01-16\", \"y\":7}, {\"x\":\"2013-01-17\", \"y\":4}, {\"x\":\"2013-01-18\", \"y\":7}, {\"x\":\"2013-01-19\", \"y\":4}, {\"x\":\"2013-01-20\", \"y\":8}, {\"x\":\"2013-01-21\", \"y\":4}, {\"x\":\"2013-01-22\", \"y\":6}, {\"x\":\"2013-01-23\", \"y\":6}, {\"x\":\"2013-01-24\", \"y\":7}, {\"x\":\"2013-01-25\", \"y\":6}, {\"x\":\"2013-01-26\", \"y\":8}, {\"x\":\"2013-01-27\", \"y\":7}, {\"x\":\"2013-01-28\", \"y\":5}, {\"x\":\"2013-01-29\", \"y\":8}, {\"x\":\"2013-01-30\", \"y\":3}, {\"x\":\"2013-01-31\", \"y\":3}, {\"x\":\"2013-02-01\", \"y\":5}, {\"x\":\"2013-02-02\", \"y\":6}, {\"x\":\"2013-02-03\", \"y\":7}, {\"x\":\"2013-02-04\", \"y\":8}, {\"x\":\"2013-02-05\", \"y\":4}, {\"x\":\"2013-02-06\", \"y\":8}, {\"x\":\"2013-02-07\", \"y\":8}, {\"x\":\"2013-02-08\", \"y\":8}, {\"x\":\"2013-02-09\", \"y\":6}, {\"x\":\"2013-02-10\", \"y\":8}, {\"x\":\"2013-02-11\", \"y\":7}, {\"x\":\"2013-02-12\", \"y\":8}, {\"x\":\"2013-02-13\", \"y\":8}, {\"x\":\"2013-02-14\", \"y\":8}, {\"x\":\"2013-02-15\", \"y\":5}, {\"x\":\"2013-02-16\", \"y\":7}, {\"x\":\"2013-02-17\", \"y\":6}, {\"x\":\"2013-02-18\", \"y\":8}, {\"x\":\"2013-02-19\", \"y\":5}, {\"x\":\"2013-02-20\", \"y\":4}, {\"x\":\"2013-02-21\", \"y\":8}, {\"x\":\"2013-02-22\", \"y\":8}, {\"x\":\"2013-02-23\", \"y\":8}, {\"x\":\"2013-02-24\", \"y\":5}, {\"x\":\"2013-02-25\", \"y\":5}, {\"x\":\"2013-02-26\", \"y\":6}, {\"x\":\"2013-02-27\", \"y\":7}, {\"x\":\"2013-02-28\", \"y\":3}, {\"x\":\"2013-03-01\", \"y\":7}, {\"x\":\"2013-03-02\", \"y\":4}, {\"x\":\"2013-03-03\", \"y\":5}, {\"x\":\"2013-03-04\", \"y\":8}, {\"x\":\"2013-03-05\", \"y\":7}, {\"x\":\"2013-03-06\", \"y\":6}, {\"x\":\"2013-03-07\", \"y\":6}, {\"x\":\"2013-03-08\", \"y\":7}, {\"x\":\"2013-03-09\", \"y\":7}, {\"x\":\"2013-03-10\", \"y\":5}, {\"x\":\"2013-03-11\", \"y\":6}]}," +
+                                     "{\"name\":\"next phase\"," +
+                                     "\"values\":[{\"x\":\"2013-01-01\", \"y\":-1}, {\"x\":\"2013-01-02\", \"y\":6}, {\"x\":\"2013-01-03\", \"y\":6}, {\"x\":\"2013-01-04\", \"y\":1}, {\"x\":\"2013-01-05\", \"y\":13}, {\"x\":\"2013-01-06\", \"y\":2}, {\"x\":\"2013-01-07\", \"y\":14}, {\"x\":\"2013-01-08\", \"y\":12}, {\"x\":\"2013-01-09\", \"y\":7}, {\"x\":\"2013-01-10\", \"y\":2}, {\"x\":\"2013-01-11\", \"y\":-1}, {\"x\":\"2013-01-12\", \"y\":8}, {\"x\":\"2013-01-13\", \"y\":6}, {\"x\":\"2013-01-14\", \"y\":14}, {\"x\":\"2013-01-15\", \"y\":1}, {\"x\":\"2013-01-16\", \"y\":9}, {\"x\":\"2013-01-17\", \"y\":6}, {\"x\":\"2013-01-18\", \"y\":5}, {\"x\":\"2013-01-19\", \"y\":11}, {\"x\":\"2013-01-20\", \"y\":11}, {\"x\":\"2013-01-21\", \"y\":4}, {\"x\":\"2013-01-22\", \"y\":8}, {\"x\":\"2013-01-23\", \"y\":3}, {\"x\":\"2013-01-24\", \"y\":12}, {\"x\":\"2013-01-25\", \"y\":0}, {\"x\":\"2013-01-26\", \"y\":11}, {\"x\":\"2013-01-27\", \"y\":12}, {\"x\":\"2013-01-28\", \"y\":3}, {\"x\":\"2013-01-29\", \"y\":5}, {\"x\":\"2013-01-30\", \"y\":12}, {\"x\":\"2013-01-31\", \"y\":10}, {\"x\":\"2013-02-01\", \"y\":-2}, {\"x\":\"2013-02-02\", \"y\":4}, {\"x\":\"2013-02-03\", \"y\":12}, {\"x\":\"2013-02-04\", \"y\":7}, {\"x\":\"2013-02-05\", \"y\":4}, {\"x\":\"2013-02-06\", \"y\":10}, {\"x\":\"2013-02-07\", \"y\":14}, {\"x\":\"2013-02-08\", \"y\":4}, {\"x\":\"2013-02-09\", \"y\":6}, {\"x\":\"2013-02-10\", \"y\":9}, {\"x\":\"2013-02-11\", \"y\":14}, {\"x\":\"2013-02-12\", \"y\":9}, {\"x\":\"2013-02-13\", \"y\":14}, {\"x\":\"2013-02-14\", \"y\":8}, {\"x\":\"2013-02-15\", \"y\":5}, {\"x\":\"2013-02-16\", \"y\":13}, {\"x\":\"2013-02-17\", \"y\":6}, {\"x\":\"2013-02-18\", \"y\":0}, {\"x\":\"2013-02-19\", \"y\":1}, {\"x\":\"2013-02-20\", \"y\":11}, {\"x\":\"2013-02-21\", \"y\":11}, {\"x\":\"2013-02-22\", \"y\":9}, {\"x\":\"2013-02-23\", \"y\":9}, {\"x\":\"2013-02-24\", \"y\":5}, {\"x\":\"2013-02-25\", \"y\":2}, {\"x\":\"2013-02-26\", \"y\":-1}, {\"x\":\"2013-02-27\", \"y\":-2}, {\"x\":\"2013-02-28\", \"y\":1}, {\"x\":\"2013-03-01\", \"y\":2}, {\"x\":\"2013-03-02\", \"y\":7}, {\"x\":\"2013-03-03\", \"y\":4}, {\"x\":\"2013-03-04\", \"y\":1}, {\"x\":\"2013-03-05\", \"y\":6}, {\"x\":\"2013-03-06\", \"y\":10}, {\"x\":\"2013-03-07\", \"y\":5}, {\"x\":\"2013-03-08\", \"y\":1}, {\"x\":\"2013-03-09\", \"y\":4}, {\"x\":\"2013-03-10\", \"y\":11}, {\"x\":\"2013-03-11\", \"y\":10}]}" +
+                                     "];";
+
+        List<NamedXYTimeSeries> result = mapper.readValue(linesDataJson, List.class);
         resp.getOutputStream().write(mapper.writeValueAsBytes(result));
         resp.setContentType("application/json");
         setCrossSiteScriptingHeaders(resp);
@@ -209,16 +198,16 @@ public class AnalyticsServlet extends HttpServlet {
         final String products = req.getParameter(QUERY_PRODUCTS);
 
         logService.log(LogService.LOG_INFO, "RecurringRevenueOverTime " +
-                                            "startDate = " +  Objects.firstNonNull(startDate, "") +
-                                            ", endDate = " +  Objects.firstNonNull(endDate, "") +
-                                            ", products = " +  Objects.firstNonNull(products, ""));
+                                            "startDate = " + Objects.firstNonNull(startDate, "") +
+                                            ", endDate = " + Objects.firstNonNull(endDate, "") +
+                                            ", products = " + Objects.firstNonNull(products, ""));
 
         // TODO STEPH until we plug the DAO to get real data, fake it...
-        final String layersDataDJson  = "[" +
-                                        "{\"name\":\"ultimate\"," + "\"values\":[{\"x\":\"2013-01-01\",\"y\":11},{\"x\":\"2013-01-02\",\"y\":37},{\"x\":\"2013-01-03\",\"y\":16},{\"x\":\"2013-01-04\",\"y\":29},{\"x\":\"2013-01-05\",\"y\":40},{\"x\":\"2013-01-06\",\"y\":3},{\"x\":\"2013-01-07\",\"y\":4},{\"x\":\"2013-01-08\",\"y\":39},{\"x\":\"2013-01-09\",\"y\":34},{\"x\":\"2013-01-10\",\"y\":31},{\"x\":\"2013-01-11\",\"y\":20},{\"x\":\"2013-01-12\",\"y\":28},{\"x\":\"2013-01-13\",\"y\":19},{\"x\":\"2013-01-14\",\"y\":15},{\"x\":\"2013-01-15\",\"y\":31},{\"x\":\"2013-01-16\",\"y\":16},{\"x\":\"2013-01-17\",\"y\":40},{\"x\":\"2013-01-18\",\"y\":29},{\"x\":\"2013-01-19\",\"y\":31},{\"x\":\"2013-01-20\",\"y\":11},{\"x\":\"2013-01-21\",\"y\":36},{\"x\":\"2013-01-22\",\"y\":18},{\"x\":\"2013-01-23\",\"y\":12},{\"x\":\"2013-01-24\",\"y\":23},{\"x\":\"2013-01-25\",\"y\":32},{\"x\":\"2013-01-26\",\"y\":27},{\"x\":\"2013-01-27\",\"y\":33},{\"x\":\"2013-01-28\",\"y\":34},{\"x\":\"2013-01-29\",\"y\":5},{\"x\":\"2013-01-30\",\"y\":7},{\"x\":\"2013-01-31\",\"y\":13},{\"x\":\"2013-02-01\",\"y\":10},{\"x\":\"2013-02-02\",\"y\":43},{\"x\":\"2013-02-03\",\"y\":15},{\"x\":\"2013-02-04\",\"y\":38},{\"x\":\"2013-02-05\",\"y\":34},{\"x\":\"2013-02-06\",\"y\":38},{\"x\":\"2013-02-07\",\"y\":26},{\"x\":\"2013-02-08\",\"y\":27},{\"x\":\"2013-02-09\",\"y\":1},{\"x\":\"2013-02-10\",\"y\":12},{\"x\":\"2013-02-11\",\"y\":28},{\"x\":\"2013-02-12\",\"y\":10},{\"x\":\"2013-02-13\",\"y\":27},{\"x\":\"2013-02-14\",\"y\":34},{\"x\":\"2013-02-15\",\"y\":25},{\"x\":\"2013-02-16\",\"y\":39},{\"x\":\"2013-02-17\",\"y\":39},{\"x\":\"2013-02-18\",\"y\":25},{\"x\":\"2013-02-19\",\"y\":38},{\"x\":\"2013-02-20\",\"y\":1},{\"x\":\"2013-02-21\",\"y\":8},{\"x\":\"2013-02-22\",\"y\":31},{\"x\":\"2013-02-23\",\"y\":38},{\"x\":\"2013-02-24\",\"y\":43},{\"x\":\"2013-02-25\",\"y\":16},{\"x\":\"2013-02-26\",\"y\":41},{\"x\":\"2013-02-27\",\"y\":44},{\"x\":\"2013-02-28\",\"y\":20},{\"x\":\"2013-03-01\",\"y\":44},{\"x\":\"2013-03-02\",\"y\":25},{\"x\":\"2013-03-03\",\"y\":41},{\"x\":\"2013-03-04\",\"y\":34},{\"x\":\"2013-03-05\",\"y\":4},{\"x\":\"2013-03-06\",\"y\":28},{\"x\":\"2013-03-07\",\"y\":34},{\"x\":\"2013-03-08\",\"y\":25},{\"x\":\"2013-03-09\",\"y\":9},{\"x\":\"2013-03-10\",\"y\":33},{\"x\":\"2013-03-11\",\"y\":40}]}," +
-                                        "{\"name\":\"scale\",\"values\":[{\"x\":\"2013-01-01\",\"y\":22},{\"x\":\"2013-01-02\",\"y\":5},{\"x\":\"2013-01-03\",\"y\":20},{\"x\":\"2013-01-04\",\"y\":3},{\"x\":\"2013-01-05\",\"y\":7},{\"x\":\"2013-01-06\",\"y\":22},{\"x\":\"2013-01-07\",\"y\":3},{\"x\":\"2013-01-08\",\"y\":5},{\"x\":\"2013-01-09\",\"y\":5},{\"x\":\"2013-01-10\",\"y\":23},{\"x\":\"2013-01-11\",\"y\":6},{\"x\":\"2013-01-12\",\"y\":24},{\"x\":\"2013-01-13\",\"y\":7},{\"x\":\"2013-01-14\",\"y\":17},{\"x\":\"2013-01-15\",\"y\":5},{\"x\":\"2013-01-16\",\"y\":8},{\"x\":\"2013-01-17\",\"y\":3},{\"x\":\"2013-01-18\",\"y\":4},{\"x\":\"2013-01-19\",\"y\":15},{\"x\":\"2013-01-20\",\"y\":15},{\"x\":\"2013-01-21\",\"y\":2},{\"x\":\"2013-01-22\",\"y\":16},{\"x\":\"2013-01-23\",\"y\":5},{\"x\":\"2013-01-24\",\"y\":8},{\"x\":\"2013-01-25\",\"y\":10},{\"x\":\"2013-01-26\",\"y\":26},{\"x\":\"2013-01-27\",\"y\":15},{\"x\":\"2013-01-28\",\"y\":27},{\"x\":\"2013-01-29\",\"y\":24},{\"x\":\"2013-01-30\",\"y\":21},{\"x\":\"2013-01-31\",\"y\":18},{\"x\":\"2013-02-01\",\"y\":15},{\"x\":\"2013-02-02\",\"y\":23},{\"x\":\"2013-02-03\",\"y\":2},{\"x\":\"2013-02-04\",\"y\":16},{\"x\":\"2013-02-05\",\"y\":17},{\"x\":\"2013-02-06\",\"y\":15},{\"x\":\"2013-02-07\",\"y\":8},{\"x\":\"2013-02-08\",\"y\":23},{\"x\":\"2013-02-09\",\"y\":19},{\"x\":\"2013-02-10\",\"y\":26},{\"x\":\"2013-02-11\",\"y\":24},{\"x\":\"2013-02-12\",\"y\":22},{\"x\":\"2013-02-13\",\"y\":3},{\"x\":\"2013-02-14\",\"y\":0},{\"x\":\"2013-02-15\",\"y\":10},{\"x\":\"2013-02-16\",\"y\":14},{\"x\":\"2013-02-17\",\"y\":6},{\"x\":\"2013-02-18\",\"y\":5},{\"x\":\"2013-02-19\",\"y\":9},{\"x\":\"2013-02-20\",\"y\":29},{\"x\":\"2013-02-21\",\"y\":14},{\"x\":\"2013-02-22\",\"y\":16},{\"x\":\"2013-02-23\",\"y\":15},{\"x\":\"2013-02-24\",\"y\":29},{\"x\":\"2013-02-25\",\"y\":7},{\"x\":\"2013-02-26\",\"y\":4},{\"x\":\"2013-02-27\",\"y\":28},{\"x\":\"2013-02-28\",\"y\":29},{\"x\":\"2013-03-01\",\"y\":1},{\"x\":\"2013-03-02\",\"y\":23},{\"x\":\"2013-03-03\",\"y\":10},{\"x\":\"2013-03-04\",\"y\":26},{\"x\":\"2013-03-05\",\"y\":18},{\"x\":\"2013-03-06\",\"y\":25},{\"x\":\"2013-03-07\",\"y\":21},{\"x\":\"2013-03-08\",\"y\":5},{\"x\":\"2013-03-09\",\"y\":29},{\"x\":\"2013-03-10\",\"y\":13},{\"x\":\"2013-03-11\",\"y\":12}]}," +
-                                        "{\"name\":\"basic\",\"values\":[{\"x\":\"2013-01-01\",\"y\":14},{\"x\":\"2013-01-02\",\"y\":55},{\"x\":\"2013-01-03\",\"y\":14},{\"x\":\"2013-01-04\",\"y\":55},{\"x\":\"2013-01-05\",\"y\":7},{\"x\":\"2013-01-06\",\"y\":15},{\"x\":\"2013-01-07\",\"y\":15},{\"x\":\"2013-01-08\",\"y\":39},{\"x\":\"2013-01-09\",\"y\":28},{\"x\":\"2013-01-10\",\"y\":30},{\"x\":\"2013-01-11\",\"y\":32},{\"x\":\"2013-01-12\",\"y\":49},{\"x\":\"2013-01-13\",\"y\":67},{\"x\":\"2013-01-14\",\"y\":69},{\"x\":\"2013-01-15\",\"y\":29},{\"x\":\"2013-01-16\",\"y\":39},{\"x\":\"2013-01-17\",\"y\":54},{\"x\":\"2013-01-18\",\"y\":56},{\"x\":\"2013-01-19\",\"y\":52},{\"x\":\"2013-01-20\",\"y\":60},{\"x\":\"2013-01-21\",\"y\":4},{\"x\":\"2013-01-22\",\"y\":37},{\"x\":\"2013-01-23\",\"y\":67},{\"x\":\"2013-01-24\",\"y\":72},{\"x\":\"2013-01-25\",\"y\":45},{\"x\":\"2013-01-26\",\"y\":2},{\"x\":\"2013-01-27\",\"y\":70},{\"x\":\"2013-01-28\",\"y\":26},{\"x\":\"2013-01-29\",\"y\":19},{\"x\":\"2013-01-30\",\"y\":36},{\"x\":\"2013-01-31\",\"y\":73},{\"x\":\"2013-02-01\",\"y\":63},{\"x\":\"2013-02-02\",\"y\":67},{\"x\":\"2013-02-03\",\"y\":11},{\"x\":\"2013-02-04\",\"y\":38},{\"x\":\"2013-02-05\",\"y\":7},{\"x\":\"2013-02-06\",\"y\":53},{\"x\":\"2013-02-07\",\"y\":52},{\"x\":\"2013-02-08\",\"y\":31},{\"x\":\"2013-02-09\",\"y\":18},{\"x\":\"2013-02-10\",\"y\":66},{\"x\":\"2013-02-11\",\"y\":32},{\"x\":\"2013-02-12\",\"y\":49},{\"x\":\"2013-02-13\",\"y\":38},{\"x\":\"2013-02-14\",\"y\":1},{\"x\":\"2013-02-15\",\"y\":61},{\"x\":\"2013-02-16\",\"y\":54},{\"x\":\"2013-02-17\",\"y\":71},{\"x\":\"2013-02-18\",\"y\":69},{\"x\":\"2013-02-19\",\"y\":59},{\"x\":\"2013-02-20\",\"y\":58},{\"x\":\"2013-02-21\",\"y\":28},{\"x\":\"2013-02-22\",\"y\":66},{\"x\":\"2013-02-23\",\"y\":59},{\"x\":\"2013-02-24\",\"y\":62},{\"x\":\"2013-02-25\",\"y\":65},{\"x\":\"2013-02-26\",\"y\":18},{\"x\":\"2013-02-27\",\"y\":57},{\"x\":\"2013-02-28\",\"y\":0},{\"x\":\"2013-03-01\",\"y\":24},{\"x\":\"2013-03-02\",\"y\":48},{\"x\":\"2013-03-03\",\"y\":2},{\"x\":\"2013-03-04\",\"y\":28},{\"x\":\"2013-03-05\",\"y\":58},{\"x\":\"2013-03-06\",\"y\":9},{\"x\":\"2013-03-07\",\"y\":59},{\"x\":\"2013-03-08\",\"y\":30},{\"x\":\"2013-03-09\",\"y\":30},{\"x\":\"2013-03-10\",\"y\":66},{\"x\":\"2013-03-11\",\"y\":48}]}" +
-                                        "]";
+        final String layersDataDJson = "[" +
+                                       "{\"name\":\"ultimate\"," + "\"values\":[{\"x\":\"2013-01-01\",\"y\":11},{\"x\":\"2013-01-02\",\"y\":37},{\"x\":\"2013-01-03\",\"y\":16},{\"x\":\"2013-01-04\",\"y\":29},{\"x\":\"2013-01-05\",\"y\":40},{\"x\":\"2013-01-06\",\"y\":3},{\"x\":\"2013-01-07\",\"y\":4},{\"x\":\"2013-01-08\",\"y\":39},{\"x\":\"2013-01-09\",\"y\":34},{\"x\":\"2013-01-10\",\"y\":31},{\"x\":\"2013-01-11\",\"y\":20},{\"x\":\"2013-01-12\",\"y\":28},{\"x\":\"2013-01-13\",\"y\":19},{\"x\":\"2013-01-14\",\"y\":15},{\"x\":\"2013-01-15\",\"y\":31},{\"x\":\"2013-01-16\",\"y\":16},{\"x\":\"2013-01-17\",\"y\":40},{\"x\":\"2013-01-18\",\"y\":29},{\"x\":\"2013-01-19\",\"y\":31},{\"x\":\"2013-01-20\",\"y\":11},{\"x\":\"2013-01-21\",\"y\":36},{\"x\":\"2013-01-22\",\"y\":18},{\"x\":\"2013-01-23\",\"y\":12},{\"x\":\"2013-01-24\",\"y\":23},{\"x\":\"2013-01-25\",\"y\":32},{\"x\":\"2013-01-26\",\"y\":27},{\"x\":\"2013-01-27\",\"y\":33},{\"x\":\"2013-01-28\",\"y\":34},{\"x\":\"2013-01-29\",\"y\":5},{\"x\":\"2013-01-30\",\"y\":7},{\"x\":\"2013-01-31\",\"y\":13},{\"x\":\"2013-02-01\",\"y\":10},{\"x\":\"2013-02-02\",\"y\":43},{\"x\":\"2013-02-03\",\"y\":15},{\"x\":\"2013-02-04\",\"y\":38},{\"x\":\"2013-02-05\",\"y\":34},{\"x\":\"2013-02-06\",\"y\":38},{\"x\":\"2013-02-07\",\"y\":26},{\"x\":\"2013-02-08\",\"y\":27},{\"x\":\"2013-02-09\",\"y\":1},{\"x\":\"2013-02-10\",\"y\":12},{\"x\":\"2013-02-11\",\"y\":28},{\"x\":\"2013-02-12\",\"y\":10},{\"x\":\"2013-02-13\",\"y\":27},{\"x\":\"2013-02-14\",\"y\":34},{\"x\":\"2013-02-15\",\"y\":25},{\"x\":\"2013-02-16\",\"y\":39},{\"x\":\"2013-02-17\",\"y\":39},{\"x\":\"2013-02-18\",\"y\":25},{\"x\":\"2013-02-19\",\"y\":38},{\"x\":\"2013-02-20\",\"y\":1},{\"x\":\"2013-02-21\",\"y\":8},{\"x\":\"2013-02-22\",\"y\":31},{\"x\":\"2013-02-23\",\"y\":38},{\"x\":\"2013-02-24\",\"y\":43},{\"x\":\"2013-02-25\",\"y\":16},{\"x\":\"2013-02-26\",\"y\":41},{\"x\":\"2013-02-27\",\"y\":44},{\"x\":\"2013-02-28\",\"y\":20},{\"x\":\"2013-03-01\",\"y\":44},{\"x\":\"2013-03-02\",\"y\":25},{\"x\":\"2013-03-03\",\"y\":41},{\"x\":\"2013-03-04\",\"y\":34},{\"x\":\"2013-03-05\",\"y\":4},{\"x\":\"2013-03-06\",\"y\":28},{\"x\":\"2013-03-07\",\"y\":34},{\"x\":\"2013-03-08\",\"y\":25},{\"x\":\"2013-03-09\",\"y\":9},{\"x\":\"2013-03-10\",\"y\":33},{\"x\":\"2013-03-11\",\"y\":40}]}," +
+                                       "{\"name\":\"scale\",\"values\":[{\"x\":\"2013-01-01\",\"y\":22},{\"x\":\"2013-01-02\",\"y\":5},{\"x\":\"2013-01-03\",\"y\":20},{\"x\":\"2013-01-04\",\"y\":3},{\"x\":\"2013-01-05\",\"y\":7},{\"x\":\"2013-01-06\",\"y\":22},{\"x\":\"2013-01-07\",\"y\":3},{\"x\":\"2013-01-08\",\"y\":5},{\"x\":\"2013-01-09\",\"y\":5},{\"x\":\"2013-01-10\",\"y\":23},{\"x\":\"2013-01-11\",\"y\":6},{\"x\":\"2013-01-12\",\"y\":24},{\"x\":\"2013-01-13\",\"y\":7},{\"x\":\"2013-01-14\",\"y\":17},{\"x\":\"2013-01-15\",\"y\":5},{\"x\":\"2013-01-16\",\"y\":8},{\"x\":\"2013-01-17\",\"y\":3},{\"x\":\"2013-01-18\",\"y\":4},{\"x\":\"2013-01-19\",\"y\":15},{\"x\":\"2013-01-20\",\"y\":15},{\"x\":\"2013-01-21\",\"y\":2},{\"x\":\"2013-01-22\",\"y\":16},{\"x\":\"2013-01-23\",\"y\":5},{\"x\":\"2013-01-24\",\"y\":8},{\"x\":\"2013-01-25\",\"y\":10},{\"x\":\"2013-01-26\",\"y\":26},{\"x\":\"2013-01-27\",\"y\":15},{\"x\":\"2013-01-28\",\"y\":27},{\"x\":\"2013-01-29\",\"y\":24},{\"x\":\"2013-01-30\",\"y\":21},{\"x\":\"2013-01-31\",\"y\":18},{\"x\":\"2013-02-01\",\"y\":15},{\"x\":\"2013-02-02\",\"y\":23},{\"x\":\"2013-02-03\",\"y\":2},{\"x\":\"2013-02-04\",\"y\":16},{\"x\":\"2013-02-05\",\"y\":17},{\"x\":\"2013-02-06\",\"y\":15},{\"x\":\"2013-02-07\",\"y\":8},{\"x\":\"2013-02-08\",\"y\":23},{\"x\":\"2013-02-09\",\"y\":19},{\"x\":\"2013-02-10\",\"y\":26},{\"x\":\"2013-02-11\",\"y\":24},{\"x\":\"2013-02-12\",\"y\":22},{\"x\":\"2013-02-13\",\"y\":3},{\"x\":\"2013-02-14\",\"y\":0},{\"x\":\"2013-02-15\",\"y\":10},{\"x\":\"2013-02-16\",\"y\":14},{\"x\":\"2013-02-17\",\"y\":6},{\"x\":\"2013-02-18\",\"y\":5},{\"x\":\"2013-02-19\",\"y\":9},{\"x\":\"2013-02-20\",\"y\":29},{\"x\":\"2013-02-21\",\"y\":14},{\"x\":\"2013-02-22\",\"y\":16},{\"x\":\"2013-02-23\",\"y\":15},{\"x\":\"2013-02-24\",\"y\":29},{\"x\":\"2013-02-25\",\"y\":7},{\"x\":\"2013-02-26\",\"y\":4},{\"x\":\"2013-02-27\",\"y\":28},{\"x\":\"2013-02-28\",\"y\":29},{\"x\":\"2013-03-01\",\"y\":1},{\"x\":\"2013-03-02\",\"y\":23},{\"x\":\"2013-03-03\",\"y\":10},{\"x\":\"2013-03-04\",\"y\":26},{\"x\":\"2013-03-05\",\"y\":18},{\"x\":\"2013-03-06\",\"y\":25},{\"x\":\"2013-03-07\",\"y\":21},{\"x\":\"2013-03-08\",\"y\":5},{\"x\":\"2013-03-09\",\"y\":29},{\"x\":\"2013-03-10\",\"y\":13},{\"x\":\"2013-03-11\",\"y\":12}]}," +
+                                       "{\"name\":\"basic\",\"values\":[{\"x\":\"2013-01-01\",\"y\":14},{\"x\":\"2013-01-02\",\"y\":55},{\"x\":\"2013-01-03\",\"y\":14},{\"x\":\"2013-01-04\",\"y\":55},{\"x\":\"2013-01-05\",\"y\":7},{\"x\":\"2013-01-06\",\"y\":15},{\"x\":\"2013-01-07\",\"y\":15},{\"x\":\"2013-01-08\",\"y\":39},{\"x\":\"2013-01-09\",\"y\":28},{\"x\":\"2013-01-10\",\"y\":30},{\"x\":\"2013-01-11\",\"y\":32},{\"x\":\"2013-01-12\",\"y\":49},{\"x\":\"2013-01-13\",\"y\":67},{\"x\":\"2013-01-14\",\"y\":69},{\"x\":\"2013-01-15\",\"y\":29},{\"x\":\"2013-01-16\",\"y\":39},{\"x\":\"2013-01-17\",\"y\":54},{\"x\":\"2013-01-18\",\"y\":56},{\"x\":\"2013-01-19\",\"y\":52},{\"x\":\"2013-01-20\",\"y\":60},{\"x\":\"2013-01-21\",\"y\":4},{\"x\":\"2013-01-22\",\"y\":37},{\"x\":\"2013-01-23\",\"y\":67},{\"x\":\"2013-01-24\",\"y\":72},{\"x\":\"2013-01-25\",\"y\":45},{\"x\":\"2013-01-26\",\"y\":2},{\"x\":\"2013-01-27\",\"y\":70},{\"x\":\"2013-01-28\",\"y\":26},{\"x\":\"2013-01-29\",\"y\":19},{\"x\":\"2013-01-30\",\"y\":36},{\"x\":\"2013-01-31\",\"y\":73},{\"x\":\"2013-02-01\",\"y\":63},{\"x\":\"2013-02-02\",\"y\":67},{\"x\":\"2013-02-03\",\"y\":11},{\"x\":\"2013-02-04\",\"y\":38},{\"x\":\"2013-02-05\",\"y\":7},{\"x\":\"2013-02-06\",\"y\":53},{\"x\":\"2013-02-07\",\"y\":52},{\"x\":\"2013-02-08\",\"y\":31},{\"x\":\"2013-02-09\",\"y\":18},{\"x\":\"2013-02-10\",\"y\":66},{\"x\":\"2013-02-11\",\"y\":32},{\"x\":\"2013-02-12\",\"y\":49},{\"x\":\"2013-02-13\",\"y\":38},{\"x\":\"2013-02-14\",\"y\":1},{\"x\":\"2013-02-15\",\"y\":61},{\"x\":\"2013-02-16\",\"y\":54},{\"x\":\"2013-02-17\",\"y\":71},{\"x\":\"2013-02-18\",\"y\":69},{\"x\":\"2013-02-19\",\"y\":59},{\"x\":\"2013-02-20\",\"y\":58},{\"x\":\"2013-02-21\",\"y\":28},{\"x\":\"2013-02-22\",\"y\":66},{\"x\":\"2013-02-23\",\"y\":59},{\"x\":\"2013-02-24\",\"y\":62},{\"x\":\"2013-02-25\",\"y\":65},{\"x\":\"2013-02-26\",\"y\":18},{\"x\":\"2013-02-27\",\"y\":57},{\"x\":\"2013-02-28\",\"y\":0},{\"x\":\"2013-03-01\",\"y\":24},{\"x\":\"2013-03-02\",\"y\":48},{\"x\":\"2013-03-03\",\"y\":2},{\"x\":\"2013-03-04\",\"y\":28},{\"x\":\"2013-03-05\",\"y\":58},{\"x\":\"2013-03-06\",\"y\":9},{\"x\":\"2013-03-07\",\"y\":59},{\"x\":\"2013-03-08\",\"y\":30},{\"x\":\"2013-03-09\",\"y\":30},{\"x\":\"2013-03-10\",\"y\":66},{\"x\":\"2013-03-11\",\"y\":48}]}" +
+                                       "]";
 
         List<NamedXYTimeSeries> result = mapper.readValue(layersDataDJson.getBytes(), List.class);
         resp.getOutputStream().write(mapper.writeValueAsBytes(result));
@@ -240,8 +229,7 @@ public class AnalyticsServlet extends HttpServlet {
         final URL resourceUrl = Resources.getResource(resourceName);
 
 
-
-        final String [] parts = resourceName.split("/");
+        final String[] parts = resourceName.split("/");
         if (parts.length > 2) {
             if (parts[1].equals("javascript")) {
                 resp.setContentType("application/javascript");
@@ -255,7 +243,7 @@ public class AnalyticsServlet extends HttpServlet {
 
             final ByteArrayOutputStream out = new ByteArrayOutputStream();
             Resources.copy(resourceUrl, out);
-            String inputHtml  = new String(out.toByteArray());
+            String inputHtml = new String(out.toByteArray());
 
             // TODO STEPH we need to extract real ip port on which http sever is listening to
             String tmp1 = inputHtml.replace("$VAR_SERVER", "\"127.0.0.1\"");
diff --git a/osgi-bundles/bundles/analytics/src/main/resources/static/dashboard.html b/osgi-bundles/bundles/analytics/src/main/resources/static/dashboard.html
index 0d15319..e6fd558 100644
--- a/osgi-bundles/bundles/analytics/src/main/resources/static/dashboard.html
+++ b/osgi-bundles/bundles/analytics/src/main/resources/static/dashboard.html
@@ -28,8 +28,96 @@
     </head>
     <body>
         <div id="chartAnchor"></div>
-        
+
         <script type="text/javascript">
+
+            function doGetData(server, port, analyticsPrefix, endpoint, queryParams, fn) {
+
+                var request_url = "http://" + server + ":" + port + analyticsPrefix + endpoint;
+                var first = true;
+
+                if (queryParams) {
+                   var queryKeys = Object.keys(queryParams);
+                   for (var i = 0; i < queryKeys.length; i++) {
+                       var curKey = queryKeys[i];
+                       var delim = first ? "?" : "&";
+                       request_url = request_url + "?" + curKey + "=" +  queryParams[curKey]
+                       first = false;
+                   }
+                }
+
+                console.log("request_url " + request_url);
+
+                return $.ajax({
+                   type: "GET",
+                   contentType: "application/json",
+                   url: request_url
+                }).done(function(data) {
+                   console.log("Done " + request_url + " with data " + data.length);
+                   fn(data);
+                });
+             }
+
+            function drawAll(input) {
+
+                var canvasHeigthWithMargins = input.canvasHeigth + input.topMargin + input.betweenGraphMargin + input.bottomMargin;
+                var canvasHeigthGraph = input.canvasHeigth / 2;
+
+                var translateX = input.leftMargin;
+
+                var graphStructure = new killbillGraph.GraphStructure();
+
+
+                graphStructure.setupDomStructure();
+                var canvas = graphStructure.createCanvas([input.topMargin, input.rightMargin, input.bottomMargin, input.leftMargin],
+                input.canvasWidth, canvasHeigthWithMargins);
+
+                var curTranslateY = input.topMargin;
+                var curTranslateLabelY = curTranslateY + (canvasHeigthGraph / 2);
+                var lineCanvas = graphStructure.createCanvasGroup(canvas, translateX, curTranslateY);
+                var linesGraph = new killbillGraph.KBLinesGraph(lineCanvas, input.linesData, input.canvasWidth, canvasHeigthGraph, d3.scale.category20b());
+                linesGraph.drawLines();
+                linesGraph.addLabels("labelsLine", curTranslateLabelY);
+
+                curTranslateY = curTranslateY + canvasHeigthGraph + input.betweenGraphMargin;
+                curTranslateLabelY = curTranslateLabelY + input.betweenGraphMargin;
+                var stackCanvas = graphStructure.createCanvasGroup(canvas, translateX, curTranslateY);
+                var layersGraph = new killbillGraph.KBLayersGraph(stackCanvas, input.layersData, input.canvasWidth, canvasHeigthGraph, d3.scale.category20c());
+                layersGraph.drawStackLayers();
+                layersGraph.addLabels("labelsLayer", curTranslateLabelY);
+
+
+                curTranslateY = curTranslateY + canvasHeigthGraph + input.betweenGraphMargin;
+                var xAxisCanvaGroup = graphStructure.createCanvasGroup(canvas, translateX, curTranslateY);
+                layersGraph.createXAxis(xAxisCanvaGroup, 2 * (canvasHeigthGraph + input.betweenGraphMargin));
+
+                layersGraph.addMouseOverCircleForValue();
+            }
+
+             function fetchDataAndDrawAll(server, port) {
+
+                 $().ready(function() {
+
+                     var layersData;
+                     var linesData;
+
+                     $.when(doGetData(server, port, "/plugins/killbill-analytics", "/planTransitionsOverTime", null, function(data) {
+                         linesData = data;
+                     }), doGetData(server, port, "/plugins/killbill-analytics", "/recurringRevenueOverTime", null, function(data) {
+                         layersData = data;
+                     })).done(function() {
+                         console.log( 'I fire once BOTH ajax requests have completed...');
+
+                         var input = new killbillGraph.KBInputGraphs(800, 600, 80, 80, 80, 80, 30, [linesData, layersData]);
+                         drawAll(input);
+                     }) .fail(function() {
+                        console.log( 'I fire if one or more requests failed.' );
+                     });
+                 });
+            }
+
+
+
             fetchDataAndDrawAll($VAR_SERVER, $VAR_PORT);
         </script>
     </body>
diff --git a/osgi-bundles/bundles/analytics/src/main/resources/static/javascript/killbill.js b/osgi-bundles/bundles/analytics/src/main/resources/static/javascript/killbill.js
index 3082242..bd11a98 100644
--- a/osgi-bundles/bundles/analytics/src/main/resources/static/javascript/killbill.js
+++ b/osgi-bundles/bundles/analytics/src/main/resources/static/javascript/killbill.js
@@ -1,499 +1,503 @@
-
-/**
-* Extract the 'x' or 'y' from dataLyer format
-* where each entry if of the form:
+/*
+*  'killbillGraph' is the namespace required to access all the public objects
 *
-*  "name": "crescendo",
-*  "values": [
-*     { "x": "2010-07-08", "y":  0},
-*     ....
-*/
-function extractKeyOrValueFromDataLayer(dataLayer, attr) {
-    var result = [];
-    for (var i = 0; i < dataLayer.values.length; i++) {
-        result.push(dataLayer.values[i][attr])
-    }
-    return result;
-}
-
-
-/**
-* Create the 'x' scale -- with x being a date
-*/
-function getScaleDate(dataX, w) {
-    var minDate = new Date(dataX[0]);
-    var maxDate = new Date(dataX[dataX.length - 1]);
-    return d3.time.scale().domain([minDate, maxDate]).range([0, w]);
-}
-
-/**
-* Create the 'y' scale for line graphs (non stacked)
-*/
-function getScaleValue(dataYs, h) {
-     var minValue = 0;
-     var maxValue = 0;
-     for (var i=0; i<dataYs.length; i++) {
-         for (var j = 0; j < dataYs[i].length; j++) {
-             if (dataYs[i][j] < minValue) {
-                 minValue = dataYs[i][j];
-             }
-             if (dataYs[i][j] > maxValue) {
-                 maxValue = dataYs[i][j];
-             }
-         }
-     }
-     if (minValue > 0) {
-         minValue = 0;
-     }
-     return d3.scale.linear().domain([minValue, maxValue]).range([h, 0]);
- }
-
-/**
-* Create the 'y' scale for the stack graph
 *
-* Extract min/max for each x value across all layers
-* 
-*/
-function getStackedScaleValue(dataLayers, h) {
-
-    var tmp = [];
-    for (var i = 0; i < dataLayers.length; i++) {
-        tmp.push(dataLayers[i].values)
-    }
-
-    var sumValues = [];
-    for (var i = 0; i < tmp[0].length; i++) {
-        var max = 0;
-        for (var j = 0; j < tmp.length; j++) {
-              max = max + tmp[j][i].y;
-        }
-        sumValues.push(max);
-    }
-    var minValue = 0;
-    var maxValue = 0;
-    for (var i = 0; i < sumValues.length; i++) {
-        if (sumValues[i] < minValue) {
-            minValue = sumValues[i];
-        }
-        if (sumValues[i] > maxValue) {
-            maxValue = sumValues[i];
-        }
-    }
-    if (minValue > 0) {
-        minValue = 0;
-    }
-    return d3.scale.linear().domain([minValue,  maxValue]).range([h, 0]);
-}
-
-
-/**
-* Create the 'X' axis in a new svg group
-*/
-function createXAxis(graph, scaleX, h, xAxisHeightTick) {
-    var xAxis = d3.svg.axis().scale(scaleX).tickSize(- xAxisHeightTick).tickSubdivide(true);
-    graph.append("svg:g")
-           .attr("class", "x axis")
-           .attr("transform", "translate(0," + h + ")")
-         .call(xAxis);
-}
-
-/**
-* Create the 'Y' axis in a new svg group
-*/
-function createYAxis(graph, scaleY) {
-    var yAxisLeft = d3.svg.axis().scale(scaleY).ticks(4).orient("left");
-    graph.append("svg:g")
-            .attr("class", "y axis")
-            .attr("transform", "translate(-25,0)")
-          .call(yAxisLeft);
-}
-
-/**
-* Create the area function that defines for each point in the stack graph
-* its x, y0 (offest from previous stacked graph) and y position
+*  Input are expected to be of the form:
+*   dataForGraph = [ {"name":"line1", "values":[{"x":"2013-01-01", "y":6}, {"x":"2013-01-02", "y":6}] },
+*                    {"name":"line2", "values":[{"x":"2013-01-01", "y":12}, {"x":"2013-01-02", "y":3}] } ];
+*
+*   There can be up to 20 lines -- limited by the color palette -- per graph; the graph can be either:
+*   - layered graph (KBLayersGraph)
+*   - lines graph (KBLinesGraph)
+*
+*   Description of the fields:
+*   - name is the 'name of the line-- as shown in the label
+*   - values are the {x,y} coordinates for each point; the x coordinates should be dates and should all be the same for each entries.
+*
 */
-function createLayerArea(scaleX, scaleY) {
-    var area = d3.svg.area()
-          .x(function(d) {
-              return scaleX(new Date(d.x));
-          })
-          .y0(function(d) {
-              return scaleY(d.y0);                
-          })
-          .y1(function(d) {
-              return scaleY(d.y + d.y0);
+(function(killbillGraph, $, undefined) {
+
+  /**
+  * Input parameters to draw all the graphs
+  */
+  killbillGraph.KBInputGraphs = function(canvasWidth, canvasHeigth, topMargin, rightMargin, bottomMargin, leftMargin, betweenGraphMargin, graphData) {
+
+      this.topMargin = topMargin;
+      this.rightMargin = rightMargin;
+      this.bottomMargin = bottomMargin;
+      this.leftMargin = leftMargin;
+
+
+      this.betweenGraphMargin = betweenGraphMargin;
+
+      this.canvasWidth = canvasWidth;
+      this.canvasHeigth = canvasHeigth;
+
+      this.linesData = graphData[0];
+      this.layersData = graphData[1];
+  }
+
+
+  /**
+  * KBGraph : Base class for both layered and non layered graphs
+  */
+  killbillGraph.KBGraph = function(graphCanvas, data, width, heigth, palette) {
+
+      this.graphCanvas = graphCanvas;
+      this.data = data;
+      this.width = width;
+      this.heigth = heigth;
+
+      // the palette function out of which we create color map
+      this.palette = palette;
+
+
+      /**
+      * Create the 'x' date scale
+      * - dataX is is an ordered array of all the dates
+      */
+      this.getScaleDate = function() {
+
+          var dataX = this.extractKeyOrValueFromDataLayer(this.data[0], 'x');
+          var minDate = new Date(dataX[0]);
+          var maxDate = new Date(dataX[dataX.length - 1]);
+          return d3.time.scale().domain([minDate, maxDate]).range([0, width]);
+      }
+
+      /**
+      * Create the 'Y' axis in a new svg group
+      * - scaleY is the d3 scale built based on height and y point range
+      */
+      this.createYAxis = function(scaleY) {
+          var yAxisLeft = d3.svg.axis().scale(scaleY).ticks(4).orient("left");
+          this.graphCanvas.append("svg:g")
+                  .attr("class", "y axis")
+                  .attr("transform", "translate(-25,0)")
+                .call(yAxisLeft);
+      }
+
+      /**
+      * Create the 'X' axis in a new svg group
+      * - dataLayer : the data for the layer forma
+      * - xAxisGraphGroup the group where this axis will be attached to
+      * - xAxisHeightTick the height of the ticks
+      */
+      this.createXAxis = function(xAxisGraphGroup, xAxisHeightTick) {
+
+          var scaleX = this.getScaleDate();
+          var xAxis = d3.svg.axis().scale(scaleX).tickSize(- xAxisHeightTick).tickSubdivide(true);
+          xAxisGraphGroup.append("svg:g")
+                 .attr("class", "x axis")
+               .call(xAxis);
+      }
+
+
+      /**
+       * Add the cirles for each point in the graph line
+       *
+       * This is used for both stacked and non stacked lines
+       */
+       this.addCirclesForGraph = function(circleGroup, lineId, dataX, dataY, scaleX, scaleY, lineColor) {
+            var node = circleGroup.selectAll("circles")
+                .data(dataY)
+                .enter()
+                .append("svg:g");
+
+           /* First we add the circle */
+           node.append("svg:circle")
+           .attr("id", function(d, i) {
+                 return "circle-" + lineId + "-" + i; })
+           .attr("cx", function(d, i) {
+               return scaleX(new Date(dataX[i]));
+            })
+           .attr("cy", function(d, i) {
+               return scaleY(d);
+            })
+           .attr("r", 3)
+           .attr("fill", lineColor)
+           .attr("value", function(d, i) {
+                 return d;
+           });
+
+           /* Second we do another pass and add the overlay text for value */
+           node.append("svg:text")
+               .attr("x",  function(d, i) {
+                     return scaleX(new Date(dataX[i]));
+               })
+               .attr("y", function(d, i) {
+                  return scaleY(d);
+                })
+               .attr("class", "overlay")
+               .attr("display", "none")
+               .text(function(d, i) {
+                   return "value = " + d;
+                });
+      }
+
+      /**
+      * Extract the 'x' or 'y' from dataLyer format where each entry if of the form:
+      * - attr is either the 'x' or 'y'
+      * - dataLayer : the data for a given layer
+      * E.g:
+      *  "name": "crescendo",
+      *  "values": [
+      *     { "x": "2010-07-08", "y":  0},
+      *     ....
+      */
+      this.extractKeyOrValueFromDataLayer = function(dataLayer, attr) {
+          var result = [];
+          for (var i = 0; i < dataLayer.values.length; i++) {
+              result.push(dataLayer.values[i][attr])
+          }
+          return result;
+      }
+
+
+      /**
+      * Create the 'y' scale for the stack graph
+      *
+      * Extract min/max for each x value across all layers
+      *
+      */
+      this.getYScaleValue = function(dataLayers, h) {
+
+          var tmp = [];
+          for (var i = 0; i < dataLayers.length; i++) {
+              tmp.push(dataLayers[i].values)
+          }
+
+          var sumValues = [];
+          for (var i = 0; i < tmp[0].length; i++) {
+              var max = 0;
+              for (var j = 0; j < tmp.length; j++) {
+                    max = max + tmp[j][i].y;
+              }
+              sumValues.push(max);
+          }
+          var minValue = 0;
+          var maxValue = 0;
+          for (var i = 0; i < sumValues.length; i++) {
+              if (sumValues[i] < minValue) {
+                  minValue = sumValues[i];
+              }
+              if (sumValues[i] > maxValue) {
+                  maxValue = sumValues[i];
+              }
+          }
+          if (minValue > 0) {
+              minValue = 0;
+          }
+          return d3.scale.linear().domain([minValue,  maxValue]).range([h, 0]);
+      }
+
+
+
+      /**
+      * Add on the path the name of the line -- not used anymore as we are using external labels
+      */
+      this.addPathLabel = function(graph, lineId, positionPercent) {
+           graph.append("svg:g")
+                .append("text")
+                .attr("font-size", "15")
+                .append("svg:textPath")
+                .attr("xlink:href", "#" + lineId)
+                .attr("startOffset", positionPercent)
+                .text(function(d) { return lineId; });
+       }
+
+
+
+      /**
+      * Create a new group for the circles-- no translations needed
+      */
+      this.createCircleGroup = function(canvas, lineId) {
+          return this.graphCanvas.append("svg:g")
+              .attr("id", "circles-" + lineId);
+      }
+
+      /**
+      * Given a colorMap, extract the k-ieme color
+      *
+      * The colormap are standard d3 colormap which swicth to new color every 4 colors;
+      * in order to maximize the difference among colors we first get colors that are far apar
+      *
+      */
+      this.getColor = function(k) {
+          var div = Math.floor(k / 4);
+          var mod = k % 4;
+          var value = div + 4 * mod;
+          return this.colorMap[value];
+      }
+
+      /**
+      *  Create the color map from the d3 palette
+      */
+      this.createColorMap = function() {
+          var colorMap = {}
+          for (var i = 0; i < 20; i++) {
+              colorMap[i] = this.palette(i);
+          }
+          return colorMap;
+      }
+
+      /**
+      * Add the list label on the 'legend'
+      */
+
+      this.addLabels = function(labelId, translateY) {
+
+          var $divLabelLayers = $('<div id=' + labelId +'  style="margin-top:' + translateY + 'px"></div>');
+          var $labelList = $('<ul></ul>');
+          $divLabelLayers.append($labelList);
+
+          $("#legend").append($divLabelLayers);
+          for (var i = 0; i < this.data.length; i++) {
+              this.addLabel($labelList, this.getColor(i), this.data[i].name);
+          }
+      }
+
+      /**
+      * Add a given label
+      */
+      this.addLabel = function(labelList, color, labelName) {
+
+          var $labelListItem =  $('<li style="list-style-type: none">');
+          var $divLabelListItem = $('<div id=label-"' + labelName + '" class="swatch" style="background-color:' + color + '"></div>');
+          var $spanListItemName = $('<span>' + labelName + '</span> ');
+
+          $labelListItem.append($divLabelListItem);
+          $labelListItem.append($spanListItemName);
+          labelList.append($labelListItem);
+      }
+
+      /**
+      * Attach handlers to all circles so as to display value
+      *
+      * Note that this will attach for all graphs-- not only the one attached to that objec
+      */
+      this.addMouseOverCircleForValue = function() {
+
+          $('circle').each(function(i) {
+              var circleGroup = $(this).parent();
+              var circleText = circleGroup.find('text').first();
+
+              $(this).hover(function() {
+                  circleText.show();
+              }, function() {
+                  circleText.hide();
+              });
           });
-    return area;
-}
+      }
 
-/**
-* All all layers on the graph
-*/
-function addLayers(graph, colorMapLayers, stack, area, dataLayers) {
-    
-   var dataLayerStack = stack(dataLayers);
-   
-   graph.selectAll("path")
-        .data(dataLayerStack)
-        .enter()
-        .append("path")
-        .style("fill", function(d,i) {
-            return getColor(colorMapLayers, i);
-         }).attr("d", function(d) {
-            return area(d.values);
-         })
-         .attr("id", function(d) {
-             return d.name;
-         });    
-}
-
-/**
-* Draw all layers-- calls previous function addLayers
-* It will create its Y axis
-*/
-function drawStackLayers(graph, margins, colorMapLayers, dataLayers, w, h, xAxisHeightTick) {
+      /* Build and save colorMap */
+      this.colorMap = this.createColorMap();
+  }
 
-      // Compute scales
-      var dataX = extractKeyOrValueFromDataLayer(dataLayers[0], 'x');
-      var scaleX = getScaleDate(dataX, w);
-      var scaleY = getStackedScaleValue(dataLayers, h);
+  /**
+  *  KBLayersGraph : Inherits KBGraph abd offers specifities for layered graphs
+  */
+  killbillGraph.KBLayersGraph = function(graphCanvas, data, width, heigth, palette) {
 
-     var stack = d3.layout.stack()
-         .offset("zero")
-         .values(function(d) { return d.values; });
+     killbillGraph.KBGraph.call(this, graphCanvas, data, width, heigth, palette);
 
-     var area = createLayerArea(scaleX, scaleY);
 
-     addLayers(graph, colorMapLayers, stack, area, dataLayers);
 
-     var dataY0 = null;
-     for (var i = 0; i < dataLayers.length; i++) {
+     /**
+     * Create the area function that defines for each point in the stack graph
+     * its x, y0 (offest from previous stacked graph) and y position
+     */
+     this.createLayerArea = function(scaleX, scaleY) {
+         var area = d3.svg.area()
+               .x(function(d) {
+                   return scaleX(new Date(d.x));
+               })
+               .y0(function(d) {
+                   return scaleY(d.y0);
+               })
+               .y1(function(d) {
+                   return scaleY(d.y + d.y0);
+               });
+         return area;
+     }
 
-         var circleGroup = createCircleGroup(graph, dataLayers[i]['name'], margins[3], margins[0]);
-         var dataY = extractKeyOrValueFromDataLayer(dataLayers[i], 'y');
-         if (dataY0) {
-             for (var k = 0; k < dataY.length; k++) {
-                 dataY[k] = dataY[k] + dataY0[k];
-             }
-         }
-         addCirclesForGraph(circleGroup, dataLayers[i]['name'], dataX, dataY, scaleX, scaleY, getColor(colorMapLayers, i));
-         dataY0 = dataY;
+     /**
+     * All all layers on the graph
+     */
+     this.addLayers = function(stack, area, dataLayers) {
+
+        var dataLayerStack = stack(dataLayers);
+
+        var currentObj = this;
+
+        this.graphCanvas.selectAll("path")
+             .data(dataLayerStack)
+             .enter()
+             .append("path")
+             .style("fill", function(d,i) {
+                 return currentObj.getColor(i);
+              }).attr("d", function(d) {
+                 return area(d.values);
+              })
+              .attr("id", function(d) {
+                  return d.name;
+              });
      }
 
-     createYAxis(graph, scaleY);
-}
+     /**
+     * Draw all layers-- calls previous function addLayers
+     * It will create its Y axis
+     */
+     this.drawStackLayers = function() {
 
-/**
-* Add the svg line for this data (dataX, dataY)
-*/
-function addLine(graph, margins, dataX, dataY, scaleX, scaleY, lineColor, lineId) {
-     graph.selectAll("path.line")
-           .data([dataY])
-           .enter()
-           .append("svg:path")
-           .attr("d", d3.svg.line()
-               .x(function(d,i) {
-                 return scaleX(new Date(dataX[i]));
-               })
-               .y(function(d) {
-                 return scaleY(d);
-               }))
-           .attr("id", lineId)
-           .style("stroke", lineColor);
-           
-      var circleGroup = createCircleGroup(graph, lineId, margins[3], margins[0] );
-      addCirclesForGraph(circleGroup, lineId, dataX, dataY, scaleX, scaleY, lineColor);
-}
-
-
-/**
-* Draw all lines
-* It will create its Y axis
-*/
-function drawLines(graph, margins, colorMapLines, linesData, w, h) {
-      
-    var dataX = linesData[0]["dates"]
-    var dataYs = [];
-    for (var i = 0; i < linesData.length; i++) { dataYs.push(linesData[i]["values"]); }
-    
-    var scaleX = getScaleDate(dataX, w);
-    var scaleY = getScaleValue(dataYs, h);
-
-    for (var k=0; k<dataYs.length; k++) {
-        addLine(graph, margins, dataX, dataYs[k], scaleX, scaleY, getColor(colorMapLines, k), linesData[k]['name']);
-    }              
-    createYAxis(graph, scaleY);
- }
- 
-
- /**
- * Add the cirles for each point in the graph line
- * 
- * This is used for both stacked and non stacked lines
- */
- function addCirclesForGraph(circleGroup, lineId, dataX, dataY, scaleX, scaleY, lineColor) {
-      var node = circleGroup.selectAll("circles") 
-          .data(dataY) 
-          .enter() 
-          .append("svg:g");
-          
-     /* First we add the circle */      
-     node.append("svg:circle") 
-     .attr("id", function(d, i) { 
-           return "circle-" + lineId + "-" + i; })
-     .attr("cx", function(d, i) { 
-         return scaleX(new Date(dataX[i]));
-      }) 
-     .attr("cy", function(d, i) { 
-         return scaleY(d);
-      }) 
-     .attr("r", 3)
-     .attr("fill", lineColor)
-     .attr("value", function(d, i) { 
-           return d;
-     });
-     
-     /* Second we do another pass and add the overlay text for value */
-     node.append("svg:text")
-         .attr("x",  function(d, i) { 
-               return scaleX(new Date(dataX[i]));
-         })
-         .attr("y", function(d, i) { 
-            return scaleY(d);
-          })
-         .attr("class", "overlay")
-         .attr("display", "none")      
-         .text(function(d, i) { 
-             return "value = " + d;
-          });
-} 
+        var scaleX = this.getScaleDate();
+        var scaleY = this.getYScaleValue(this.data, this.heigth);
 
-/**
-* Add on the path the name of the line -- not used anymore as we are using external labels
-*/
-function addPathLabel(graph, lineId, positionPercent) {
-     graph.append("svg:g")
-          .append("text")
-          .attr("font-size", "15")
-          .append("svg:textPath")
-          .attr("xlink:href", "#" + lineId)
-          .attr("startOffset", positionPercent)
-          .text(function(d) { return lineId; });               
- }
-
-/**
-* Create initial canvas on which to draw all graphs
-*/
-function createCanvas(m, w, h) {
-    return d3.select("#chartId")
-              .append("svg:svg")
-              .attr("width", w + m[1] + m[3])
-              .attr("height", h + m[0] + m[2]);
-}
-
-/**
-* Create a new group and make the translation to leave room for margins
-*/
-function createCanvasGroup(canvas, translateX, translateY) {
-    return canvas
-          .append("svg:g")
-          .attr("transform", "translate(" + translateX + "," + translateY + ")");
-}
-
-/**
-* Create a new group for the circles-- no translations needed
-*/
-function createCircleGroup(canvas, lineId, translateX, translateY) {
-    return canvas.append("svg:g")
-        .attr("id", "circles-" + lineId);
-}
+          var stack = d3.layout.stack()
+              .offset("zero")
+              .values(function(d) { return d.values; });
 
-/**
-* Given a colorMap, extract the k-ieme color
-*
-* The colormap are standard d3 colormap which swicth to new color every 4 colors;
-* in order to maximize the difference among colors we first get colors that are far apart
-* 
-*/
-function getColor(colorMap, k) {
-    var div = Math.floor(k / 4);
-    var mod = k % 4;
-    var value = div + 4 * mod;
-    return colorMap[value];
-}
-
-/**
-*  Create the color map from the d3 palette
-*/
-function createColorMap(palette) {
-    var colorMap = {}
-    for (var i = 0; i < 20; i++) {
-        colorMap[i] = palette(i);
-    }
-    return colorMap;         
-}
-
-/**
-* Add the list label on the 'legend'
-*/
+          var area = this.createLayerArea(scaleX, scaleY);
 
-function addLabels(linesOrLayers, labelId, colorMap, className) {
-    
-    var $divLabelLayers = $('<div id=' + labelId +'  class=' + className + ' ></div>');
-    var $labelList = $('<ul></ul>');  
-    $divLabelLayers.append($labelList);
-    
-    $("#legend").append($divLabelLayers);
-    for (var i = 0; i < linesOrLayers.length; i++) {
-        addLabel($labelList, getColor(colorMap, i), linesOrLayers[i]);
-    }
-}
-
-/**
-* Add a given label
-*/
-function addLabel(labelList, color, labelName) {
-
-    var $labelListItem =  $('<li style="list-style-type: none">'); 
-    var $divLabelListItem = $('<div id=label-"' + labelName + '" class="swatch" style="background-color:' + color + '"></div>'); 
-    var $spanListItemName = $('<span>' + labelName + '</span> '); 
-     
-    $labelListItem.append($divLabelListItem);
-    $labelListItem.append($spanListItemName);
-     
-    labelList.append($labelListItem);
-}
-
-/**
-* Setup the main divs for both legend and main charts
-*
-* It is expected to have a mnain div anchir on the html with id = 'chartAnchor'.
-*/
-function setupDomStructure() {
-
-    var $divLegend = $('<div id="legend" class="legend">');            
-    var $divLegendText = $('<h1>Killbill Data</h1>');
-     $divLegend.append($divLegendText);
-     
-     var $divChart = $('<div id="charts" class="charts">');
-     var $spanChart = $('<span id="chartId" class="charts"></span>');
-     $divChart.prepend($spanChart);
-     
-     $("#chartAnchor").append($divLegend);    
-     $("#chartAnchor").append($divChart);     
-}
-
-
-/**
-* Attach handlers to all circles so as to display value
-*/
-function addMouseOverCircleForValue() {
-    $('circle').each(function(i) { 
-        
-        var circleGroup = $(this).parent();
-        var circleText = circleGroup.find('text').first();   
-
-        $(this).hover(function() {
-            circleText.show();
-        }, function() {
-            circleText.hide();
-        });
-    });
-}
-
-/**
-* Draw the dashboard based on the data provided.
-*/
-function drawAll(ajaxLinesData, ajaxLayersData) {
-    
-    
-    var colorMapLines = createColorMap(d3.scale.category20b());
-    var colorMapLayers = createColorMap(d3.scale.category20c());
-                  
-    var margins = [80, 80, 20, 80];
-    var width = 1000 - margins[1] - margins[3];
-    var heigth = 800 - margins[0] - margins[2];
-    
-    setupDomStructure();
-    
-    var canvas = createCanvas(margins, width, heigth + (margins[0] + margins[2]) );
-    
-    var lineCanvas = createCanvasGroup(canvas, margins[3], margins[0]);
-    
-    var linesDataValues = [];
-    for (var i = 0; i < ajaxLinesData.length; i++) { linesDataValues.push(ajaxLinesData[i]["values"]); }
-    drawLines(lineCanvas, margins, colorMapLines, ajaxLinesData, width, heigth / 2);
-    
-    var stackCanvas = createCanvasGroup(canvas, margins[3], (heigth / 2) + 2 * margins[0] );
-    drawStackLayers(stackCanvas, margins, colorMapLayers, ajaxLayersData, width, heigth / 2, (heigth + margins[0]));
-    
-    var dataX = extractKeyOrValueFromDataLayer(ajaxLayersData[0], 'x');
-    var scaleX = getScaleDate(dataX, width);
-    
-    var xAxisCanvaGroup = createCanvasGroup(canvas, margins[3], (heigth / 2) + 2 * margins[0]);
-    createXAxis(xAxisCanvaGroup, scaleX, heigth / 2, (heigth + margins[0]));
-    
-    var lines = []
-    for (var i = 0; i < ajaxLinesData.length; i++) { lines.push(ajaxLinesData[i]["name"]) }  
-    addLabels(lines, "labelsLine",  colorMapLines, "labelLines");
-        
-    var layers = [];
-    for (var i = 0; i < ajaxLayersData.length; i++) { layers.push(ajaxLayersData[i]["name"]) }  
-    addLabels(layers, "labelsLayer" , colorMapLayers, "labelLayers");
-      
-    addMouseOverCircleForValue();
- }
-
-/**
-* Ajax call to retrieve the data from the server
-*/
-function doGetData(server, port, analyticsPrefix, endpoint, queryParams, fn) {
-    var request_url = "http://" + server + ":" + port + analyticsPrefix + endpoint;
-    var first = true;
-    if (queryParams) {
-
-        var queryKeys = Object.keys(queryParams);
-        for (var i = 0; i < queryKeys.length; i++) {
-            var curKey = queryKeys[i];
-            var delim = first ? "?" : "&";
-            request_url = request_url + "?" + curKey + "=" +  queryParams[curKey]
-            first = false;
-        }
-    }
-
-    console.log("request_url " + request_url);
-
-    return $.ajax({
-      type: "GET",
-      contentType: "application/json",
-      url: request_url
-    }).done(function(data) {
-       console.log("Done " + request_url + " with data " + data.length);
-       fn(data);
-    });
-}
-
-/**
-* Entry point where we specify server and port to retrieve data can call drawlAll()
-*/
-function fetchDataAndDrawAll(server, port) {
-
-    $().ready(function() {
-
-        var layersData;
-        var linesData;
-
-        $.when(doGetData(server, port, "/plugins/killbill-analytics", "/planTransitionsOverTime", null, function(data) {
-            linesData = data;
-        }), doGetData(server, port, "/plugins/killbill-analytics", "/recurringRevenueOverTime", null, function(data) {
-            layersData = data;
-        })).done(function() {
-              console.log( 'I fire once BOTH ajax requests have completed: linesData = ' + linesData.length);
-              console.log( 'I fire once BOTH ajax requests have completed: layersData = ' + layersData.length);
-              drawAll(linesData, layersData);
-          })
-          .fail(function() {
-            console.log( 'I fire if one or more requests failed.' );
-          });
-    });
-}
+          this.addLayers(stack, area, this.data);
+
+          var dataX = this.extractKeyOrValueFromDataLayer(this.data[0], 'x');
+          var dataY0 = null;
+          for (var i = 0; i < this.data.length; i++) {
+
+              var circleGroup = this.createCircleGroup(this.data[i]['name']);
+              var dataY = this.extractKeyOrValueFromDataLayer(this.data[i], 'y');
+              if (dataY0) {
+                  for (var k = 0; k < dataY.length; k++) {
+                      dataY[k] = dataY[k] + dataY0[k];
+                  }
+              }
+              this.addCirclesForGraph(circleGroup, this.data[i]['name'], dataX, dataY, scaleX, scaleY, this.getColor(i));
+              dataY0 = dataY;
+          }
+
+          this.createYAxis(scaleY);
+     }
+
+  }
+  killbillGraph.KBLayersGraph.prototype = Object.create(killbillGraph.KBGraph.prototype);
+
+
+
+  /**
+  *  KBLayersGraph : Inherits KBGraph abd offers specifities for layered graphs
+  */
+  killbillGraph.KBLinesGraph = function(graphCanvas, data, width, heigth, palette) {
+
+     killbillGraph.KBGraph.call(this, graphCanvas, data, width, heigth, palette);
+
+     /**
+     * Create the 'y' scale for line graphs (non stacked)
+     */
+     this.getScaleValue = function(dataYs, h) {
+          var minValue = 0;
+          var maxValue = 0;
+          for (var i=0; i<dataYs.length; i++) {
+              for (var j = 0; j < dataYs[i].length; j++) {
+                  if (dataYs[i][j] < minValue) {
+                      minValue = dataYs[i][j];
+                  }
+                  if (dataYs[i][j] > maxValue) {
+                      maxValue = dataYs[i][j];
+                  }
+              }
+          }
+          if (minValue > 0) {
+              minValue = 0;
+          }
+          return d3.scale.linear().domain([minValue, maxValue]).range([h, 0]);
+      }
+
+      /**
+      * Add the svg line for this data (dataX, dataY)
+      */
+      this.addLine = function(dataY, scaleX, scaleY, lineColor, lineId) {
+
+          var dataX = this.extractKeyOrValueFromDataLayer(this.data[0], 'x');
+          this.graphCanvas.selectAll("path.line")
+                 .data([dataY])
+                 .enter()
+                 .append("svg:path")
+                 .attr("d", d3.svg.line()
+                     .x(function(d,i) {
+                       return scaleX(new Date(dataX[i]));
+                     })
+                     .y(function(d) {
+                       return scaleY(d);
+                     }))
+                 .attr("id", lineId)
+                 .style("stroke", lineColor);
+
+            var circleGroup = this.createCircleGroup(lineId);
+            this.addCirclesForGraph(circleGroup, lineId, dataX, dataY, scaleX, scaleY, lineColor);
+      }
+
+
+      /**
+      * Draw all lines
+      * It will create its Y axis
+      */
+      this.drawLines = function() {
+
+          var scaleX = this.getScaleDate();
+          var scaleY = this.getYScaleValue(this.data, this.heigth);
+
+          for (var k=0; k<this.data.length; k++) {
+              var dataY = this.extractKeyOrValueFromDataLayer(this.data[k], 'y');
+              this.addLine(dataY, scaleX, scaleY, this.getColor(k), this.data[k]['name']);
+          }
+          this.createYAxis(scaleY);
+       }
+
+  }
+  killbillGraph.KBLinesGraph.prototype = Object.create(killbillGraph.KBGraph.prototype);
+
+
+  killbillGraph.GraphStructure = function() {
+
+      /**
+      * Setup the main divs for both legend and main charts
+      *
+      * It is expected to have a mnain div anchir on the html with id = 'chartAnchor'.
+      */
+      this.setupDomStructure = function() {
+
+          var $divLegend = $('<div id="legend" class="legend">');
+          //var $divLegendText = $('<h1>Killbill Data</h1>');
+          //$divLegend.append($divLegendText);
+
+           var $divChart = $('<div id="charts" class="charts">');
+           var $spanChart = $('<span id="chartId" class="charts"></span>');
+           $divChart.prepend($spanChart);
+
+           $("#chartAnchor").append($divLegend);
+           $("#chartAnchor").append($divChart);
+      }
+
+
+      /**
+      * Create initial canvas on which to draw all graphs
+      */
+      this.createCanvas = function(m, w, h) {
+          return d3.select("#chartId")
+                    .append("svg:svg")
+                    .attr("width", w + m[1] + m[3])
+                    .attr("height", h + m[0] + m[2]);
+      }
+
+
+      /**
+      * Create a new group and make the translation to leave room for margins
+      */
+      this.createCanvasGroup = function(canvas, translateX, translateY) {
+          return canvas
+                .append("svg:g")
+                .attr("transform", "translate(" + translateX + "," + translateY + ")");
+      }
+  };
+
+} (window.killbillGraph = window.killbillGraph || {}, jQuery));
diff --git a/osgi-bundles/bundles/analytics/src/main/resources/static/styles/dashboard.css b/osgi-bundles/bundles/analytics/src/main/resources/static/styles/dashboard.css
index 461f0c0..3bbdc8b 100644
--- a/osgi-bundles/bundles/analytics/src/main/resources/static/styles/dashboard.css
+++ b/osgi-bundles/bundles/analytics/src/main/resources/static/styles/dashboard.css
@@ -13,14 +13,6 @@ display: inline-block;
 vertical-align: top;
 }
 
-.labelLines {
-    margin-top: 200px;
-}
-
-.labelLayers {
-    margin-top: 350px;
-}
-
 .charts {
 display: inline-block;
 position: relative;