diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GadgetDataServiceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/AbstractGadgetDataServiceDAO.java
similarity index 70%
rename from components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GadgetDataServiceDAOImpl.java
rename to components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/AbstractGadgetDataServiceDAO.java
index 641783aceb..bfc8af3f62 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GadgetDataServiceDAOImpl.java
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/AbstractGadgetDataServiceDAO.java
@@ -16,23 +16,25 @@
  * under the License.
  */
 
-package org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl;
+package org.wso2.carbon.device.mgt.analytics.dashboard.dao;
 
 import org.wso2.carbon.context.PrivilegedCarbonContext;
-import org.wso2.carbon.device.mgt.analytics.dashboard.dao.GadgetDataServiceDAO;
-import org.wso2.carbon.device.mgt.analytics.dashboard.dao.GadgetDataServiceDAOFactory;
-import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.*;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.DetailedDeviceEntry;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.DeviceCountByGroupEntry;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.FilterSet;
 import org.wso2.carbon.device.mgt.analytics.dashboard.dao.exception.InvalidParameterValueException;
-import org.wso2.carbon.device.mgt.common.PaginationResult;
 import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
 
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
-public class GadgetDataServiceDAOImpl implements GadgetDataServiceDAO {
+public abstract class AbstractGadgetDataServiceDAO implements GadgetDataServiceDAO {
 
     @Override
     public DeviceCountByGroupEntry getTotalDeviceCount() throws SQLException {
@@ -51,14 +53,15 @@ public class GadgetDataServiceDAOImpl implements GadgetDataServiceDAO {
         return deviceCountByGroupEntry;
     }
 
+    @Override
     public DeviceCountByGroupEntry getDeviceCount(FilterSet filterSet)
                                                   throws InvalidParameterValueException, SQLException {
 
         int filteredDeviceCount = this.getFilteredDeviceCount(filterSet);
 
         DeviceCountByGroupEntry deviceCountByGroupEntry = new DeviceCountByGroupEntry();
-        deviceCountByGroupEntry.setGroup("non-specific");
-        deviceCountByGroupEntry.setDisplayNameForGroup("Non-specific");
+        deviceCountByGroupEntry.setGroup("filtered");
+        deviceCountByGroupEntry.setDisplayNameForGroup("Filtered");
         deviceCountByGroupEntry.setDeviceCount(filteredDeviceCount);
 
         return deviceCountByGroupEntry;
@@ -161,8 +164,8 @@ public class GadgetDataServiceDAOImpl implements GadgetDataServiceDAO {
         }
 
         DeviceCountByGroupEntry deviceCountByGroupEntry = new DeviceCountByGroupEntry();
-        deviceCountByGroupEntry.setGroup("feature-non-compliant");
-        deviceCountByGroupEntry.setDisplayNameForGroup("Feature-non-compliant");
+        deviceCountByGroupEntry.setGroup("feature-non-compliant-and-filtered");
+        deviceCountByGroupEntry.setDisplayNameForGroup("Feature-non-compliant-and-filtered");
         deviceCountByGroupEntry.setDeviceCount(filteredDeviceCount);
 
         return deviceCountByGroupEntry;
@@ -240,66 +243,6 @@ public class GadgetDataServiceDAOImpl implements GadgetDataServiceDAO {
         }
     }
 
-    @Override
-    public PaginationResult getNonCompliantDeviceCountsByFeatures(int startIndex, int resultCount)
-                                                                  throws InvalidParameterValueException, SQLException {
-
-        if (startIndex < 0) {
-            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
-        }
-
-        if (resultCount < 5) {
-            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
-        }
-
-        Connection con;
-        PreparedStatement stmt = null;
-        ResultSet rs = null;
-        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
-        List<DeviceCountByGroupEntry> filteredNonCompliantDeviceCountsByFeatures = new ArrayList<>();
-        int totalRecordsCount = 0;
-        try {
-            con = this.getConnection();
-            String sql = "SELECT FEATURE_CODE, COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_2 " +
-                "WHERE TENANT_ID = ? GROUP BY FEATURE_CODE ORDER BY DEVICE_COUNT DESC LIMIT ?, ?";
-            stmt = con.prepareStatement(sql);
-            stmt.setInt(1, tenantId);
-            stmt.setInt(2, startIndex);
-            stmt.setInt(3, resultCount);
-
-            // executing query
-            rs = stmt.executeQuery();
-            // fetching query results
-            DeviceCountByGroupEntry filteredNonCompliantDeviceCountByFeature;
-            while (rs.next()) {
-                filteredNonCompliantDeviceCountByFeature = new DeviceCountByGroupEntry();
-                filteredNonCompliantDeviceCountByFeature.setGroup(rs.getString("FEATURE_CODE"));
-                filteredNonCompliantDeviceCountByFeature.setDisplayNameForGroup(rs.getString("FEATURE_CODE"));
-                filteredNonCompliantDeviceCountByFeature.setDeviceCount(rs.getInt("DEVICE_COUNT"));
-                filteredNonCompliantDeviceCountsByFeatures.add(filteredNonCompliantDeviceCountByFeature);
-            }
-            // fetching total records count
-            sql = "SELECT COUNT(FEATURE_CODE) AS NON_COMPLIANT_FEATURE_COUNT FROM " +
-                "(SELECT DISTINCT FEATURE_CODE FROM DEVICES_VIEW_2 WHERE TENANT_ID = ?)";
-
-            stmt = con.prepareStatement(sql);
-            stmt.setInt(1, tenantId);
-
-            // executing query
-            rs = stmt.executeQuery();
-            // fetching query results
-            while (rs.next()) {
-                totalRecordsCount = rs.getInt("NON_COMPLIANT_FEATURE_COUNT");
-            }
-        } finally {
-            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
-        }
-        PaginationResult paginationResult = new PaginationResult();
-        paginationResult.setData(filteredNonCompliantDeviceCountsByFeatures);
-        paginationResult.setRecordsTotal(totalRecordsCount);
-        return paginationResult;
-    }
-
     @Override
     public List<DeviceCountByGroupEntry> getDeviceCountsByPlatforms(FilterSet filterSet)
                                                            throws InvalidParameterValueException, SQLException {
@@ -527,184 +470,6 @@ public class GadgetDataServiceDAOImpl implements GadgetDataServiceDAO {
         return filteredDeviceCountsByOwnershipTypes;
     }
 
-    @Override
-    public PaginationResult getDevicesWithDetails(FilterSet filterSet, int startIndex, int resultCount)
-                                                                throws InvalidParameterValueException, SQLException {
-
-        if (startIndex < 0) {
-            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
-        }
-
-        if (resultCount < 5) {
-            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
-        }
-
-        Map<String, Object> filters = this.extractDatabaseFiltersFromBean(filterSet);
-
-        Connection con;
-        PreparedStatement stmt = null;
-        ResultSet rs = null;
-        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
-        List<DetailedDeviceEntry> filteredDevicesWithDetails = new ArrayList<>();
-        int totalRecordsCount = 0;
-        try {
-            con = this.getConnection();
-            String sql, advancedSqlFiltering = "";
-            // appending filters if exist, to support advanced filtering options
-            // [1] appending filter columns, if exist
-            if (filters != null && filters.size() > 0) {
-                for (String column : filters.keySet()) {
-                    advancedSqlFiltering = advancedSqlFiltering + "AND " + column + " = ? ";
-                }
-            }
-            sql = "SELECT DEVICE_ID, PLATFORM, OWNERSHIP, CONNECTIVITY_STATUS FROM DEVICES_VIEW_1 " +
-                  "WHERE TENANT_ID = ? " + advancedSqlFiltering + "ORDER BY DEVICE_ID ASC LIMIT ?, ?";
-            stmt = con.prepareStatement(sql);
-            // [2] appending filter column values, if exist
-            stmt.setInt(1, tenantId);
-            if (filters != null && filters.values().size() > 0) {
-                int i = 2;
-                for (Object value : filters.values()) {
-                    if (value instanceof Integer) {
-                        stmt.setInt(i, (Integer) value);
-                    } else if (value instanceof String) {
-                        stmt.setString(i, (String) value);
-                    }
-                    i++;
-                }
-                stmt.setInt(i, startIndex);
-                stmt.setInt(++i, resultCount);
-            } else {
-                stmt.setInt(2, startIndex);
-                stmt.setInt(3, resultCount);
-            }
-            // executing query
-            rs = stmt.executeQuery();
-            // fetching query results
-            DetailedDeviceEntry filteredDeviceWithDetails;
-            while (rs.next()) {
-                filteredDeviceWithDetails = new DetailedDeviceEntry();
-                filteredDeviceWithDetails.setDeviceId(rs.getInt("DEVICE_ID"));
-                filteredDeviceWithDetails.setPlatform(rs.getString("PLATFORM"));
-                filteredDeviceWithDetails.setOwnershipType(rs.getString("OWNERSHIP"));
-                filteredDeviceWithDetails.setConnectivityStatus(rs.getString("CONNECTIVITY_STATUS"));
-                filteredDevicesWithDetails.add(filteredDeviceWithDetails);
-            }
-
-            // fetching total records count
-            sql = "SELECT COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_1 WHERE TENANT_ID = ?";
-
-            stmt = con.prepareStatement(sql);
-            stmt.setInt(1, tenantId);
-
-            // executing query
-            rs = stmt.executeQuery();
-            // fetching query results
-            while (rs.next()) {
-                totalRecordsCount = rs.getInt("DEVICE_COUNT");
-            }
-        } finally {
-            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
-        }
-        PaginationResult paginationResult = new PaginationResult();
-        paginationResult.setData(filteredDevicesWithDetails);
-        paginationResult.setRecordsTotal(totalRecordsCount);
-        return paginationResult;
-    }
-
-    @Override
-    public PaginationResult getFeatureNonCompliantDevicesWithDetails(String nonCompliantFeatureCode,
-                                                        FilterSet filterSet, int startIndex, int resultCount)
-                                                                throws InvalidParameterValueException, SQLException {
-
-        if (nonCompliantFeatureCode == null || "".equals(nonCompliantFeatureCode)) {
-            throw new InvalidParameterValueException("Non-compliant feature code should not be either null or empty.");
-        }
-
-        if (startIndex < 0) {
-            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
-        }
-
-        if (resultCount < 5) {
-            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
-        }
-
-        Map<String, Object> filters = this.extractDatabaseFiltersFromBean(filterSet);
-
-        Connection con;
-        PreparedStatement stmt = null;
-        ResultSet rs = null;
-        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
-        List<DetailedDeviceEntry> filteredDevicesWithDetails = new ArrayList<>();
-        int totalRecordsCount = 0;
-        try {
-            con = this.getConnection();
-            String sql, advancedSqlFiltering = "";
-            // appending filters if exist, to support advanced filtering options
-            // [1] appending filter columns, if exist
-            if (filters != null && filters.size() > 0) {
-                for (String column : filters.keySet()) {
-                    advancedSqlFiltering = advancedSqlFiltering + "AND " + column + " = ? ";
-                }
-            }
-            sql = "SELECT DEVICE_ID, PLATFORM, OWNERSHIP, CONNECTIVITY_STATUS FROM DEVICES_VIEW_2 " +
-                  "WHERE TENANT_ID = ? AND FEATURE_CODE = ? " + advancedSqlFiltering + "ORDER BY DEVICE_ID ASC LIMIT ?, ?";
-            stmt = con.prepareStatement(sql);
-            // [2] appending filter column values, if exist
-            stmt.setInt(1, tenantId);
-            stmt.setString(2, nonCompliantFeatureCode);
-            if (filters != null && filters.values().size() > 0) {
-                int i = 3;
-                for (Object value : filters.values()) {
-                    if (value instanceof Integer) {
-                        stmt.setInt(i, (Integer) value);
-                    } else if (value instanceof String) {
-                        stmt.setString(i, (String) value);
-                    }
-                    i++;
-                }
-                stmt.setInt(i, startIndex);
-                stmt.setInt(++i, resultCount);
-            } else {
-                stmt.setInt(3, startIndex);
-                stmt.setInt(4, resultCount);
-            }
-            // executing query
-            rs = stmt.executeQuery();
-            // fetching query results
-            DetailedDeviceEntry filteredDeviceWithDetails;
-            while (rs.next()) {
-                filteredDeviceWithDetails = new DetailedDeviceEntry();
-                filteredDeviceWithDetails.setDeviceId(rs.getInt("DEVICE_ID"));
-                filteredDeviceWithDetails.setPlatform(rs.getString("PLATFORM"));
-                filteredDeviceWithDetails.setOwnershipType(rs.getString("OWNERSHIP"));
-                filteredDeviceWithDetails.setConnectivityStatus(rs.getString("CONNECTIVITY_STATUS"));
-                filteredDevicesWithDetails.add(filteredDeviceWithDetails);
-            }
-
-            // fetching total records count
-            sql = "SELECT COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_2 " +
-                  "WHERE TENANT_ID = ? AND FEATURE_CODE = ?";
-
-            stmt = con.prepareStatement(sql);
-            stmt.setInt(1, tenantId);
-            stmt.setString(2, nonCompliantFeatureCode);
-
-            // executing query
-            rs = stmt.executeQuery();
-            // fetching query results
-            while (rs.next()) {
-                totalRecordsCount = rs.getInt("DEVICE_COUNT");
-            }
-        } finally {
-            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
-        }
-        PaginationResult paginationResult = new PaginationResult();
-        paginationResult.setData(filteredDevicesWithDetails);
-        paginationResult.setRecordsTotal(totalRecordsCount);
-        return paginationResult;
-    }
-
     @Override
     public List<DetailedDeviceEntry> getDevicesWithDetails(FilterSet filterSet)
                                             throws InvalidParameterValueException, SQLException {
@@ -819,7 +584,7 @@ public class GadgetDataServiceDAOImpl implements GadgetDataServiceDAO {
         return filteredDevicesWithDetails;
     }
 
-    private Map<String, Object> extractDatabaseFiltersFromBean(FilterSet filterSet)
+    protected Map<String, Object> extractDatabaseFiltersFromBean(FilterSet filterSet)
                                                         throws InvalidParameterValueException {
         if (filterSet == null) {
             return null;
@@ -862,7 +627,7 @@ public class GadgetDataServiceDAOImpl implements GadgetDataServiceDAO {
         return filters;
     }
 
-    private Connection getConnection() throws SQLException {
+    protected Connection getConnection() throws SQLException {
         return GadgetDataServiceDAOFactory.getConnection();
     }
 
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GadgetDataServiceDAOConstants.java b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/GadgetDataServiceDAOConstants.java
similarity index 87%
rename from components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GadgetDataServiceDAOConstants.java
rename to components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/GadgetDataServiceDAOConstants.java
index 9caad2f3a8..82ca1b9594 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GadgetDataServiceDAOConstants.java
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/GadgetDataServiceDAOConstants.java
@@ -16,14 +16,14 @@
  * under the License.
  */
 
-package org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl;
+package org.wso2.carbon.device.mgt.analytics.dashboard.dao;
 
 public final class GadgetDataServiceDAOConstants {
 
     public static class PotentialVulnerability {
 
         // These constants do not hold actual database values
-        // These are just logical values defined and used @ Gadget Data Service DAO Implementation layer
+        // These are just abstract values defined and used @ Gadget Data Service DAO Implementation layer
         public static final String NON_COMPLIANT = "NON_COMPLIANT";
         public static final String UNMONITORED = "UNMONITORED";
 
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/GadgetDataServiceDAOFactory.java b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/GadgetDataServiceDAOFactory.java
index c5a6097f3f..5ee595424f 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/GadgetDataServiceDAOFactory.java
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/GadgetDataServiceDAOFactory.java
@@ -20,8 +20,12 @@ package org.wso2.carbon.device.mgt.analytics.dashboard.dao;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl.GadgetDataServiceDAOImpl;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl.GenericGadgetDataServiceDAOImpl;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl.OracleGadgetDataServiceDAOImpl;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl.PostgreSQLGadgetDataServiceDAOImpl;
+import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
 import org.wso2.carbon.device.mgt.common.IllegalTransactionStateException;
+import org.wso2.carbon.device.mgt.common.UnsupportedDatabaseEngineException;
 import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig;
 import org.wso2.carbon.device.mgt.core.config.datasource.JNDILookupDefinition;
 import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
@@ -41,7 +45,23 @@ public class GadgetDataServiceDAOFactory {
     private static ThreadLocal<Connection> currentConnection = new ThreadLocal<>();
 
     public static GadgetDataServiceDAO getGadgetDataServiceDAO() {
-        return new GadgetDataServiceDAOImpl();
+        if (databaseEngine != null) {
+            switch (databaseEngine) {
+                case DeviceManagementConstants.DataBaseTypes.DB_TYPE_H2:
+                    return new GenericGadgetDataServiceDAOImpl();
+                case DeviceManagementConstants.DataBaseTypes.DB_TYPE_MYSQL:
+                    return new GenericGadgetDataServiceDAOImpl();
+                case DeviceManagementConstants.DataBaseTypes.DB_TYPE_MSSQL:
+                    // to be added
+                case DeviceManagementConstants.DataBaseTypes.DB_TYPE_POSTGRESQL:
+                    return new PostgreSQLGadgetDataServiceDAOImpl();
+                case DeviceManagementConstants.DataBaseTypes.DB_TYPE_ORACLE:
+                    return new OracleGadgetDataServiceDAOImpl();
+                default:
+                    throw new UnsupportedDatabaseEngineException("Unsupported database engine : " + databaseEngine);
+            }
+        }
+        throw new IllegalStateException("Database engine has not initialized properly.");
     }
 
     public static void init(DataSourceConfig config) {
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GenericGadgetDataServiceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GenericGadgetDataServiceDAOImpl.java
new file mode 100644
index 0000000000..caa6f9bb25
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/GenericGadgetDataServiceDAOImpl.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * you may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl;
+
+import org.wso2.carbon.context.PrivilegedCarbonContext;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.AbstractGadgetDataServiceDAO;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.DetailedDeviceEntry;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.DeviceCountByGroupEntry;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.FilterSet;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.exception.InvalidParameterValueException;
+import org.wso2.carbon.device.mgt.common.PaginationResult;
+import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class GenericGadgetDataServiceDAOImpl extends AbstractGadgetDataServiceDAO {
+
+    @Override
+    public PaginationResult getNonCompliantDeviceCountsByFeatures(int startIndex, int resultCount)
+                                                    throws InvalidParameterValueException, SQLException {
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DeviceCountByGroupEntry> filteredNonCompliantDeviceCountsByFeatures = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql = "SELECT FEATURE_CODE, COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_2 " +
+                "WHERE TENANT_ID = ? GROUP BY FEATURE_CODE ORDER BY DEVICE_COUNT DESC LIMIT ?, ?";
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+            stmt.setInt(2, startIndex);
+            stmt.setInt(3, resultCount);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DeviceCountByGroupEntry filteredNonCompliantDeviceCountByFeature;
+            while (rs.next()) {
+                filteredNonCompliantDeviceCountByFeature = new DeviceCountByGroupEntry();
+                filteredNonCompliantDeviceCountByFeature.setGroup(rs.getString("FEATURE_CODE"));
+                filteredNonCompliantDeviceCountByFeature.setDisplayNameForGroup(rs.getString("FEATURE_CODE"));
+                filteredNonCompliantDeviceCountByFeature.setDeviceCount(rs.getInt("DEVICE_COUNT"));
+                filteredNonCompliantDeviceCountsByFeatures.add(filteredNonCompliantDeviceCountByFeature);
+            }
+            // fetching total records count
+            sql = "SELECT COUNT(FEATURE_CODE) AS NON_COMPLIANT_FEATURE_COUNT FROM " +
+                "(SELECT DISTINCT FEATURE_CODE FROM DEVICES_VIEW_2 WHERE TENANT_ID = ?) NON_COMPLIANT_FEATURE_CODE";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("NON_COMPLIANT_FEATURE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredNonCompliantDeviceCountsByFeatures);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+    @Override
+    public PaginationResult getDevicesWithDetails(FilterSet filterSet, int startIndex, int resultCount)
+            throws InvalidParameterValueException, SQLException {
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Map<String, Object> filters = this.extractDatabaseFiltersFromBean(filterSet);
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DetailedDeviceEntry> filteredDevicesWithDetails = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql, advancedSqlFiltering = "";
+            // appending filters if exist, to support advanced filtering options
+            // [1] appending filter columns, if exist
+            if (filters != null && filters.size() > 0) {
+                for (String column : filters.keySet()) {
+                    advancedSqlFiltering = advancedSqlFiltering + "AND " + column + " = ? ";
+                }
+            }
+            sql = "SELECT DEVICE_ID, PLATFORM, OWNERSHIP, CONNECTIVITY_STATUS FROM DEVICES_VIEW_1 " +
+                "WHERE TENANT_ID = ? " + advancedSqlFiltering + "ORDER BY DEVICE_ID ASC LIMIT ?, ?";
+            stmt = con.prepareStatement(sql);
+            // [2] appending filter column values, if exist
+            stmt.setInt(1, tenantId);
+            if (filters != null && filters.values().size() > 0) {
+                int i = 2;
+                for (Object value : filters.values()) {
+                    if (value instanceof Integer) {
+                        stmt.setInt(i, (Integer) value);
+                    } else if (value instanceof String) {
+                        stmt.setString(i, (String) value);
+                    }
+                    i++;
+                }
+                stmt.setInt(i, startIndex);
+                stmt.setInt(++i, resultCount);
+            } else {
+                stmt.setInt(2, startIndex);
+                stmt.setInt(3, resultCount);
+            }
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DetailedDeviceEntry filteredDeviceWithDetails;
+            while (rs.next()) {
+                filteredDeviceWithDetails = new DetailedDeviceEntry();
+                filteredDeviceWithDetails.setDeviceId(rs.getInt("DEVICE_ID"));
+                filteredDeviceWithDetails.setPlatform(rs.getString("PLATFORM"));
+                filteredDeviceWithDetails.setOwnershipType(rs.getString("OWNERSHIP"));
+                filteredDeviceWithDetails.setConnectivityStatus(rs.getString("CONNECTIVITY_STATUS"));
+                filteredDevicesWithDetails.add(filteredDeviceWithDetails);
+            }
+
+            // fetching total records count
+            sql = "SELECT COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_1 WHERE TENANT_ID = ?";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("DEVICE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredDevicesWithDetails);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+    @Override
+    public PaginationResult getFeatureNonCompliantDevicesWithDetails(String nonCompliantFeatureCode,
+                                                         FilterSet filterSet, int startIndex, int resultCount)
+                                                                  throws InvalidParameterValueException, SQLException {
+
+        if (nonCompliantFeatureCode == null || "".equals(nonCompliantFeatureCode)) {
+            throw new InvalidParameterValueException("Non-compliant feature code should not be either null or empty.");
+        }
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Map<String, Object> filters = this.extractDatabaseFiltersFromBean(filterSet);
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DetailedDeviceEntry> filteredDevicesWithDetails = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql, advancedSqlFiltering = "";
+            // appending filters if exist, to support advanced filtering options
+            // [1] appending filter columns, if exist
+            if (filters != null && filters.size() > 0) {
+                for (String column : filters.keySet()) {
+                    advancedSqlFiltering = advancedSqlFiltering + "AND " + column + " = ? ";
+                }
+            }
+            sql = "SELECT DEVICE_ID, PLATFORM, OWNERSHIP, CONNECTIVITY_STATUS FROM DEVICES_VIEW_2 " +
+                "WHERE TENANT_ID = ? AND FEATURE_CODE = ? " + advancedSqlFiltering +
+                    "ORDER BY DEVICE_ID ASC LIMIT ?, ?";
+            stmt = con.prepareStatement(sql);
+            // [2] appending filter column values, if exist
+            stmt.setInt(1, tenantId);
+            stmt.setString(2, nonCompliantFeatureCode);
+            if (filters != null && filters.values().size() > 0) {
+                int i = 3;
+                for (Object value : filters.values()) {
+                    if (value instanceof Integer) {
+                        stmt.setInt(i, (Integer) value);
+                    } else if (value instanceof String) {
+                        stmt.setString(i, (String) value);
+                    }
+                    i++;
+                }
+                stmt.setInt(i, startIndex);
+                stmt.setInt(++i, resultCount);
+            } else {
+                stmt.setInt(3, startIndex);
+                stmt.setInt(4, resultCount);
+            }
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DetailedDeviceEntry filteredDeviceWithDetails;
+            while (rs.next()) {
+                filteredDeviceWithDetails = new DetailedDeviceEntry();
+                filteredDeviceWithDetails.setDeviceId(rs.getInt("DEVICE_ID"));
+                filteredDeviceWithDetails.setPlatform(rs.getString("PLATFORM"));
+                filteredDeviceWithDetails.setOwnershipType(rs.getString("OWNERSHIP"));
+                filteredDeviceWithDetails.setConnectivityStatus(rs.getString("CONNECTIVITY_STATUS"));
+                filteredDevicesWithDetails.add(filteredDeviceWithDetails);
+            }
+
+            // fetching total records count
+            sql = "SELECT COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_2 " +
+                "WHERE TENANT_ID = ? AND FEATURE_CODE = ?";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+            stmt.setString(2, nonCompliantFeatureCode);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("DEVICE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredDevicesWithDetails);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/OracleGadgetDataServiceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/OracleGadgetDataServiceDAOImpl.java
new file mode 100644
index 0000000000..b82ea38a30
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/OracleGadgetDataServiceDAOImpl.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * you may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl;
+
+import org.wso2.carbon.context.PrivilegedCarbonContext;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.AbstractGadgetDataServiceDAO;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.DetailedDeviceEntry;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.DeviceCountByGroupEntry;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.FilterSet;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.exception.InvalidParameterValueException;
+import org.wso2.carbon.device.mgt.common.PaginationResult;
+import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class OracleGadgetDataServiceDAOImpl extends AbstractGadgetDataServiceDAO {
+
+    @Override
+    public PaginationResult getNonCompliantDeviceCountsByFeatures(int startIndex, int resultCount)
+            throws InvalidParameterValueException, SQLException {
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DeviceCountByGroupEntry> filteredNonCompliantDeviceCountsByFeatures = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql = "SELECT * FROM (SELECT ROWNUM offset, rs.* FROM (SELECT FEATURE_CODE, COUNT(DEVICE_ID) " +
+                "AS DEVICE_COUNT FROM DEVICES_VIEW_2 WHERE TENANT_ID = ? GROUP BY FEATURE_CODE ORDER BY " +
+                    "DEVICE_COUNT DESC) rs) WHERE offset >= ? AND ROWNUM <= ?";
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+            stmt.setInt(2, startIndex);
+            stmt.setInt(3, resultCount);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DeviceCountByGroupEntry filteredNonCompliantDeviceCountByFeature;
+            while (rs.next()) {
+                filteredNonCompliantDeviceCountByFeature = new DeviceCountByGroupEntry();
+                filteredNonCompliantDeviceCountByFeature.setGroup(rs.getString("FEATURE_CODE"));
+                filteredNonCompliantDeviceCountByFeature.setDisplayNameForGroup(rs.getString("FEATURE_CODE"));
+                filteredNonCompliantDeviceCountByFeature.setDeviceCount(rs.getInt("DEVICE_COUNT"));
+                filteredNonCompliantDeviceCountsByFeatures.add(filteredNonCompliantDeviceCountByFeature);
+            }
+            // fetching total records count
+            sql = "SELECT COUNT(FEATURE_CODE) AS NON_COMPLIANT_FEATURE_COUNT FROM " +
+                    "(SELECT DISTINCT FEATURE_CODE FROM DEVICES_VIEW_2 WHERE TENANT_ID = ?) NON_COMPLIANT_FEATURE_CODE";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("NON_COMPLIANT_FEATURE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredNonCompliantDeviceCountsByFeatures);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+    @Override
+    public PaginationResult getDevicesWithDetails(FilterSet filterSet, int startIndex, int resultCount)
+            throws InvalidParameterValueException, SQLException {
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Map<String, Object> filters = this.extractDatabaseFiltersFromBean(filterSet);
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DetailedDeviceEntry> filteredDevicesWithDetails = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql, advancedSqlFiltering = "";
+            // appending filters if exist, to support advanced filtering options
+            // [1] appending filter columns, if exist
+            if (filters != null && filters.size() > 0) {
+                for (String column : filters.keySet()) {
+                    advancedSqlFiltering = advancedSqlFiltering + "AND " + column + " = ? ";
+                }
+            }
+            sql = "SELECT * FROM (SELECT ROWNUM offset, rs.* FROM (SELECT DEVICE_ID, PLATFORM, OWNERSHIP, " +
+                "CONNECTIVITY_STATUS FROM DEVICES_VIEW_1 WHERE TENANT_ID = ? " + advancedSqlFiltering +
+                    "ORDER BY DEVICE_ID ASC) rs) WHERE offset >= ? AND ROWNUM <= ?";
+            stmt = con.prepareStatement(sql);
+            // [2] appending filter column values, if exist
+            stmt.setInt(1, tenantId);
+            if (filters != null && filters.values().size() > 0) {
+                int i = 2;
+                for (Object value : filters.values()) {
+                    if (value instanceof Integer) {
+                        stmt.setInt(i, (Integer) value);
+                    } else if (value instanceof String) {
+                        stmt.setString(i, (String) value);
+                    }
+                    i++;
+                }
+                stmt.setInt(i, startIndex);
+                stmt.setInt(++i, resultCount);
+            } else {
+                stmt.setInt(2, startIndex);
+                stmt.setInt(3, resultCount);
+            }
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DetailedDeviceEntry filteredDeviceWithDetails;
+            while (rs.next()) {
+                filteredDeviceWithDetails = new DetailedDeviceEntry();
+                filteredDeviceWithDetails.setDeviceId(rs.getInt("DEVICE_ID"));
+                filteredDeviceWithDetails.setPlatform(rs.getString("PLATFORM"));
+                filteredDeviceWithDetails.setOwnershipType(rs.getString("OWNERSHIP"));
+                filteredDeviceWithDetails.setConnectivityStatus(rs.getString("CONNECTIVITY_STATUS"));
+                filteredDevicesWithDetails.add(filteredDeviceWithDetails);
+            }
+
+            // fetching total records count
+            sql = "SELECT COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_1 WHERE TENANT_ID = ?";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("DEVICE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredDevicesWithDetails);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+    @Override
+    public PaginationResult getFeatureNonCompliantDevicesWithDetails(String nonCompliantFeatureCode,
+                                                                     FilterSet filterSet, int startIndex, int resultCount)
+            throws InvalidParameterValueException, SQLException {
+
+        if (nonCompliantFeatureCode == null || "".equals(nonCompliantFeatureCode)) {
+            throw new InvalidParameterValueException("Non-compliant feature code should not be either null or empty.");
+        }
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Map<String, Object> filters = this.extractDatabaseFiltersFromBean(filterSet);
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DetailedDeviceEntry> filteredDevicesWithDetails = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql, advancedSqlFiltering = "";
+            // appending filters if exist, to support advanced filtering options
+            // [1] appending filter columns, if exist
+            if (filters != null && filters.size() > 0) {
+                for (String column : filters.keySet()) {
+                    advancedSqlFiltering = advancedSqlFiltering + "AND " + column + " = ? ";
+                }
+            }
+            sql = "SELECT * FROM (SELECT ROWNUM offset, rs.* FROM (SELECT DEVICE_ID, PLATFORM, OWNERSHIP, " +
+                "CONNECTIVITY_STATUS FROM DEVICES_VIEW_2 WHERE TENANT_ID = ? AND FEATURE_CODE = ? " +
+                    advancedSqlFiltering + "ORDER BY DEVICE_ID ASC) rs) WHERE offset >= ? AND ROWNUM <= ?";
+            stmt = con.prepareStatement(sql);
+            // [2] appending filter column values, if exist
+            stmt.setInt(1, tenantId);
+            stmt.setString(2, nonCompliantFeatureCode);
+            if (filters != null && filters.values().size() > 0) {
+                int i = 3;
+                for (Object value : filters.values()) {
+                    if (value instanceof Integer) {
+                        stmt.setInt(i, (Integer) value);
+                    } else if (value instanceof String) {
+                        stmt.setString(i, (String) value);
+                    }
+                    i++;
+                }
+                stmt.setInt(i, startIndex);
+                stmt.setInt(++i, resultCount);
+            } else {
+                stmt.setInt(3, startIndex);
+                stmt.setInt(4, resultCount);
+            }
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DetailedDeviceEntry filteredDeviceWithDetails;
+            while (rs.next()) {
+                filteredDeviceWithDetails = new DetailedDeviceEntry();
+                filteredDeviceWithDetails.setDeviceId(rs.getInt("DEVICE_ID"));
+                filteredDeviceWithDetails.setPlatform(rs.getString("PLATFORM"));
+                filteredDeviceWithDetails.setOwnershipType(rs.getString("OWNERSHIP"));
+                filteredDeviceWithDetails.setConnectivityStatus(rs.getString("CONNECTIVITY_STATUS"));
+                filteredDevicesWithDetails.add(filteredDeviceWithDetails);
+            }
+
+            // fetching total records count
+            sql = "SELECT COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_2 " +
+                "WHERE TENANT_ID = ? AND FEATURE_CODE = ?";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+            stmt.setString(2, nonCompliantFeatureCode);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("DEVICE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredDevicesWithDetails);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/PostgreSQLGadgetDataServiceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/PostgreSQLGadgetDataServiceDAOImpl.java
new file mode 100644
index 0000000000..778f4b453e
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.analytics.dashboard/src/main/java/org/wso2/carbon/device/mgt/analytics/dashboard/dao/impl/PostgreSQLGadgetDataServiceDAOImpl.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * you may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.device.mgt.analytics.dashboard.dao.impl;
+
+import org.wso2.carbon.context.PrivilegedCarbonContext;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.AbstractGadgetDataServiceDAO;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.DetailedDeviceEntry;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.DeviceCountByGroupEntry;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.bean.FilterSet;
+import org.wso2.carbon.device.mgt.analytics.dashboard.dao.exception.InvalidParameterValueException;
+import org.wso2.carbon.device.mgt.common.PaginationResult;
+import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class PostgreSQLGadgetDataServiceDAOImpl extends AbstractGadgetDataServiceDAO {
+
+    @Override
+    public PaginationResult getNonCompliantDeviceCountsByFeatures(int startIndex, int resultCount)
+            throws InvalidParameterValueException, SQLException {
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DeviceCountByGroupEntry> filteredNonCompliantDeviceCountsByFeatures = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql = "SELECT FEATURE_CODE, COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_2 " +
+                "WHERE TENANT_ID = ? GROUP BY FEATURE_CODE ORDER BY DEVICE_COUNT DESC OFFSET ? LIMIT ?";
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+            stmt.setInt(2, startIndex);
+            stmt.setInt(3, resultCount);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DeviceCountByGroupEntry filteredNonCompliantDeviceCountByFeature;
+            while (rs.next()) {
+                filteredNonCompliantDeviceCountByFeature = new DeviceCountByGroupEntry();
+                filteredNonCompliantDeviceCountByFeature.setGroup(rs.getString("FEATURE_CODE"));
+                filteredNonCompliantDeviceCountByFeature.setDisplayNameForGroup(rs.getString("FEATURE_CODE"));
+                filteredNonCompliantDeviceCountByFeature.setDeviceCount(rs.getInt("DEVICE_COUNT"));
+                filteredNonCompliantDeviceCountsByFeatures.add(filteredNonCompliantDeviceCountByFeature);
+            }
+            // fetching total records count
+            sql = "SELECT COUNT(FEATURE_CODE) AS NON_COMPLIANT_FEATURE_COUNT FROM " +
+                "(SELECT DISTINCT FEATURE_CODE FROM DEVICES_VIEW_2 WHERE TENANT_ID = ?) NON_COMPLIANT_FEATURE_CODE";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("NON_COMPLIANT_FEATURE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredNonCompliantDeviceCountsByFeatures);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+    @Override
+    public PaginationResult getDevicesWithDetails(FilterSet filterSet, int startIndex, int resultCount)
+            throws InvalidParameterValueException, SQLException {
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Map<String, Object> filters = this.extractDatabaseFiltersFromBean(filterSet);
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DetailedDeviceEntry> filteredDevicesWithDetails = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql, advancedSqlFiltering = "";
+            // appending filters if exist, to support advanced filtering options
+            // [1] appending filter columns, if exist
+            if (filters != null && filters.size() > 0) {
+                for (String column : filters.keySet()) {
+                    advancedSqlFiltering = advancedSqlFiltering + "AND " + column + " = ? ";
+                }
+            }
+            sql = "SELECT DEVICE_ID, PLATFORM, OWNERSHIP, CONNECTIVITY_STATUS FROM DEVICES_VIEW_1 " +
+                "WHERE TENANT_ID = ? " + advancedSqlFiltering + "ORDER BY DEVICE_ID ASC OFFSET ? LIMIT ?";
+            stmt = con.prepareStatement(sql);
+            // [2] appending filter column values, if exist
+            stmt.setInt(1, tenantId);
+            if (filters != null && filters.values().size() > 0) {
+                int i = 2;
+                for (Object value : filters.values()) {
+                    if (value instanceof Integer) {
+                        stmt.setInt(i, (Integer) value);
+                    } else if (value instanceof String) {
+                        stmt.setString(i, (String) value);
+                    }
+                    i++;
+                }
+                stmt.setInt(i, startIndex);
+                stmt.setInt(++i, resultCount);
+            } else {
+                stmt.setInt(2, startIndex);
+                stmt.setInt(3, resultCount);
+            }
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DetailedDeviceEntry filteredDeviceWithDetails;
+            while (rs.next()) {
+                filteredDeviceWithDetails = new DetailedDeviceEntry();
+                filteredDeviceWithDetails.setDeviceId(rs.getInt("DEVICE_ID"));
+                filteredDeviceWithDetails.setPlatform(rs.getString("PLATFORM"));
+                filteredDeviceWithDetails.setOwnershipType(rs.getString("OWNERSHIP"));
+                filteredDeviceWithDetails.setConnectivityStatus(rs.getString("CONNECTIVITY_STATUS"));
+                filteredDevicesWithDetails.add(filteredDeviceWithDetails);
+            }
+
+            // fetching total records count
+            sql = "SELECT COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_1 WHERE TENANT_ID = ?";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("DEVICE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredDevicesWithDetails);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+    @Override
+    public PaginationResult getFeatureNonCompliantDevicesWithDetails(String nonCompliantFeatureCode,
+                                                        FilterSet filterSet, int startIndex, int resultCount)
+                                                                throws InvalidParameterValueException, SQLException {
+
+        if (nonCompliantFeatureCode == null || "".equals(nonCompliantFeatureCode)) {
+            throw new InvalidParameterValueException("Non-compliant feature code should not be either null or empty.");
+        }
+
+        if (startIndex < 0) {
+            throw new InvalidParameterValueException("Start index should be equal to 0 or greater than that.");
+        }
+
+        if (resultCount < 5) {
+            throw new InvalidParameterValueException("Result count should be equal to 5 or greater than that.");
+        }
+
+        Map<String, Object> filters = this.extractDatabaseFiltersFromBean(filterSet);
+
+        Connection con;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+        List<DetailedDeviceEntry> filteredDevicesWithDetails = new ArrayList<>();
+        int totalRecordsCount = 0;
+        try {
+            con = this.getConnection();
+            String sql, advancedSqlFiltering = "";
+            // appending filters if exist, to support advanced filtering options
+            // [1] appending filter columns, if exist
+            if (filters != null && filters.size() > 0) {
+                for (String column : filters.keySet()) {
+                    advancedSqlFiltering = advancedSqlFiltering + "AND " + column + " = ? ";
+                }
+            }
+            sql = "SELECT DEVICE_ID, PLATFORM, OWNERSHIP, CONNECTIVITY_STATUS FROM DEVICES_VIEW_2 " +
+                "WHERE TENANT_ID = ? AND FEATURE_CODE = ? " + advancedSqlFiltering +
+                    "ORDER BY DEVICE_ID ASC OFFSET ? LIMIT ?";
+            stmt = con.prepareStatement(sql);
+            // [2] appending filter column values, if exist
+            stmt.setInt(1, tenantId);
+            stmt.setString(2, nonCompliantFeatureCode);
+            if (filters != null && filters.values().size() > 0) {
+                int i = 3;
+                for (Object value : filters.values()) {
+                    if (value instanceof Integer) {
+                        stmt.setInt(i, (Integer) value);
+                    } else if (value instanceof String) {
+                        stmt.setString(i, (String) value);
+                    }
+                    i++;
+                }
+                stmt.setInt(i, startIndex);
+                stmt.setInt(++i, resultCount);
+            } else {
+                stmt.setInt(3, startIndex);
+                stmt.setInt(4, resultCount);
+            }
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            DetailedDeviceEntry filteredDeviceWithDetails;
+            while (rs.next()) {
+                filteredDeviceWithDetails = new DetailedDeviceEntry();
+                filteredDeviceWithDetails.setDeviceId(rs.getInt("DEVICE_ID"));
+                filteredDeviceWithDetails.setPlatform(rs.getString("PLATFORM"));
+                filteredDeviceWithDetails.setOwnershipType(rs.getString("OWNERSHIP"));
+                filteredDeviceWithDetails.setConnectivityStatus(rs.getString("CONNECTIVITY_STATUS"));
+                filteredDevicesWithDetails.add(filteredDeviceWithDetails);
+            }
+
+            // fetching total records count
+            sql = "SELECT COUNT(DEVICE_ID) AS DEVICE_COUNT FROM DEVICES_VIEW_2 " +
+                "WHERE TENANT_ID = ? AND FEATURE_CODE = ?";
+
+            stmt = con.prepareStatement(sql);
+            stmt.setInt(1, tenantId);
+            stmt.setString(2, nonCompliantFeatureCode);
+
+            // executing query
+            rs = stmt.executeQuery();
+            // fetching query results
+            while (rs.next()) {
+                totalRecordsCount = rs.getInt("DEVICE_COUNT");
+            }
+        } finally {
+            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
+        }
+        PaginationResult paginationResult = new PaginationResult();
+        paginationResult.setData(filteredDevicesWithDetails);
+        paginationResult.setRecordsTotal(totalRecordsCount);
+        return paginationResult;
+    }
+
+}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java
index 4f98359104..9ec776d2ef 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java
@@ -97,7 +97,7 @@ public class DeviceManagementDAOFactory {
     private static ThreadLocal<Connection> currentConnection = new ThreadLocal<Connection>();
 
     public static DeviceDAO getDeviceDAO() {
-        if(databaseEngine != null) {
+        if (databaseEngine != null) {
             switch (databaseEngine) {
                 case DeviceManagementConstants.DataBaseTypes.DB_TYPE_ORACLE:
                     return new OracleDeviceDAOImpl();
@@ -112,7 +112,7 @@ public class DeviceManagementDAOFactory {
                     throw new UnsupportedDatabaseEngineException("Unsupported database engine : " + databaseEngine);
             }
         }
-        throw new RuntimeException("Database engine has not initialized properly.");
+        throw new IllegalStateException("Database engine has not initialized properly.");
     }
 
     public static DeviceTypeDAO getDeviceTypeDAO() {
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/analytics.hbs b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/analytics.hbs
old mode 100644
new mode 100755
index d03feccbd3..7f27c97e94
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/analytics.hbs
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/analytics.hbs
@@ -1,13 +1,12 @@
 {{#zone "topCss"}}
     {{css "css/analytics.css"}}
-    {{css "css/daterangepicker.css"}}
-    {{css "css/graph.css"}}
 {{/zone}}
 
 {{unit "cdmf.unit.ui.title" pageTitle="Analytics"}}
 {{unit "cdmf.unit.ui.content.title" pageHeader=title}}
 {{unit "cdmf.unit.lib.service-invoker-utility"}}
 {{unit "cdmf.unit.lib.handlebars"}}
+{{unit "cdmf.unit.lib.rickshaw-graph"}}
 
 {{#zone "breadcrumbs"}}
     <li>
@@ -15,29 +14,16 @@
             <i class="icon fw fw-home"></i>
         </a>
     </li>
-    {{#if groupName}}
-        <li>
-            <a href="{{@app.context}}/groups">
-                Groups
-            </a>
-        </li>
-        <li>
-            <a href="{{@app.context}}/devices?groupId={{groupId}}&groupName={{groupName}}">
-                {{groupName}}
-            </a>
-        </li>
-    {{else}}
-        <li>
-            <a href="{{@app.context}}/devices">
-                Devices
-            </a>
-        </li>
-        <li>
-            <a href="{{@app.context}}/device/{{deviceType}}?id={{deviceId}}">
-                {{deviceName}}
-            </a>
-        </li>
-    {{/if}}
+    <li>
+        <a href="{{@app.context}}/devices">
+            Devices
+        </a>
+    </li>
+    <li>
+        <a href="{{@app.context}}/device/{{deviceType}}?id={{deviceId}}">
+            {{deviceName}}
+        </a>
+    </li>
     <li>
         <a href="#">
             Analytics
@@ -52,28 +38,7 @@
                 <div class="col-lg-3  margin-top-double">
                     <h1 class="grey ">{{deviceName}} Analytics</h1>
                 </div>
-                <div id="rangeSliderWrapper" class="pull-right col-lg-9">
-                    <div id="dateRangePickerContainer">
-                        <div class="btn-group" role="group">
-                            <button id="hour-btn" type="button"
-                                    class="btn btn-default date-range">Hour
-                            </button>
-                            <button id="h12-btn" type="button"
-                                    class="btn btn-default date-range">12 Hours
-                            </button>
-                            <button id="h24-btn" type="button"
-                                    class="btn btn-default date-range">24 Hours
-                            </button>
-                            <button id="h48-btn" type="button"
-                                    class="btn btn-default date-range">48 Hours
-                            </button>
-                            <button id="date-range" type="button"
-                                    class="btn btn-default date-range last-child"
-                                    data-toggle="popup"
-                                    title="Click to set custom date range"></button>
-                        </div>
-                    </div>
-                </div>
+                {{unit "cdmf.unit.analytics.date-range-picker"}}
             </div>
             <hr>
             <div class="clear"></div>
@@ -83,7 +48,3 @@
         </div>
     </div>
 {{/zone}}
-{{#zone "bottomJs"}}
-    {{js "js/jquery.daterangepicker.js"}}
-    {{js "js/graph_util.js"}}
-{{/zone}}
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/analytics.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/analytics.js
old mode 100644
new mode 100755
index 4b11b91aa1..f77e3d14ce
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/analytics.js
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/analytics.js
@@ -18,33 +18,12 @@
 
 function onRequest(context) {
     var utility = require("/app/modules/utility.js").utility;
-    context.handlebars.registerHelper('equal', function (lvalue, rvalue, options) {
-        if (arguments.length < 3) {
-            throw new Error("Handlebars Helper equal needs 2 parameters");
-        }
-        if (lvalue != rvalue) {
-            return options.inverse(this);
-        } else {
-            return options.fn(this);
-        }
-    });
-    var groupName = request.getParameter("groupName");
-    var groupId = request.getParameter("groupId");
+    var deviceType = context.uriParams.deviceType;
     var deviceName = request.getParameter("deviceName");
     var deviceId = request.getParameter("deviceId");
-    var deviceType = context.uriParams.deviceType;
-    var title = "Analytics";
-    if (groupName) {
-        title = "Group " + title;
-    } else {
-        title = "Device " + title;
-    }
     return {
         "deviceAnalyticsViewUnitName": utility.getTenantedDeviceUnitName(deviceType, "analytics-view"),
         "deviceType": deviceType,
-        "title": title,
-        "groupName": groupName,
-        "groupId": groupId,
         "deviceName": deviceName,
         "deviceId": deviceId
     };
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/analytics.css b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/analytics.css
old mode 100644
new mode 100755
index 84a8a9090b..42ad3c17f0
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/analytics.css
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/analytics.css
@@ -54,10 +54,3 @@
     margin-right: 20px;
     margin-left: 20px;
 }
-.date-range{
-    border: 1px solid #ccc;
-}
-
-#dateRangePickerContainer button.active{
-    background-color: #e6e6e6 !important;
-}
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.hbs b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.hbs
new file mode 100755
index 0000000000..7fc77538b0
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.hbs
@@ -0,0 +1,52 @@
+{{#zone "topCss"}}
+    {{css "css/analytics.css"}}
+{{/zone}}
+
+{{unit "cdmf.unit.ui.title" pageTitle="Analytics"}}
+{{unit "cdmf.unit.ui.content.title" pageHeader=title}}
+{{unit "cdmf.unit.lib.service-invoker-utility"}}
+{{unit "cdmf.unit.lib.handlebars"}}
+{{unit "cdmf.unit.lib.rickshaw-graph"}}
+
+{{#zone "breadcrumbs"}}
+    <li>
+        <a href="{{@app.context}}/">
+            <i class="icon fw fw-home"></i>
+        </a>
+    </li>
+    <li>
+        <a href="{{@app.context}}/groups">
+            Groups
+        </a>
+    </li>
+    <li>
+        <a href="{{@app.context}}/devices?groupOwner={{groupOwner}}&groupName={{groupName}}">
+            {{groupName}}
+        </a>
+    </li>
+    <li>
+        <a href="#">
+            Analytics
+        </a>
+    </li>
+{{/zone}}
+
+{{#zone "content"}}
+    <div class="container container-bg  white-bg">
+        <div class=" margin-top-double">
+            <div class="row padding-top-double padding-bottom-double margin-bottom-double">
+                <div class="col-lg-3  margin-top-double">
+                    <h1 class="grey ">{{groupName}} Analytics</h1>
+                </div>
+                {{unit "cdmf.unit.analytics.date-range-picker" deviceTypes=deviceTypes}}
+            </div>
+            <hr>
+            <div class="clear"></div>
+            <div id="div-chart">
+                {{#each deviceTypes}}
+                    {{unit deviceAnalyticsViewUnitName devices=devices}}
+                {{/each}}
+            </div>
+        </div>
+    </div>
+{{/zone}}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.js
new file mode 100755
index 0000000000..64173b06c8
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.js
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+function onRequest(context) {
+    var utility = require("/app/modules/utility.js").utility;
+    var groupModule = require("/app/modules/group.js").groupModule;
+    var groupName = context.uriParams.name;
+    var groupOwner = context.uriParams.owner;
+    var deviceTypes = [];
+    var devices = groupModule.getGroupDevices(groupName, groupOwner).data;
+
+    for (var i = 0; i < devices.length; i++) {
+        var hasDeviceType = false;
+        for (var j = 0; j < deviceTypes.length; j++) {
+            if (deviceTypes[j].type === devices[i].type) {
+                deviceTypes[j].devices.push(devices[i]);
+                hasDeviceType = true;
+                break;
+            }
+        }
+        if (!hasDeviceType) {
+            var deviceType = {};
+            deviceType.type = devices[i].type;
+            deviceType.devices = [];
+            deviceType.devices.push(devices[i]);
+            deviceType.deviceAnalyticsViewUnitName = utility.getTenantedDeviceUnitName(deviceType.type, "analytics-view");
+            deviceTypes.push(deviceType);
+        }
+    }
+
+    return {
+        "groupName": groupName,
+        "groupOwner": groupOwner,
+        "deviceTypes": deviceTypes
+    };
+}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.json b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.json
new file mode 100755
index 0000000000..9a06298a8c
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/analytics.json
@@ -0,0 +1,5 @@
+{
+  "version": "1.0.0",
+  "uri": "/group/{owner}/{name}/analytics",
+  "layout": "cdmf.layout.default"
+}
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/public/css/analytics.css b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/public/css/analytics.css
new file mode 100755
index 0000000000..84a8a9090b
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.group.analytics/public/css/analytics.css
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#rangeSliderWrapper {
+    margin-top: 25px;
+}
+
+#chart {
+    display: inline-block;
+}
+
+#legend {
+    display: inline-block;
+    position: relative;
+    left: 8px;
+}
+
+#legend_container {
+    position: absolute;
+    right: 0;
+    bottom: 26px;
+    width: 0;
+}
+
+#chart_container {
+    float: left;
+    position: relative;
+}
+
+.ast-container {
+    padding-bottom: 30px;
+}
+
+.container {
+    width: auto;
+}
+
+.shrink {
+    margin-right: 20px;
+    margin-left: 20px;
+}
+.date-range{
+    border: 1px solid #ccc;
+}
+
+#dateRangePickerContainer button.active{
+    background-color: #e6e6e6 !important;
+}
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.groups/public/js/listing.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.groups/public/js/listing.js
old mode 100644
new mode 100755
index 86f6768c0e..bd44775759
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.groups/public/js/listing.js
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.groups/public/js/listing.js
@@ -142,7 +142,7 @@ function loadGroups() {
                            '<span class="fw-stack"><i class="fw fw-ring fw-stack-2x"></i><i class="fw fw-view fw-stack-1x"></i></span>' +
                            '<span class="hidden-xs hidden-on-grid-view">View Devices</span></a>';
 
-                    html += '<a href="analytics?groupName=' + row.name + '&groupOwner=' + row.owner + '" data-click-event="remove-form" class="btn padding-reduce-on-grid-view">' +
+                    html += '<a href="group/' + row.owner + '/' + row.name + '/analytics" data-click-event="remove-form" class="btn padding-reduce-on-grid-view">' +
                             '<span class="fw-stack"><i class="fw fw-ring fw-stack-2x"></i><i class="fw fw-statistics fw-stack-1x"></i></span>' +
                             '<span class="hidden-xs hidden-on-grid-view">Analytics</span></a>';
 
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.groups/public/templates/listing.hbs b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.groups/public/templates/listing.hbs
deleted file mode 100644
index 929b40be8a..0000000000
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.groups/public/templates/listing.hbs
+++ /dev/null
@@ -1,62 +0,0 @@
-{{#each groups}}
-    <tr data-type="selectable" data-group-name="{{name}}" data-group-owner="{{owner}}">
-        <td class="remove-padding icon-only content-fill">
-            <div class="thumbnail icon">
-                <img class="square-element text fw "
-                     src="public/cdmf.page.groups/images/group-icon.png"/>
-            </div>
-        </td>
-        <td class="fade-edge">
-            <h4>{{name}}</h4>
-        </td>
-        <td class="fade-edge remove-padding-top" data-search="{{owner}}"
-            data-display="{{owner}}"
-            data-grid-label="Owner">{{owner}}</td>
-        <td class="text-right content-fill text-left-on-grid-view no-wrap">
-            <a href="devices?groupName={{name}}&groupOwner={{owner}}"
-               data-click-event="remove-form" class="btn padding-reduce-on-grid-view">
-                <span class="fw-stack">
-                    <i class="fw fw-ring fw-stack-2x"></i>
-                    <i class="fw fw-view fw-stack-1x"></i>
-                </span>
-                <span class="hidden-xs hidden-on-grid-view">View Devices</span>
-            </a>
-            <a href="analytics?groupName={{name}}&groupOwner={{owner}}"
-               data-click-event="remove-form" class="btn padding-reduce-on-grid-view">
-                <span class="fw-stack">
-                    <i class="fw fw-ring fw-stack-2x"></i>
-                    <i class="fw fw-statistics fw-stack-1x"></i>
-                </span>
-                <span class="hidden-xs hidden-on-grid-view">Analytics</span>
-            </a>
-            <a href="#" data-click-event="remove-form"
-               class="btn padding-reduce-on-grid-view share-group-link"
-               data-group-name="{{name}}" data-group-owner="{{owner}}">
-                <span class="fw-stack">
-                    <i class="fw fw-ring fw-stack-2x"></i>
-                    <i class="fw fw-share fw-stack-1x"></i>
-                </span>
-                <span class="hidden-xs hidden-on-grid-view">Share</span>
-            </a>
-            <a href="#" data-click-event="remove-form"
-               class="btn padding-reduce-on-grid-view edit-group-link"
-               data-group-name="{{name}}" data-group-owner="{{owner}}"
-               data-group-description="{{description}}">
-                <span class="fw-stack">
-                    <i class="fw fw-ring fw-stack-2x"></i>
-                    <i class="fw fw-edit fw-stack-1x"></i>
-                </span>
-                <span class="hidden-xs hidden-on-grid-view">Edit</span>
-            </a>
-            <a href="#" data-click-event="remove-form"
-               class="btn padding-reduce-on-grid-view remove-group-link"
-               data-group-name="{{name}}" data-group-owner="{{owner}}">
-                <span class="fw-stack">
-                    <i class="fw fw-ring fw-stack-2x"></i>
-                    <i class="fw fw-delete fw-stack-1x"></i>
-                </span>
-                <span class="hidden-xs hidden-on-grid-view">Delete</span>
-            </a>
-        </td>
-    </tr>
-{{/each}}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.hbs b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.hbs
new file mode 100755
index 0000000000..20a035a8bd
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.hbs
@@ -0,0 +1,31 @@
+{{#zone "topCss"}}
+    {{css "css/daterangepicker.css"}}
+{{/zone}}
+<span id="device-type-details" data-devicetypes="{{deviceTypes}}"></span>
+<div id="rangeSliderWrapper" class="pull-right col-lg-9">
+    <div id="dateRangePickerContainer">
+        <div class="btn-group" role="group">
+            <button id="hour-btn" type="button"
+                    class="btn btn-default date-range">Hour
+            </button>
+            <button id="h12-btn" type="button"
+                    class="btn btn-default date-range">12 Hours
+            </button>
+            <button id="h24-btn" type="button"
+                    class="btn btn-default date-range">24 Hours
+            </button>
+            <button id="h48-btn" type="button"
+                    class="btn btn-default date-range">48 Hours
+            </button>
+            <button id="date-range" type="button"
+                    class="btn btn-default date-range last-child"
+                    data-toggle="popup"
+                    title="Click to set custom date range"></button>
+        </div>
+    </div>
+</div>
+{{#zone "bottomJs"}}
+    {{js "js/moment.js"}}
+    {{js "js/jquery.daterangepicker.js"}}
+    {{js "js/date-picker.js"}}
+{{/zone}}
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.js
new file mode 100755
index 0000000000..9dc92ccdb3
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.js
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+function onRequest(context) {
+    var deviceTypes = context.unit.params.deviceTypes;
+    var deviceType = context.uriParams.deviceType;
+
+    var deviceTypesList = [];
+    if (deviceTypes) {
+        for (var i = 0; i < deviceTypes.length; i++) {
+            deviceTypesList.push(deviceTypes[i].type);
+        }
+    } else if (deviceType) {
+        deviceTypesList.push(deviceType);
+    }
+    return {"deviceTypes": stringify(deviceTypesList)};
+}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.json b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.json
new file mode 100755
index 0000000000..688e939808
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/date-range-picker.json
@@ -0,0 +1,3 @@
+{
+  "version": "1.0.0"
+}
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/daterangepicker.css b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/css/daterangepicker.css
similarity index 96%
rename from components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/daterangepicker.css
rename to components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/css/daterangepicker.css
index b708a1a88c..b1b44916b4 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/daterangepicker.css
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/css/daterangepicker.css
@@ -345,4 +345,17 @@ input.hour-range, input.minute-range {
 
 #dateRangePickerContainer {
     float: right;
-}
\ No newline at end of file
+}
+
+.date-range {
+    border: 1px solid #ccc;
+}
+
+#dateRangePickerContainer button.active {
+    background-color: #e6e6e6 !important;
+}
+
+#dateRangePickerContainer .btn-default:hover {
+    background-color: #b2b2b2;
+    border-color: #000000;
+}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/graph_util.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/date-picker.js
old mode 100644
new mode 100755
similarity index 80%
rename from components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/graph_util.js
rename to components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/date-picker.js
index c8171b5bf2..3f89797692
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/graph_util.js
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/date-picker.js
@@ -28,9 +28,26 @@ var DateRange = convertDate(startDate) + " to " + convertDate(endDate);
 
 $(document).ready(function () {
     initDate();
-
+    var configObject = {
+        startOfWeek: 'monday',
+        separator: ' to ',
+        format: 'YYYY-MM-DD HH:mm',
+        autoClose: false,
+        time: {
+            enabled: true
+        },
+        shortcuts: 'hide',
+        endDate: currentDay,
+        maxDays: 2,
+        getValue: function () {
+            return this.value;
+        },
+        setValue: function (s) {
+            this.value = s;
+        }
+    };
     $('#date-range').html(DateRange);
-    $('#date-range').dateRangePicker()
+    $('#date-range').dateRangePicker(configObject)
             .bind('datepicker-apply', function (event, dateRange) {
                       $(this).addClass('active');
                       $(this).siblings().removeClass('active');
@@ -83,8 +100,11 @@ function setDateTime(from, to) {
     from += tzOffset;
     to += tzOffset;
 
-    // the relevant import units needs to implement this.
-    drawGraph(parseInt(from / 1000), parseInt(to / 1000));
+    // Implement drawGraph_<device type name> method in your UI unit for analytics.
+    var deviceTypes = $("#device-type-details").data("devicetypes");
+    for (var i = 0; i < deviceTypes.length; i++){
+        window["drawGraph_" + deviceTypes](parseInt(from / 1000), parseInt(to / 1000));
+    }
 }
 
 function convertDate(date) {
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/jquery-ui-timepicker-addon.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/jquery-ui-timepicker-addon.js
similarity index 99%
rename from components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/jquery-ui-timepicker-addon.js
rename to components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/jquery-ui-timepicker-addon.js
index 1beb64247a..acd3479075 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/jquery-ui-timepicker-addon.js
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/jquery-ui-timepicker-addon.js
@@ -1,20 +1,3 @@
-/*
- * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
- *
- * WSO2 Inc. licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
 (function ($) {
 
 	/*
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/jquery.daterangepicker.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/jquery.daterangepicker.js
similarity index 98%
rename from components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/jquery.daterangepicker.js
rename to components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/jquery.daterangepicker.js
index 1807ec2409..872bc59740 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/js/jquery.daterangepicker.js
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/jquery.daterangepicker.js
@@ -1,21 +1,3 @@
-/*
- * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
- *
- * WSO2 Inc. licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
 // daterangepicker.js
 // version : 0.0.5
 // author : Chunlong Liu
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/moment.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/moment.js
new file mode 100755
index 0000000000..ed94e44a1e
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.analytics.date-range-picker/public/js/moment.js
@@ -0,0 +1,4040 @@
+//! moment.js
+//! version : 2.13.0
+//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
+//! license : MIT
+//! momentjs.com
+
+;(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    global.moment = factory()
+}(this, function () { 'use strict';
+
+    var hookCallback;
+
+    function utils_hooks__hooks () {
+        return hookCallback.apply(null, arguments);
+    }
+
+    // This is done to register the method called with moment()
+    // without creating circular dependencies.
+    function setHookCallback (callback) {
+        hookCallback = callback;
+    }
+
+    function isArray(input) {
+        return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
+    }
+
+    function isDate(input) {
+        return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
+    }
+
+    function map(arr, fn) {
+        var res = [], i;
+        for (i = 0; i < arr.length; ++i) {
+            res.push(fn(arr[i], i));
+        }
+        return res;
+    }
+
+    function hasOwnProp(a, b) {
+        return Object.prototype.hasOwnProperty.call(a, b);
+    }
+
+    function extend(a, b) {
+        for (var i in b) {
+            if (hasOwnProp(b, i)) {
+                a[i] = b[i];
+            }
+        }
+
+        if (hasOwnProp(b, 'toString')) {
+            a.toString = b.toString;
+        }
+
+        if (hasOwnProp(b, 'valueOf')) {
+            a.valueOf = b.valueOf;
+        }
+
+        return a;
+    }
+
+    function create_utc__createUTC (input, format, locale, strict) {
+        return createLocalOrUTC(input, format, locale, strict, true).utc();
+    }
+
+    function defaultParsingFlags() {
+        // We need to deep clone this object.
+        return {
+            empty           : false,
+            unusedTokens    : [],
+            unusedInput     : [],
+            overflow        : -2,
+            charsLeftOver   : 0,
+            nullInput       : false,
+            invalidMonth    : null,
+            invalidFormat   : false,
+            userInvalidated : false,
+            iso             : false,
+            parsedDateParts : [],
+            meridiem        : null
+        };
+    }
+
+    function getParsingFlags(m) {
+        if (m._pf == null) {
+            m._pf = defaultParsingFlags();
+        }
+        return m._pf;
+    }
+
+    var some;
+    if (Array.prototype.some) {
+        some = Array.prototype.some;
+    } else {
+        some = function (fun) {
+            var t = Object(this);
+            var len = t.length >>> 0;
+
+            for (var i = 0; i < len; i++) {
+                if (i in t && fun.call(this, t[i], i, t)) {
+                    return true;
+                }
+            }
+
+            return false;
+        };
+    }
+
+    function valid__isValid(m) {
+        if (m._isValid == null) {
+            var flags = getParsingFlags(m);
+            var parsedParts = some.call(flags.parsedDateParts, function (i) {
+                return i != null;
+            });
+            m._isValid = !isNaN(m._d.getTime()) &&
+                flags.overflow < 0 &&
+                !flags.empty &&
+                !flags.invalidMonth &&
+                !flags.invalidWeekday &&
+                !flags.nullInput &&
+                !flags.invalidFormat &&
+                !flags.userInvalidated &&
+                (!flags.meridiem || (flags.meridiem && parsedParts));
+
+            if (m._strict) {
+                m._isValid = m._isValid &&
+                    flags.charsLeftOver === 0 &&
+                    flags.unusedTokens.length === 0 &&
+                    flags.bigHour === undefined;
+            }
+        }
+        return m._isValid;
+    }
+
+    function valid__createInvalid (flags) {
+        var m = create_utc__createUTC(NaN);
+        if (flags != null) {
+            extend(getParsingFlags(m), flags);
+        }
+        else {
+            getParsingFlags(m).userInvalidated = true;
+        }
+
+        return m;
+    }
+
+    function isUndefined(input) {
+        return input === void 0;
+    }
+
+    // Plugins that add properties should also add the key here (null value),
+    // so we can properly clone ourselves.
+    var momentProperties = utils_hooks__hooks.momentProperties = [];
+
+    function copyConfig(to, from) {
+        var i, prop, val;
+
+        if (!isUndefined(from._isAMomentObject)) {
+            to._isAMomentObject = from._isAMomentObject;
+        }
+        if (!isUndefined(from._i)) {
+            to._i = from._i;
+        }
+        if (!isUndefined(from._f)) {
+            to._f = from._f;
+        }
+        if (!isUndefined(from._l)) {
+            to._l = from._l;
+        }
+        if (!isUndefined(from._strict)) {
+            to._strict = from._strict;
+        }
+        if (!isUndefined(from._tzm)) {
+            to._tzm = from._tzm;
+        }
+        if (!isUndefined(from._isUTC)) {
+            to._isUTC = from._isUTC;
+        }
+        if (!isUndefined(from._offset)) {
+            to._offset = from._offset;
+        }
+        if (!isUndefined(from._pf)) {
+            to._pf = getParsingFlags(from);
+        }
+        if (!isUndefined(from._locale)) {
+            to._locale = from._locale;
+        }
+
+        if (momentProperties.length > 0) {
+            for (i in momentProperties) {
+                prop = momentProperties[i];
+                val = from[prop];
+                if (!isUndefined(val)) {
+                    to[prop] = val;
+                }
+            }
+        }
+
+        return to;
+    }
+
+    var updateInProgress = false;
+
+    // Moment prototype object
+    function Moment(config) {
+        copyConfig(this, config);
+        this._d = new Date(config._d != null ? config._d.getTime() : NaN);
+        // Prevent infinite loop in case updateOffset creates new moment
+        // objects.
+        if (updateInProgress === false) {
+            updateInProgress = true;
+            utils_hooks__hooks.updateOffset(this);
+            updateInProgress = false;
+        }
+    }
+
+    function isMoment (obj) {
+        return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
+    }
+
+    function absFloor (number) {
+        if (number < 0) {
+            return Math.ceil(number);
+        } else {
+            return Math.floor(number);
+        }
+    }
+
+    function toInt(argumentForCoercion) {
+        var coercedNumber = +argumentForCoercion,
+            value = 0;
+
+        if (coercedNumber !== 0 && isFinite(coercedNumber)) {
+            value = absFloor(coercedNumber);
+        }
+
+        return value;
+    }
+
+    // compare two arrays, return the number of differences
+    function compareArrays(array1, array2, dontConvert) {
+        var len = Math.min(array1.length, array2.length),
+            lengthDiff = Math.abs(array1.length - array2.length),
+            diffs = 0,
+            i;
+        for (i = 0; i < len; i++) {
+            if ((dontConvert && array1[i] !== array2[i]) ||
+                (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
+                diffs++;
+            }
+        }
+        return diffs + lengthDiff;
+    }
+
+    function warn(msg) {
+        if (utils_hooks__hooks.suppressDeprecationWarnings === false &&
+                (typeof console !==  'undefined') && console.warn) {
+            console.warn('Deprecation warning: ' + msg);
+        }
+    }
+
+    function deprecate(msg, fn) {
+        var firstTime = true;
+
+        return extend(function () {
+            if (utils_hooks__hooks.deprecationHandler != null) {
+                utils_hooks__hooks.deprecationHandler(null, msg);
+            }
+            if (firstTime) {
+                warn(msg + '\nArguments: ' + Array.prototype.slice.call(arguments).join(', ') + '\n' + (new Error()).stack);
+                firstTime = false;
+            }
+            return fn.apply(this, arguments);
+        }, fn);
+    }
+
+    var deprecations = {};
+
+    function deprecateSimple(name, msg) {
+        if (utils_hooks__hooks.deprecationHandler != null) {
+            utils_hooks__hooks.deprecationHandler(name, msg);
+        }
+        if (!deprecations[name]) {
+            warn(msg);
+            deprecations[name] = true;
+        }
+    }
+
+    utils_hooks__hooks.suppressDeprecationWarnings = false;
+    utils_hooks__hooks.deprecationHandler = null;
+
+    function isFunction(input) {
+        return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
+    }
+
+    function isObject(input) {
+        return Object.prototype.toString.call(input) === '[object Object]';
+    }
+
+    function locale_set__set (config) {
+        var prop, i;
+        for (i in config) {
+            prop = config[i];
+            if (isFunction(prop)) {
+                this[i] = prop;
+            } else {
+                this['_' + i] = prop;
+            }
+        }
+        this._config = config;
+        // Lenient ordinal parsing accepts just a number in addition to
+        // number + (possibly) stuff coming from _ordinalParseLenient.
+        this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source);
+    }
+
+    function mergeConfigs(parentConfig, childConfig) {
+        var res = extend({}, parentConfig), prop;
+        for (prop in childConfig) {
+            if (hasOwnProp(childConfig, prop)) {
+                if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
+                    res[prop] = {};
+                    extend(res[prop], parentConfig[prop]);
+                    extend(res[prop], childConfig[prop]);
+                } else if (childConfig[prop] != null) {
+                    res[prop] = childConfig[prop];
+                } else {
+                    delete res[prop];
+                }
+            }
+        }
+        return res;
+    }
+
+    function Locale(config) {
+        if (config != null) {
+            this.set(config);
+        }
+    }
+
+    var keys;
+
+    if (Object.keys) {
+        keys = Object.keys;
+    } else {
+        keys = function (obj) {
+            var i, res = [];
+            for (i in obj) {
+                if (hasOwnProp(obj, i)) {
+                    res.push(i);
+                }
+            }
+            return res;
+        };
+    }
+
+    // internal storage for locale config files
+    var locales = {};
+    var globalLocale;
+
+    function normalizeLocale(key) {
+        return key ? key.toLowerCase().replace('_', '-') : key;
+    }
+
+    // pick the locale from the array
+    // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
+    // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
+    function chooseLocale(names) {
+        var i = 0, j, next, locale, split;
+
+        while (i < names.length) {
+            split = normalizeLocale(names[i]).split('-');
+            j = split.length;
+            next = normalizeLocale(names[i + 1]);
+            next = next ? next.split('-') : null;
+            while (j > 0) {
+                locale = loadLocale(split.slice(0, j).join('-'));
+                if (locale) {
+                    return locale;
+                }
+                if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
+                    //the next array item is better than a shallower substring of this one
+                    break;
+                }
+                j--;
+            }
+            i++;
+        }
+        return null;
+    }
+
+    function loadLocale(name) {
+        var oldLocale = null;
+        // TODO: Find a better way to register and load all the locales in Node
+        if (!locales[name] && (typeof module !== 'undefined') &&
+                module && module.exports) {
+            try {
+                oldLocale = globalLocale._abbr;
+                require('./locale/' + name);
+                // because defineLocale currently also sets the global locale, we
+                // want to undo that for lazy loaded locales
+                locale_locales__getSetGlobalLocale(oldLocale);
+            } catch (e) { }
+        }
+        return locales[name];
+    }
+
+    // This function will load locale and then set the global locale.  If
+    // no arguments are passed in, it will simply return the current global
+    // locale key.
+    function locale_locales__getSetGlobalLocale (key, values) {
+        var data;
+        if (key) {
+            if (isUndefined(values)) {
+                data = locale_locales__getLocale(key);
+            }
+            else {
+                data = defineLocale(key, values);
+            }
+
+            if (data) {
+                // moment.duration._locale = moment._locale = data;
+                globalLocale = data;
+            }
+        }
+
+        return globalLocale._abbr;
+    }
+
+    function defineLocale (name, config) {
+        if (config !== null) {
+            config.abbr = name;
+            if (locales[name] != null) {
+                deprecateSimple('defineLocaleOverride',
+                        'use moment.updateLocale(localeName, config) to change ' +
+                        'an existing locale. moment.defineLocale(localeName, ' +
+                        'config) should only be used for creating a new locale');
+                config = mergeConfigs(locales[name]._config, config);
+            } else if (config.parentLocale != null) {
+                if (locales[config.parentLocale] != null) {
+                    config = mergeConfigs(locales[config.parentLocale]._config, config);
+                } else {
+                    // treat as if there is no base config
+                    deprecateSimple('parentLocaleUndefined',
+                            'specified parentLocale is not defined yet');
+                }
+            }
+            locales[name] = new Locale(config);
+
+            // backwards compat for now: also set the locale
+            locale_locales__getSetGlobalLocale(name);
+
+            return locales[name];
+        } else {
+            // useful for testing
+            delete locales[name];
+            return null;
+        }
+    }
+
+    function updateLocale(name, config) {
+        if (config != null) {
+            var locale;
+            if (locales[name] != null) {
+                config = mergeConfigs(locales[name]._config, config);
+            }
+            locale = new Locale(config);
+            locale.parentLocale = locales[name];
+            locales[name] = locale;
+
+            // backwards compat for now: also set the locale
+            locale_locales__getSetGlobalLocale(name);
+        } else {
+            // pass null for config to unupdate, useful for tests
+            if (locales[name] != null) {
+                if (locales[name].parentLocale != null) {
+                    locales[name] = locales[name].parentLocale;
+                } else if (locales[name] != null) {
+                    delete locales[name];
+                }
+            }
+        }
+        return locales[name];
+    }
+
+    // returns locale data
+    function locale_locales__getLocale (key) {
+        var locale;
+
+        if (key && key._locale && key._locale._abbr) {
+            key = key._locale._abbr;
+        }
+
+        if (!key) {
+            return globalLocale;
+        }
+
+        if (!isArray(key)) {
+            //short-circuit everything else
+            locale = loadLocale(key);
+            if (locale) {
+                return locale;
+            }
+            key = [key];
+        }
+
+        return chooseLocale(key);
+    }
+
+    function locale_locales__listLocales() {
+        return keys(locales);
+    }
+
+    var aliases = {};
+
+    function addUnitAlias (unit, shorthand) {
+        var lowerCase = unit.toLowerCase();
+        aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
+    }
+
+    function normalizeUnits(units) {
+        return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
+    }
+
+    function normalizeObjectUnits(inputObject) {
+        var normalizedInput = {},
+            normalizedProp,
+            prop;
+
+        for (prop in inputObject) {
+            if (hasOwnProp(inputObject, prop)) {
+                normalizedProp = normalizeUnits(prop);
+                if (normalizedProp) {
+                    normalizedInput[normalizedProp] = inputObject[prop];
+                }
+            }
+        }
+
+        return normalizedInput;
+    }
+
+    function makeGetSet (unit, keepTime) {
+        return function (value) {
+            if (value != null) {
+                get_set__set(this, unit, value);
+                utils_hooks__hooks.updateOffset(this, keepTime);
+                return this;
+            } else {
+                return get_set__get(this, unit);
+            }
+        };
+    }
+
+    function get_set__get (mom, unit) {
+        return mom.isValid() ?
+            mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
+    }
+
+    function get_set__set (mom, unit, value) {
+        if (mom.isValid()) {
+            mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
+        }
+    }
+
+    // MOMENTS
+
+    function getSet (units, value) {
+        var unit;
+        if (typeof units === 'object') {
+            for (unit in units) {
+                this.set(unit, units[unit]);
+            }
+        } else {
+            units = normalizeUnits(units);
+            if (isFunction(this[units])) {
+                return this[units](value);
+            }
+        }
+        return this;
+    }
+
+    function zeroFill(number, targetLength, forceSign) {
+        var absNumber = '' + Math.abs(number),
+            zerosToFill = targetLength - absNumber.length,
+            sign = number >= 0;
+        return (sign ? (forceSign ? '+' : '') : '-') +
+            Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
+    }
+
+    var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;
+
+    var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
+
+    var formatFunctions = {};
+
+    var formatTokenFunctions = {};
+
+    // token:    'M'
+    // padded:   ['MM', 2]
+    // ordinal:  'Mo'
+    // callback: function () { this.month() + 1 }
+    function addFormatToken (token, padded, ordinal, callback) {
+        var func = callback;
+        if (typeof callback === 'string') {
+            func = function () {
+                return this[callback]();
+            };
+        }
+        if (token) {
+            formatTokenFunctions[token] = func;
+        }
+        if (padded) {
+            formatTokenFunctions[padded[0]] = function () {
+                return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
+            };
+        }
+        if (ordinal) {
+            formatTokenFunctions[ordinal] = function () {
+                return this.localeData().ordinal(func.apply(this, arguments), token);
+            };
+        }
+    }
+
+    function removeFormattingTokens(input) {
+        if (input.match(/\[[\s\S]/)) {
+            return input.replace(/^\[|\]$/g, '');
+        }
+        return input.replace(/\\/g, '');
+    }
+
+    function makeFormatFunction(format) {
+        var array = format.match(formattingTokens), i, length;
+
+        for (i = 0, length = array.length; i < length; i++) {
+            if (formatTokenFunctions[array[i]]) {
+                array[i] = formatTokenFunctions[array[i]];
+            } else {
+                array[i] = removeFormattingTokens(array[i]);
+            }
+        }
+
+        return function (mom) {
+            var output = '', i;
+            for (i = 0; i < length; i++) {
+                output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
+            }
+            return output;
+        };
+    }
+
+    // format date using native date object
+    function formatMoment(m, format) {
+        if (!m.isValid()) {
+            return m.localeData().invalidDate();
+        }
+
+        format = expandFormat(format, m.localeData());
+        formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
+
+        return formatFunctions[format](m);
+    }
+
+    function expandFormat(format, locale) {
+        var i = 5;
+
+        function replaceLongDateFormatTokens(input) {
+            return locale.longDateFormat(input) || input;
+        }
+
+        localFormattingTokens.lastIndex = 0;
+        while (i >= 0 && localFormattingTokens.test(format)) {
+            format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
+            localFormattingTokens.lastIndex = 0;
+            i -= 1;
+        }
+
+        return format;
+    }
+
+    var match1         = /\d/;            //       0 - 9
+    var match2         = /\d\d/;          //      00 - 99
+    var match3         = /\d{3}/;         //     000 - 999
+    var match4         = /\d{4}/;         //    0000 - 9999
+    var match6         = /[+-]?\d{6}/;    // -999999 - 999999
+    var match1to2      = /\d\d?/;         //       0 - 99
+    var match3to4      = /\d\d\d\d?/;     //     999 - 9999
+    var match5to6      = /\d\d\d\d\d\d?/; //   99999 - 999999
+    var match1to3      = /\d{1,3}/;       //       0 - 999
+    var match1to4      = /\d{1,4}/;       //       0 - 9999
+    var match1to6      = /[+-]?\d{1,6}/;  // -999999 - 999999
+
+    var matchUnsigned  = /\d+/;           //       0 - inf
+    var matchSigned    = /[+-]?\d+/;      //    -inf - inf
+
+    var matchOffset    = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
+    var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
+
+    var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
+
+    // any word (or two) characters or numbers including two/three word month in arabic.
+    // includes scottish gaelic two word and hyphenated months
+    var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
+
+
+    var regexes = {};
+
+    function addRegexToken (token, regex, strictRegex) {
+        regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
+            return (isStrict && strictRegex) ? strictRegex : regex;
+        };
+    }
+
+    function getParseRegexForToken (token, config) {
+        if (!hasOwnProp(regexes, token)) {
+            return new RegExp(unescapeFormat(token));
+        }
+
+        return regexes[token](config._strict, config._locale);
+    }
+
+    // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
+    function unescapeFormat(s) {
+        return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
+            return p1 || p2 || p3 || p4;
+        }));
+    }
+
+    function regexEscape(s) {
+        return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+    }
+
+    var tokens = {};
+
+    function addParseToken (token, callback) {
+        var i, func = callback;
+        if (typeof token === 'string') {
+            token = [token];
+        }
+        if (typeof callback === 'number') {
+            func = function (input, array) {
+                array[callback] = toInt(input);
+            };
+        }
+        for (i = 0; i < token.length; i++) {
+            tokens[token[i]] = func;
+        }
+    }
+
+    function addWeekParseToken (token, callback) {
+        addParseToken(token, function (input, array, config, token) {
+            config._w = config._w || {};
+            callback(input, config._w, config, token);
+        });
+    }
+
+    function addTimeToArrayFromToken(token, input, config) {
+        if (input != null && hasOwnProp(tokens, token)) {
+            tokens[token](input, config._a, config, token);
+        }
+    }
+
+    var YEAR = 0;
+    var MONTH = 1;
+    var DATE = 2;
+    var HOUR = 3;
+    var MINUTE = 4;
+    var SECOND = 5;
+    var MILLISECOND = 6;
+    var WEEK = 7;
+    var WEEKDAY = 8;
+
+    var indexOf;
+
+    if (Array.prototype.indexOf) {
+        indexOf = Array.prototype.indexOf;
+    } else {
+        indexOf = function (o) {
+            // I know
+            var i;
+            for (i = 0; i < this.length; ++i) {
+                if (this[i] === o) {
+                    return i;
+                }
+            }
+            return -1;
+        };
+    }
+
+    function daysInMonth(year, month) {
+        return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
+    }
+
+    // FORMATTING
+
+    addFormatToken('M', ['MM', 2], 'Mo', function () {
+        return this.month() + 1;
+    });
+
+    addFormatToken('MMM', 0, 0, function (format) {
+        return this.localeData().monthsShort(this, format);
+    });
+
+    addFormatToken('MMMM', 0, 0, function (format) {
+        return this.localeData().months(this, format);
+    });
+
+    // ALIASES
+
+    addUnitAlias('month', 'M');
+
+    // PARSING
+
+    addRegexToken('M',    match1to2);
+    addRegexToken('MM',   match1to2, match2);
+    addRegexToken('MMM',  function (isStrict, locale) {
+        return locale.monthsShortRegex(isStrict);
+    });
+    addRegexToken('MMMM', function (isStrict, locale) {
+        return locale.monthsRegex(isStrict);
+    });
+
+    addParseToken(['M', 'MM'], function (input, array) {
+        array[MONTH] = toInt(input) - 1;
+    });
+
+    addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
+        var month = config._locale.monthsParse(input, token, config._strict);
+        // if we didn't find a month name, mark the date as invalid.
+        if (month != null) {
+            array[MONTH] = month;
+        } else {
+            getParsingFlags(config).invalidMonth = input;
+        }
+    });
+
+    // LOCALES
+
+    var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/;
+    var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
+    function localeMonths (m, format) {
+        return isArray(this._months) ? this._months[m.month()] :
+            this._months[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
+    }
+
+    var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
+    function localeMonthsShort (m, format) {
+        return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
+            this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
+    }
+
+    function units_month__handleStrictParse(monthName, format, strict) {
+        var i, ii, mom, llc = monthName.toLocaleLowerCase();
+        if (!this._monthsParse) {
+            // this is not used
+            this._monthsParse = [];
+            this._longMonthsParse = [];
+            this._shortMonthsParse = [];
+            for (i = 0; i < 12; ++i) {
+                mom = create_utc__createUTC([2000, i]);
+                this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
+                this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
+            }
+        }
+
+        if (strict) {
+            if (format === 'MMM') {
+                ii = indexOf.call(this._shortMonthsParse, llc);
+                return ii !== -1 ? ii : null;
+            } else {
+                ii = indexOf.call(this._longMonthsParse, llc);
+                return ii !== -1 ? ii : null;
+            }
+        } else {
+            if (format === 'MMM') {
+                ii = indexOf.call(this._shortMonthsParse, llc);
+                if (ii !== -1) {
+                    return ii;
+                }
+                ii = indexOf.call(this._longMonthsParse, llc);
+                return ii !== -1 ? ii : null;
+            } else {
+                ii = indexOf.call(this._longMonthsParse, llc);
+                if (ii !== -1) {
+                    return ii;
+                }
+                ii = indexOf.call(this._shortMonthsParse, llc);
+                return ii !== -1 ? ii : null;
+            }
+        }
+    }
+
+    function localeMonthsParse (monthName, format, strict) {
+        var i, mom, regex;
+
+        if (this._monthsParseExact) {
+            return units_month__handleStrictParse.call(this, monthName, format, strict);
+        }
+
+        if (!this._monthsParse) {
+            this._monthsParse = [];
+            this._longMonthsParse = [];
+            this._shortMonthsParse = [];
+        }
+
+        // TODO: add sorting
+        // Sorting makes sure if one month (or abbr) is a prefix of another
+        // see sorting in computeMonthsParse
+        for (i = 0; i < 12; i++) {
+            // make the regex if we don't have it already
+            mom = create_utc__createUTC([2000, i]);
+            if (strict && !this._longMonthsParse[i]) {
+                this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
+                this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
+            }
+            if (!strict && !this._monthsParse[i]) {
+                regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
+                this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
+            }
+            // test the regex
+            if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
+                return i;
+            } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
+                return i;
+            } else if (!strict && this._monthsParse[i].test(monthName)) {
+                return i;
+            }
+        }
+    }
+
+    // MOMENTS
+
+    function setMonth (mom, value) {
+        var dayOfMonth;
+
+        if (!mom.isValid()) {
+            // No op
+            return mom;
+        }
+
+        if (typeof value === 'string') {
+            if (/^\d+$/.test(value)) {
+                value = toInt(value);
+            } else {
+                value = mom.localeData().monthsParse(value);
+                // TODO: Another silent failure?
+                if (typeof value !== 'number') {
+                    return mom;
+                }
+            }
+        }
+
+        dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
+        mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
+        return mom;
+    }
+
+    function getSetMonth (value) {
+        if (value != null) {
+            setMonth(this, value);
+            utils_hooks__hooks.updateOffset(this, true);
+            return this;
+        } else {
+            return get_set__get(this, 'Month');
+        }
+    }
+
+    function getDaysInMonth () {
+        return daysInMonth(this.year(), this.month());
+    }
+
+    var defaultMonthsShortRegex = matchWord;
+    function monthsShortRegex (isStrict) {
+        if (this._monthsParseExact) {
+            if (!hasOwnProp(this, '_monthsRegex')) {
+                computeMonthsParse.call(this);
+            }
+            if (isStrict) {
+                return this._monthsShortStrictRegex;
+            } else {
+                return this._monthsShortRegex;
+            }
+        } else {
+            return this._monthsShortStrictRegex && isStrict ?
+                this._monthsShortStrictRegex : this._monthsShortRegex;
+        }
+    }
+
+    var defaultMonthsRegex = matchWord;
+    function monthsRegex (isStrict) {
+        if (this._monthsParseExact) {
+            if (!hasOwnProp(this, '_monthsRegex')) {
+                computeMonthsParse.call(this);
+            }
+            if (isStrict) {
+                return this._monthsStrictRegex;
+            } else {
+                return this._monthsRegex;
+            }
+        } else {
+            return this._monthsStrictRegex && isStrict ?
+                this._monthsStrictRegex : this._monthsRegex;
+        }
+    }
+
+    function computeMonthsParse () {
+        function cmpLenRev(a, b) {
+            return b.length - a.length;
+        }
+
+        var shortPieces = [], longPieces = [], mixedPieces = [],
+            i, mom;
+        for (i = 0; i < 12; i++) {
+            // make the regex if we don't have it already
+            mom = create_utc__createUTC([2000, i]);
+            shortPieces.push(this.monthsShort(mom, ''));
+            longPieces.push(this.months(mom, ''));
+            mixedPieces.push(this.months(mom, ''));
+            mixedPieces.push(this.monthsShort(mom, ''));
+        }
+        // Sorting makes sure if one month (or abbr) is a prefix of another it
+        // will match the longer piece.
+        shortPieces.sort(cmpLenRev);
+        longPieces.sort(cmpLenRev);
+        mixedPieces.sort(cmpLenRev);
+        for (i = 0; i < 12; i++) {
+            shortPieces[i] = regexEscape(shortPieces[i]);
+            longPieces[i] = regexEscape(longPieces[i]);
+            mixedPieces[i] = regexEscape(mixedPieces[i]);
+        }
+
+        this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
+        this._monthsShortRegex = this._monthsRegex;
+        this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
+        this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
+    }
+
+    function checkOverflow (m) {
+        var overflow;
+        var a = m._a;
+
+        if (a && getParsingFlags(m).overflow === -2) {
+            overflow =
+                a[MONTH]       < 0 || a[MONTH]       > 11  ? MONTH :
+                a[DATE]        < 1 || a[DATE]        > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
+                a[HOUR]        < 0 || a[HOUR]        > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
+                a[MINUTE]      < 0 || a[MINUTE]      > 59  ? MINUTE :
+                a[SECOND]      < 0 || a[SECOND]      > 59  ? SECOND :
+                a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
+                -1;
+
+            if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
+                overflow = DATE;
+            }
+            if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
+                overflow = WEEK;
+            }
+            if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
+                overflow = WEEKDAY;
+            }
+
+            getParsingFlags(m).overflow = overflow;
+        }
+
+        return m;
+    }
+
+    // iso 8601 regex
+    // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
+    var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/;
+    var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/;
+
+    var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
+
+    var isoDates = [
+        ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
+        ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
+        ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
+        ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
+        ['YYYY-DDD', /\d{4}-\d{3}/],
+        ['YYYY-MM', /\d{4}-\d\d/, false],
+        ['YYYYYYMMDD', /[+-]\d{10}/],
+        ['YYYYMMDD', /\d{8}/],
+        // YYYYMM is NOT allowed by the standard
+        ['GGGG[W]WWE', /\d{4}W\d{3}/],
+        ['GGGG[W]WW', /\d{4}W\d{2}/, false],
+        ['YYYYDDD', /\d{7}/]
+    ];
+
+    // iso time formats and regexes
+    var isoTimes = [
+        ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
+        ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
+        ['HH:mm:ss', /\d\d:\d\d:\d\d/],
+        ['HH:mm', /\d\d:\d\d/],
+        ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
+        ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
+        ['HHmmss', /\d\d\d\d\d\d/],
+        ['HHmm', /\d\d\d\d/],
+        ['HH', /\d\d/]
+    ];
+
+    var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
+
+    // date from iso format
+    function configFromISO(config) {
+        var i, l,
+            string = config._i,
+            match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
+            allowTime, dateFormat, timeFormat, tzFormat;
+
+        if (match) {
+            getParsingFlags(config).iso = true;
+
+            for (i = 0, l = isoDates.length; i < l; i++) {
+                if (isoDates[i][1].exec(match[1])) {
+                    dateFormat = isoDates[i][0];
+                    allowTime = isoDates[i][2] !== false;
+                    break;
+                }
+            }
+            if (dateFormat == null) {
+                config._isValid = false;
+                return;
+            }
+            if (match[3]) {
+                for (i = 0, l = isoTimes.length; i < l; i++) {
+                    if (isoTimes[i][1].exec(match[3])) {
+                        // match[2] should be 'T' or space
+                        timeFormat = (match[2] || ' ') + isoTimes[i][0];
+                        break;
+                    }
+                }
+                if (timeFormat == null) {
+                    config._isValid = false;
+                    return;
+                }
+            }
+            if (!allowTime && timeFormat != null) {
+                config._isValid = false;
+                return;
+            }
+            if (match[4]) {
+                if (tzRegex.exec(match[4])) {
+                    tzFormat = 'Z';
+                } else {
+                    config._isValid = false;
+                    return;
+                }
+            }
+            config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
+            configFromStringAndFormat(config);
+        } else {
+            config._isValid = false;
+        }
+    }
+
+    // date from iso format or fallback
+    function configFromString(config) {
+        var matched = aspNetJsonRegex.exec(config._i);
+
+        if (matched !== null) {
+            config._d = new Date(+matched[1]);
+            return;
+        }
+
+        configFromISO(config);
+        if (config._isValid === false) {
+            delete config._isValid;
+            utils_hooks__hooks.createFromInputFallback(config);
+        }
+    }
+
+    utils_hooks__hooks.createFromInputFallback = deprecate(
+        'moment construction falls back to js Date. This is ' +
+        'discouraged and will be removed in upcoming major ' +
+        'release. Please refer to ' +
+        'https://github.com/moment/moment/issues/1407 for more info.',
+        function (config) {
+            config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
+        }
+    );
+
+    function createDate (y, m, d, h, M, s, ms) {
+        //can't just apply() to create a date:
+        //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
+        var date = new Date(y, m, d, h, M, s, ms);
+
+        //the date constructor remaps years 0-99 to 1900-1999
+        if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
+            date.setFullYear(y);
+        }
+        return date;
+    }
+
+    function createUTCDate (y) {
+        var date = new Date(Date.UTC.apply(null, arguments));
+
+        //the Date.UTC function remaps years 0-99 to 1900-1999
+        if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
+            date.setUTCFullYear(y);
+        }
+        return date;
+    }
+
+    // FORMATTING
+
+    addFormatToken('Y', 0, 0, function () {
+        var y = this.year();
+        return y <= 9999 ? '' + y : '+' + y;
+    });
+
+    addFormatToken(0, ['YY', 2], 0, function () {
+        return this.year() % 100;
+    });
+
+    addFormatToken(0, ['YYYY',   4],       0, 'year');
+    addFormatToken(0, ['YYYYY',  5],       0, 'year');
+    addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
+
+    // ALIASES
+
+    addUnitAlias('year', 'y');
+
+    // PARSING
+
+    addRegexToken('Y',      matchSigned);
+    addRegexToken('YY',     match1to2, match2);
+    addRegexToken('YYYY',   match1to4, match4);
+    addRegexToken('YYYYY',  match1to6, match6);
+    addRegexToken('YYYYYY', match1to6, match6);
+
+    addParseToken(['YYYYY', 'YYYYYY'], YEAR);
+    addParseToken('YYYY', function (input, array) {
+        array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input);
+    });
+    addParseToken('YY', function (input, array) {
+        array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input);
+    });
+    addParseToken('Y', function (input, array) {
+        array[YEAR] = parseInt(input, 10);
+    });
+
+    // HELPERS
+
+    function daysInYear(year) {
+        return isLeapYear(year) ? 366 : 365;
+    }
+
+    function isLeapYear(year) {
+        return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
+    }
+
+    // HOOKS
+
+    utils_hooks__hooks.parseTwoDigitYear = function (input) {
+        return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
+    };
+
+    // MOMENTS
+
+    var getSetYear = makeGetSet('FullYear', true);
+
+    function getIsLeapYear () {
+        return isLeapYear(this.year());
+    }
+
+    // start-of-first-week - start-of-year
+    function firstWeekOffset(year, dow, doy) {
+        var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
+            fwd = 7 + dow - doy,
+            // first-week day local weekday -- which local weekday is fwd
+            fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
+
+        return -fwdlw + fwd - 1;
+    }
+
+    //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
+    function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
+        var localWeekday = (7 + weekday - dow) % 7,
+            weekOffset = firstWeekOffset(year, dow, doy),
+            dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
+            resYear, resDayOfYear;
+
+        if (dayOfYear <= 0) {
+            resYear = year - 1;
+            resDayOfYear = daysInYear(resYear) + dayOfYear;
+        } else if (dayOfYear > daysInYear(year)) {
+            resYear = year + 1;
+            resDayOfYear = dayOfYear - daysInYear(year);
+        } else {
+            resYear = year;
+            resDayOfYear = dayOfYear;
+        }
+
+        return {
+            year: resYear,
+            dayOfYear: resDayOfYear
+        };
+    }
+
+    function weekOfYear(mom, dow, doy) {
+        var weekOffset = firstWeekOffset(mom.year(), dow, doy),
+            week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
+            resWeek, resYear;
+
+        if (week < 1) {
+            resYear = mom.year() - 1;
+            resWeek = week + weeksInYear(resYear, dow, doy);
+        } else if (week > weeksInYear(mom.year(), dow, doy)) {
+            resWeek = week - weeksInYear(mom.year(), dow, doy);
+            resYear = mom.year() + 1;
+        } else {
+            resYear = mom.year();
+            resWeek = week;
+        }
+
+        return {
+            week: resWeek,
+            year: resYear
+        };
+    }
+
+    function weeksInYear(year, dow, doy) {
+        var weekOffset = firstWeekOffset(year, dow, doy),
+            weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
+        return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
+    }
+
+    // Pick the first defined of two or three arguments.
+    function defaults(a, b, c) {
+        if (a != null) {
+            return a;
+        }
+        if (b != null) {
+            return b;
+        }
+        return c;
+    }
+
+    function currentDateArray(config) {
+        // hooks is actually the exported moment object
+        var nowValue = new Date(utils_hooks__hooks.now());
+        if (config._useUTC) {
+            return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
+        }
+        return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
+    }
+
+    // convert an array to a date.
+    // the array should mirror the parameters below
+    // note: all values past the year are optional and will default to the lowest possible value.
+    // [year, month, day , hour, minute, second, millisecond]
+    function configFromArray (config) {
+        var i, date, input = [], currentDate, yearToUse;
+
+        if (config._d) {
+            return;
+        }
+
+        currentDate = currentDateArray(config);
+
+        //compute day of the year from weeks and weekdays
+        if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
+            dayOfYearFromWeekInfo(config);
+        }
+
+        //if the day of the year is set, figure out what it is
+        if (config._dayOfYear) {
+            yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
+
+            if (config._dayOfYear > daysInYear(yearToUse)) {
+                getParsingFlags(config)._overflowDayOfYear = true;
+            }
+
+            date = createUTCDate(yearToUse, 0, config._dayOfYear);
+            config._a[MONTH] = date.getUTCMonth();
+            config._a[DATE] = date.getUTCDate();
+        }
+
+        // Default to current date.
+        // * if no year, month, day of month are given, default to today
+        // * if day of month is given, default month and year
+        // * if month is given, default only year
+        // * if year is given, don't default anything
+        for (i = 0; i < 3 && config._a[i] == null; ++i) {
+            config._a[i] = input[i] = currentDate[i];
+        }
+
+        // Zero out whatever was not defaulted, including time
+        for (; i < 7; i++) {
+            config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
+        }
+
+        // Check for 24:00:00.000
+        if (config._a[HOUR] === 24 &&
+                config._a[MINUTE] === 0 &&
+                config._a[SECOND] === 0 &&
+                config._a[MILLISECOND] === 0) {
+            config._nextDay = true;
+            config._a[HOUR] = 0;
+        }
+
+        config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
+        // Apply timezone offset from input. The actual utcOffset can be changed
+        // with parseZone.
+        if (config._tzm != null) {
+            config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
+        }
+
+        if (config._nextDay) {
+            config._a[HOUR] = 24;
+        }
+    }
+
+    function dayOfYearFromWeekInfo(config) {
+        var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
+
+        w = config._w;
+        if (w.GG != null || w.W != null || w.E != null) {
+            dow = 1;
+            doy = 4;
+
+            // TODO: We need to take the current isoWeekYear, but that depends on
+            // how we interpret now (local, utc, fixed offset). So create
+            // a now version of current config (take local/utc/offset flags, and
+            // create now).
+            weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year);
+            week = defaults(w.W, 1);
+            weekday = defaults(w.E, 1);
+            if (weekday < 1 || weekday > 7) {
+                weekdayOverflow = true;
+            }
+        } else {
+            dow = config._locale._week.dow;
+            doy = config._locale._week.doy;
+
+            weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year);
+            week = defaults(w.w, 1);
+
+            if (w.d != null) {
+                // weekday -- low day numbers are considered next week
+                weekday = w.d;
+                if (weekday < 0 || weekday > 6) {
+                    weekdayOverflow = true;
+                }
+            } else if (w.e != null) {
+                // local weekday -- counting starts from begining of week
+                weekday = w.e + dow;
+                if (w.e < 0 || w.e > 6) {
+                    weekdayOverflow = true;
+                }
+            } else {
+                // default to begining of week
+                weekday = dow;
+            }
+        }
+        if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
+            getParsingFlags(config)._overflowWeeks = true;
+        } else if (weekdayOverflow != null) {
+            getParsingFlags(config)._overflowWeekday = true;
+        } else {
+            temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
+            config._a[YEAR] = temp.year;
+            config._dayOfYear = temp.dayOfYear;
+        }
+    }
+
+    // constant that refers to the ISO standard
+    utils_hooks__hooks.ISO_8601 = function () {};
+
+    // date from string and format string
+    function configFromStringAndFormat(config) {
+        // TODO: Move this to another part of the creation flow to prevent circular deps
+        if (config._f === utils_hooks__hooks.ISO_8601) {
+            configFromISO(config);
+            return;
+        }
+
+        config._a = [];
+        getParsingFlags(config).empty = true;
+
+        // This array is used to make a Date, either with `new Date` or `Date.UTC`
+        var string = '' + config._i,
+            i, parsedInput, tokens, token, skipped,
+            stringLength = string.length,
+            totalParsedInputLength = 0;
+
+        tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
+
+        for (i = 0; i < tokens.length; i++) {
+            token = tokens[i];
+            parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
+            // console.log('token', token, 'parsedInput', parsedInput,
+            //         'regex', getParseRegexForToken(token, config));
+            if (parsedInput) {
+                skipped = string.substr(0, string.indexOf(parsedInput));
+                if (skipped.length > 0) {
+                    getParsingFlags(config).unusedInput.push(skipped);
+                }
+                string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
+                totalParsedInputLength += parsedInput.length;
+            }
+            // don't parse if it's not a known token
+            if (formatTokenFunctions[token]) {
+                if (parsedInput) {
+                    getParsingFlags(config).empty = false;
+                }
+                else {
+                    getParsingFlags(config).unusedTokens.push(token);
+                }
+                addTimeToArrayFromToken(token, parsedInput, config);
+            }
+            else if (config._strict && !parsedInput) {
+                getParsingFlags(config).unusedTokens.push(token);
+            }
+        }
+
+        // add remaining unparsed input length to the string
+        getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
+        if (string.length > 0) {
+            getParsingFlags(config).unusedInput.push(string);
+        }
+
+        // clear _12h flag if hour is <= 12
+        if (getParsingFlags(config).bigHour === true &&
+                config._a[HOUR] <= 12 &&
+                config._a[HOUR] > 0) {
+            getParsingFlags(config).bigHour = undefined;
+        }
+
+        getParsingFlags(config).parsedDateParts = config._a.slice(0);
+        getParsingFlags(config).meridiem = config._meridiem;
+        // handle meridiem
+        config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
+
+        configFromArray(config);
+        checkOverflow(config);
+    }
+
+
+    function meridiemFixWrap (locale, hour, meridiem) {
+        var isPm;
+
+        if (meridiem == null) {
+            // nothing to do
+            return hour;
+        }
+        if (locale.meridiemHour != null) {
+            return locale.meridiemHour(hour, meridiem);
+        } else if (locale.isPM != null) {
+            // Fallback
+            isPm = locale.isPM(meridiem);
+            if (isPm && hour < 12) {
+                hour += 12;
+            }
+            if (!isPm && hour === 12) {
+                hour = 0;
+            }
+            return hour;
+        } else {
+            // this is not supposed to happen
+            return hour;
+        }
+    }
+
+    // date from string and array of format strings
+    function configFromStringAndArray(config) {
+        var tempConfig,
+            bestMoment,
+
+            scoreToBeat,
+            i,
+            currentScore;
+
+        if (config._f.length === 0) {
+            getParsingFlags(config).invalidFormat = true;
+            config._d = new Date(NaN);
+            return;
+        }
+
+        for (i = 0; i < config._f.length; i++) {
+            currentScore = 0;
+            tempConfig = copyConfig({}, config);
+            if (config._useUTC != null) {
+                tempConfig._useUTC = config._useUTC;
+            }
+            tempConfig._f = config._f[i];
+            configFromStringAndFormat(tempConfig);
+
+            if (!valid__isValid(tempConfig)) {
+                continue;
+            }
+
+            // if there is any input that was not parsed add a penalty for that format
+            currentScore += getParsingFlags(tempConfig).charsLeftOver;
+
+            //or tokens
+            currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
+
+            getParsingFlags(tempConfig).score = currentScore;
+
+            if (scoreToBeat == null || currentScore < scoreToBeat) {
+                scoreToBeat = currentScore;
+                bestMoment = tempConfig;
+            }
+        }
+
+        extend(config, bestMoment || tempConfig);
+    }
+
+    function configFromObject(config) {
+        if (config._d) {
+            return;
+        }
+
+        var i = normalizeObjectUnits(config._i);
+        config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
+            return obj && parseInt(obj, 10);
+        });
+
+        configFromArray(config);
+    }
+
+    function createFromConfig (config) {
+        var res = new Moment(checkOverflow(prepareConfig(config)));
+        if (res._nextDay) {
+            // Adding is smart enough around DST
+            res.add(1, 'd');
+            res._nextDay = undefined;
+        }
+
+        return res;
+    }
+
+    function prepareConfig (config) {
+        var input = config._i,
+            format = config._f;
+
+        config._locale = config._locale || locale_locales__getLocale(config._l);
+
+        if (input === null || (format === undefined && input === '')) {
+            return valid__createInvalid({nullInput: true});
+        }
+
+        if (typeof input === 'string') {
+            config._i = input = config._locale.preparse(input);
+        }
+
+        if (isMoment(input)) {
+            return new Moment(checkOverflow(input));
+        } else if (isArray(format)) {
+            configFromStringAndArray(config);
+        } else if (format) {
+            configFromStringAndFormat(config);
+        } else if (isDate(input)) {
+            config._d = input;
+        } else {
+            configFromInput(config);
+        }
+
+        if (!valid__isValid(config)) {
+            config._d = null;
+        }
+
+        return config;
+    }
+
+    function configFromInput(config) {
+        var input = config._i;
+        if (input === undefined) {
+            config._d = new Date(utils_hooks__hooks.now());
+        } else if (isDate(input)) {
+            config._d = new Date(input.valueOf());
+        } else if (typeof input === 'string') {
+            configFromString(config);
+        } else if (isArray(input)) {
+            config._a = map(input.slice(0), function (obj) {
+                return parseInt(obj, 10);
+            });
+            configFromArray(config);
+        } else if (typeof(input) === 'object') {
+            configFromObject(config);
+        } else if (typeof(input) === 'number') {
+            // from milliseconds
+            config._d = new Date(input);
+        } else {
+            utils_hooks__hooks.createFromInputFallback(config);
+        }
+    }
+
+    function createLocalOrUTC (input, format, locale, strict, isUTC) {
+        var c = {};
+
+        if (typeof(locale) === 'boolean') {
+            strict = locale;
+            locale = undefined;
+        }
+        // object construction must be done this way.
+        // https://github.com/moment/moment/issues/1423
+        c._isAMomentObject = true;
+        c._useUTC = c._isUTC = isUTC;
+        c._l = locale;
+        c._i = input;
+        c._f = format;
+        c._strict = strict;
+
+        return createFromConfig(c);
+    }
+
+    function local__createLocal (input, format, locale, strict) {
+        return createLocalOrUTC(input, format, locale, strict, false);
+    }
+
+    var prototypeMin = deprecate(
+         'moment().min is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548',
+         function () {
+             var other = local__createLocal.apply(null, arguments);
+             if (this.isValid() && other.isValid()) {
+                 return other < this ? this : other;
+             } else {
+                 return valid__createInvalid();
+             }
+         }
+     );
+
+    var prototypeMax = deprecate(
+        'moment().max is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548',
+        function () {
+            var other = local__createLocal.apply(null, arguments);
+            if (this.isValid() && other.isValid()) {
+                return other > this ? this : other;
+            } else {
+                return valid__createInvalid();
+            }
+        }
+    );
+
+    // Pick a moment m from moments so that m[fn](other) is true for all
+    // other. This relies on the function fn to be transitive.
+    //
+    // moments should either be an array of moment objects or an array, whose
+    // first element is an array of moment objects.
+    function pickBy(fn, moments) {
+        var res, i;
+        if (moments.length === 1 && isArray(moments[0])) {
+            moments = moments[0];
+        }
+        if (!moments.length) {
+            return local__createLocal();
+        }
+        res = moments[0];
+        for (i = 1; i < moments.length; ++i) {
+            if (!moments[i].isValid() || moments[i][fn](res)) {
+                res = moments[i];
+            }
+        }
+        return res;
+    }
+
+    // TODO: Use [].sort instead?
+    function min () {
+        var args = [].slice.call(arguments, 0);
+
+        return pickBy('isBefore', args);
+    }
+
+    function max () {
+        var args = [].slice.call(arguments, 0);
+
+        return pickBy('isAfter', args);
+    }
+
+    var now = function () {
+        return Date.now ? Date.now() : +(new Date());
+    };
+
+    function Duration (duration) {
+        var normalizedInput = normalizeObjectUnits(duration),
+            years = normalizedInput.year || 0,
+            quarters = normalizedInput.quarter || 0,
+            months = normalizedInput.month || 0,
+            weeks = normalizedInput.week || 0,
+            days = normalizedInput.day || 0,
+            hours = normalizedInput.hour || 0,
+            minutes = normalizedInput.minute || 0,
+            seconds = normalizedInput.second || 0,
+            milliseconds = normalizedInput.millisecond || 0;
+
+        // representation for dateAddRemove
+        this._milliseconds = +milliseconds +
+            seconds * 1e3 + // 1000
+            minutes * 6e4 + // 1000 * 60
+            hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
+        // Because of dateAddRemove treats 24 hours as different from a
+        // day when working around DST, we need to store them separately
+        this._days = +days +
+            weeks * 7;
+        // It is impossible translate months into days without knowing
+        // which months you are are talking about, so we have to store
+        // it separately.
+        this._months = +months +
+            quarters * 3 +
+            years * 12;
+
+        this._data = {};
+
+        this._locale = locale_locales__getLocale();
+
+        this._bubble();
+    }
+
+    function isDuration (obj) {
+        return obj instanceof Duration;
+    }
+
+    // FORMATTING
+
+    function offset (token, separator) {
+        addFormatToken(token, 0, 0, function () {
+            var offset = this.utcOffset();
+            var sign = '+';
+            if (offset < 0) {
+                offset = -offset;
+                sign = '-';
+            }
+            return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
+        });
+    }
+
+    offset('Z', ':');
+    offset('ZZ', '');
+
+    // PARSING
+
+    addRegexToken('Z',  matchShortOffset);
+    addRegexToken('ZZ', matchShortOffset);
+    addParseToken(['Z', 'ZZ'], function (input, array, config) {
+        config._useUTC = true;
+        config._tzm = offsetFromString(matchShortOffset, input);
+    });
+
+    // HELPERS
+
+    // timezone chunker
+    // '+10:00' > ['10',  '00']
+    // '-1530'  > ['-15', '30']
+    var chunkOffset = /([\+\-]|\d\d)/gi;
+
+    function offsetFromString(matcher, string) {
+        var matches = ((string || '').match(matcher) || []);
+        var chunk   = matches[matches.length - 1] || [];
+        var parts   = (chunk + '').match(chunkOffset) || ['-', 0, 0];
+        var minutes = +(parts[1] * 60) + toInt(parts[2]);
+
+        return parts[0] === '+' ? minutes : -minutes;
+    }
+
+    // Return a moment from input, that is local/utc/zone equivalent to model.
+    function cloneWithOffset(input, model) {
+        var res, diff;
+        if (model._isUTC) {
+            res = model.clone();
+            diff = (isMoment(input) || isDate(input) ? input.valueOf() : local__createLocal(input).valueOf()) - res.valueOf();
+            // Use low-level api, because this fn is low-level api.
+            res._d.setTime(res._d.valueOf() + diff);
+            utils_hooks__hooks.updateOffset(res, false);
+            return res;
+        } else {
+            return local__createLocal(input).local();
+        }
+    }
+
+    function getDateOffset (m) {
+        // On Firefox.24 Date#getTimezoneOffset returns a floating point.
+        // https://github.com/moment/moment/pull/1871
+        return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
+    }
+
+    // HOOKS
+
+    // This function will be called whenever a moment is mutated.
+    // It is intended to keep the offset in sync with the timezone.
+    utils_hooks__hooks.updateOffset = function () {};
+
+    // MOMENTS
+
+    // keepLocalTime = true means only change the timezone, without
+    // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
+    // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
+    // +0200, so we adjust the time as needed, to be valid.
+    //
+    // Keeping the time actually adds/subtracts (one hour)
+    // from the actual represented time. That is why we call updateOffset
+    // a second time. In case it wants us to change the offset again
+    // _changeInProgress == true case, then we have to adjust, because
+    // there is no such time in the given timezone.
+    function getSetOffset (input, keepLocalTime) {
+        var offset = this._offset || 0,
+            localAdjust;
+        if (!this.isValid()) {
+            return input != null ? this : NaN;
+        }
+        if (input != null) {
+            if (typeof input === 'string') {
+                input = offsetFromString(matchShortOffset, input);
+            } else if (Math.abs(input) < 16) {
+                input = input * 60;
+            }
+            if (!this._isUTC && keepLocalTime) {
+                localAdjust = getDateOffset(this);
+            }
+            this._offset = input;
+            this._isUTC = true;
+            if (localAdjust != null) {
+                this.add(localAdjust, 'm');
+            }
+            if (offset !== input) {
+                if (!keepLocalTime || this._changeInProgress) {
+                    add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false);
+                } else if (!this._changeInProgress) {
+                    this._changeInProgress = true;
+                    utils_hooks__hooks.updateOffset(this, true);
+                    this._changeInProgress = null;
+                }
+            }
+            return this;
+        } else {
+            return this._isUTC ? offset : getDateOffset(this);
+        }
+    }
+
+    function getSetZone (input, keepLocalTime) {
+        if (input != null) {
+            if (typeof input !== 'string') {
+                input = -input;
+            }
+
+            this.utcOffset(input, keepLocalTime);
+
+            return this;
+        } else {
+            return -this.utcOffset();
+        }
+    }
+
+    function setOffsetToUTC (keepLocalTime) {
+        return this.utcOffset(0, keepLocalTime);
+    }
+
+    function setOffsetToLocal (keepLocalTime) {
+        if (this._isUTC) {
+            this.utcOffset(0, keepLocalTime);
+            this._isUTC = false;
+
+            if (keepLocalTime) {
+                this.subtract(getDateOffset(this), 'm');
+            }
+        }
+        return this;
+    }
+
+    function setOffsetToParsedOffset () {
+        if (this._tzm) {
+            this.utcOffset(this._tzm);
+        } else if (typeof this._i === 'string') {
+            this.utcOffset(offsetFromString(matchOffset, this._i));
+        }
+        return this;
+    }
+
+    function hasAlignedHourOffset (input) {
+        if (!this.isValid()) {
+            return false;
+        }
+        input = input ? local__createLocal(input).utcOffset() : 0;
+
+        return (this.utcOffset() - input) % 60 === 0;
+    }
+
+    function isDaylightSavingTime () {
+        return (
+            this.utcOffset() > this.clone().month(0).utcOffset() ||
+            this.utcOffset() > this.clone().month(5).utcOffset()
+        );
+    }
+
+    function isDaylightSavingTimeShifted () {
+        if (!isUndefined(this._isDSTShifted)) {
+            return this._isDSTShifted;
+        }
+
+        var c = {};
+
+        copyConfig(c, this);
+        c = prepareConfig(c);
+
+        if (c._a) {
+            var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a);
+            this._isDSTShifted = this.isValid() &&
+                compareArrays(c._a, other.toArray()) > 0;
+        } else {
+            this._isDSTShifted = false;
+        }
+
+        return this._isDSTShifted;
+    }
+
+    function isLocal () {
+        return this.isValid() ? !this._isUTC : false;
+    }
+
+    function isUtcOffset () {
+        return this.isValid() ? this._isUTC : false;
+    }
+
+    function isUtc () {
+        return this.isValid() ? this._isUTC && this._offset === 0 : false;
+    }
+
+    // ASP.NET json date format regex
+    var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/;
+
+    // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
+    // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
+    // and further modified to allow for strings containing both week and day
+    var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;
+
+    function create__createDuration (input, key) {
+        var duration = input,
+            // matching against regexp is expensive, do it on demand
+            match = null,
+            sign,
+            ret,
+            diffRes;
+
+        if (isDuration(input)) {
+            duration = {
+                ms : input._milliseconds,
+                d  : input._days,
+                M  : input._months
+            };
+        } else if (typeof input === 'number') {
+            duration = {};
+            if (key) {
+                duration[key] = input;
+            } else {
+                duration.milliseconds = input;
+            }
+        } else if (!!(match = aspNetRegex.exec(input))) {
+            sign = (match[1] === '-') ? -1 : 1;
+            duration = {
+                y  : 0,
+                d  : toInt(match[DATE])        * sign,
+                h  : toInt(match[HOUR])        * sign,
+                m  : toInt(match[MINUTE])      * sign,
+                s  : toInt(match[SECOND])      * sign,
+                ms : toInt(match[MILLISECOND]) * sign
+            };
+        } else if (!!(match = isoRegex.exec(input))) {
+            sign = (match[1] === '-') ? -1 : 1;
+            duration = {
+                y : parseIso(match[2], sign),
+                M : parseIso(match[3], sign),
+                w : parseIso(match[4], sign),
+                d : parseIso(match[5], sign),
+                h : parseIso(match[6], sign),
+                m : parseIso(match[7], sign),
+                s : parseIso(match[8], sign)
+            };
+        } else if (duration == null) {// checks for null or undefined
+            duration = {};
+        } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
+            diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to));
+
+            duration = {};
+            duration.ms = diffRes.milliseconds;
+            duration.M = diffRes.months;
+        }
+
+        ret = new Duration(duration);
+
+        if (isDuration(input) && hasOwnProp(input, '_locale')) {
+            ret._locale = input._locale;
+        }
+
+        return ret;
+    }
+
+    create__createDuration.fn = Duration.prototype;
+
+    function parseIso (inp, sign) {
+        // We'd normally use ~~inp for this, but unfortunately it also
+        // converts floats to ints.
+        // inp may be undefined, so careful calling replace on it.
+        var res = inp && parseFloat(inp.replace(',', '.'));
+        // apply sign while we're at it
+        return (isNaN(res) ? 0 : res) * sign;
+    }
+
+    function positiveMomentsDifference(base, other) {
+        var res = {milliseconds: 0, months: 0};
+
+        res.months = other.month() - base.month() +
+            (other.year() - base.year()) * 12;
+        if (base.clone().add(res.months, 'M').isAfter(other)) {
+            --res.months;
+        }
+
+        res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
+
+        return res;
+    }
+
+    function momentsDifference(base, other) {
+        var res;
+        if (!(base.isValid() && other.isValid())) {
+            return {milliseconds: 0, months: 0};
+        }
+
+        other = cloneWithOffset(other, base);
+        if (base.isBefore(other)) {
+            res = positiveMomentsDifference(base, other);
+        } else {
+            res = positiveMomentsDifference(other, base);
+            res.milliseconds = -res.milliseconds;
+            res.months = -res.months;
+        }
+
+        return res;
+    }
+
+    function absRound (number) {
+        if (number < 0) {
+            return Math.round(-1 * number) * -1;
+        } else {
+            return Math.round(number);
+        }
+    }
+
+    // TODO: remove 'name' arg after deprecation is removed
+    function createAdder(direction, name) {
+        return function (val, period) {
+            var dur, tmp;
+            //invert the arguments, but complain about it
+            if (period !== null && !isNaN(+period)) {
+                deprecateSimple(name, 'moment().' + name  + '(period, number) is deprecated. Please use moment().' + name + '(number, period).');
+                tmp = val; val = period; period = tmp;
+            }
+
+            val = typeof val === 'string' ? +val : val;
+            dur = create__createDuration(val, period);
+            add_subtract__addSubtract(this, dur, direction);
+            return this;
+        };
+    }
+
+    function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) {
+        var milliseconds = duration._milliseconds,
+            days = absRound(duration._days),
+            months = absRound(duration._months);
+
+        if (!mom.isValid()) {
+            // No op
+            return;
+        }
+
+        updateOffset = updateOffset == null ? true : updateOffset;
+
+        if (milliseconds) {
+            mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
+        }
+        if (days) {
+            get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding);
+        }
+        if (months) {
+            setMonth(mom, get_set__get(mom, 'Month') + months * isAdding);
+        }
+        if (updateOffset) {
+            utils_hooks__hooks.updateOffset(mom, days || months);
+        }
+    }
+
+    var add_subtract__add      = createAdder(1, 'add');
+    var add_subtract__subtract = createAdder(-1, 'subtract');
+
+    function moment_calendar__calendar (time, formats) {
+        // We want to compare the start of today, vs this.
+        // Getting start-of-today depends on whether we're local/utc/offset or not.
+        var now = time || local__createLocal(),
+            sod = cloneWithOffset(now, this).startOf('day'),
+            diff = this.diff(sod, 'days', true),
+            format = diff < -6 ? 'sameElse' :
+                diff < -1 ? 'lastWeek' :
+                diff < 0 ? 'lastDay' :
+                diff < 1 ? 'sameDay' :
+                diff < 2 ? 'nextDay' :
+                diff < 7 ? 'nextWeek' : 'sameElse';
+
+        var output = formats && (isFunction(formats[format]) ? formats[format]() : formats[format]);
+
+        return this.format(output || this.localeData().calendar(format, this, local__createLocal(now)));
+    }
+
+    function clone () {
+        return new Moment(this);
+    }
+
+    function isAfter (input, units) {
+        var localInput = isMoment(input) ? input : local__createLocal(input);
+        if (!(this.isValid() && localInput.isValid())) {
+            return false;
+        }
+        units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
+        if (units === 'millisecond') {
+            return this.valueOf() > localInput.valueOf();
+        } else {
+            return localInput.valueOf() < this.clone().startOf(units).valueOf();
+        }
+    }
+
+    function isBefore (input, units) {
+        var localInput = isMoment(input) ? input : local__createLocal(input);
+        if (!(this.isValid() && localInput.isValid())) {
+            return false;
+        }
+        units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
+        if (units === 'millisecond') {
+            return this.valueOf() < localInput.valueOf();
+        } else {
+            return this.clone().endOf(units).valueOf() < localInput.valueOf();
+        }
+    }
+
+    function isBetween (from, to, units, inclusivity) {
+        inclusivity = inclusivity || '()';
+        return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
+            (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
+    }
+
+    function isSame (input, units) {
+        var localInput = isMoment(input) ? input : local__createLocal(input),
+            inputMs;
+        if (!(this.isValid() && localInput.isValid())) {
+            return false;
+        }
+        units = normalizeUnits(units || 'millisecond');
+        if (units === 'millisecond') {
+            return this.valueOf() === localInput.valueOf();
+        } else {
+            inputMs = localInput.valueOf();
+            return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
+        }
+    }
+
+    function isSameOrAfter (input, units) {
+        return this.isSame(input, units) || this.isAfter(input,units);
+    }
+
+    function isSameOrBefore (input, units) {
+        return this.isSame(input, units) || this.isBefore(input,units);
+    }
+
+    function diff (input, units, asFloat) {
+        var that,
+            zoneDelta,
+            delta, output;
+
+        if (!this.isValid()) {
+            return NaN;
+        }
+
+        that = cloneWithOffset(input, this);
+
+        if (!that.isValid()) {
+            return NaN;
+        }
+
+        zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
+
+        units = normalizeUnits(units);
+
+        if (units === 'year' || units === 'month' || units === 'quarter') {
+            output = monthDiff(this, that);
+            if (units === 'quarter') {
+                output = output / 3;
+            } else if (units === 'year') {
+                output = output / 12;
+            }
+        } else {
+            delta = this - that;
+            output = units === 'second' ? delta / 1e3 : // 1000
+                units === 'minute' ? delta / 6e4 : // 1000 * 60
+                units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
+                units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
+                units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
+                delta;
+        }
+        return asFloat ? output : absFloor(output);
+    }
+
+    function monthDiff (a, b) {
+        // difference in months
+        var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
+            // b is in (anchor - 1 month, anchor + 1 month)
+            anchor = a.clone().add(wholeMonthDiff, 'months'),
+            anchor2, adjust;
+
+        if (b - anchor < 0) {
+            anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
+            // linear across the month
+            adjust = (b - anchor) / (anchor - anchor2);
+        } else {
+            anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
+            // linear across the month
+            adjust = (b - anchor) / (anchor2 - anchor);
+        }
+
+        //check for negative zero, return zero if negative zero
+        return -(wholeMonthDiff + adjust) || 0;
+    }
+
+    utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
+    utils_hooks__hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
+
+    function toString () {
+        return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
+    }
+
+    function moment_format__toISOString () {
+        var m = this.clone().utc();
+        if (0 < m.year() && m.year() <= 9999) {
+            if (isFunction(Date.prototype.toISOString)) {
+                // native implementation is ~50x faster, use it when we can
+                return this.toDate().toISOString();
+            } else {
+                return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+            }
+        } else {
+            return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+        }
+    }
+
+    function format (inputString) {
+        if (!inputString) {
+            inputString = this.isUtc() ? utils_hooks__hooks.defaultFormatUtc : utils_hooks__hooks.defaultFormat;
+        }
+        var output = formatMoment(this, inputString);
+        return this.localeData().postformat(output);
+    }
+
+    function from (time, withoutSuffix) {
+        if (this.isValid() &&
+                ((isMoment(time) && time.isValid()) ||
+                 local__createLocal(time).isValid())) {
+            return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
+        } else {
+            return this.localeData().invalidDate();
+        }
+    }
+
+    function fromNow (withoutSuffix) {
+        return this.from(local__createLocal(), withoutSuffix);
+    }
+
+    function to (time, withoutSuffix) {
+        if (this.isValid() &&
+                ((isMoment(time) && time.isValid()) ||
+                 local__createLocal(time).isValid())) {
+            return create__createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
+        } else {
+            return this.localeData().invalidDate();
+        }
+    }
+
+    function toNow (withoutSuffix) {
+        return this.to(local__createLocal(), withoutSuffix);
+    }
+
+    // If passed a locale key, it will set the locale for this
+    // instance.  Otherwise, it will return the locale configuration
+    // variables for this instance.
+    function locale (key) {
+        var newLocaleData;
+
+        if (key === undefined) {
+            return this._locale._abbr;
+        } else {
+            newLocaleData = locale_locales__getLocale(key);
+            if (newLocaleData != null) {
+                this._locale = newLocaleData;
+            }
+            return this;
+        }
+    }
+
+    var lang = deprecate(
+        'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
+        function (key) {
+            if (key === undefined) {
+                return this.localeData();
+            } else {
+                return this.locale(key);
+            }
+        }
+    );
+
+    function localeData () {
+        return this._locale;
+    }
+
+    function startOf (units) {
+        units = normalizeUnits(units);
+        // the following switch intentionally omits break keywords
+        // to utilize falling through the cases.
+        switch (units) {
+        case 'year':
+            this.month(0);
+            /* falls through */
+        case 'quarter':
+        case 'month':
+            this.date(1);
+            /* falls through */
+        case 'week':
+        case 'isoWeek':
+        case 'day':
+        case 'date':
+            this.hours(0);
+            /* falls through */
+        case 'hour':
+            this.minutes(0);
+            /* falls through */
+        case 'minute':
+            this.seconds(0);
+            /* falls through */
+        case 'second':
+            this.milliseconds(0);
+        }
+
+        // weeks are a special case
+        if (units === 'week') {
+            this.weekday(0);
+        }
+        if (units === 'isoWeek') {
+            this.isoWeekday(1);
+        }
+
+        // quarters are also special
+        if (units === 'quarter') {
+            this.month(Math.floor(this.month() / 3) * 3);
+        }
+
+        return this;
+    }
+
+    function endOf (units) {
+        units = normalizeUnits(units);
+        if (units === undefined || units === 'millisecond') {
+            return this;
+        }
+
+        // 'date' is an alias for 'day', so it should be considered as such.
+        if (units === 'date') {
+            units = 'day';
+        }
+
+        return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
+    }
+
+    function to_type__valueOf () {
+        return this._d.valueOf() - ((this._offset || 0) * 60000);
+    }
+
+    function unix () {
+        return Math.floor(this.valueOf() / 1000);
+    }
+
+    function toDate () {
+        return this._offset ? new Date(this.valueOf()) : this._d;
+    }
+
+    function toArray () {
+        var m = this;
+        return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
+    }
+
+    function toObject () {
+        var m = this;
+        return {
+            years: m.year(),
+            months: m.month(),
+            date: m.date(),
+            hours: m.hours(),
+            minutes: m.minutes(),
+            seconds: m.seconds(),
+            milliseconds: m.milliseconds()
+        };
+    }
+
+    function toJSON () {
+        // new Date(NaN).toJSON() === null
+        return this.isValid() ? this.toISOString() : null;
+    }
+
+    function moment_valid__isValid () {
+        return valid__isValid(this);
+    }
+
+    function parsingFlags () {
+        return extend({}, getParsingFlags(this));
+    }
+
+    function invalidAt () {
+        return getParsingFlags(this).overflow;
+    }
+
+    function creationData() {
+        return {
+            input: this._i,
+            format: this._f,
+            locale: this._locale,
+            isUTC: this._isUTC,
+            strict: this._strict
+        };
+    }
+
+    // FORMATTING
+
+    addFormatToken(0, ['gg', 2], 0, function () {
+        return this.weekYear() % 100;
+    });
+
+    addFormatToken(0, ['GG', 2], 0, function () {
+        return this.isoWeekYear() % 100;
+    });
+
+    function addWeekYearFormatToken (token, getter) {
+        addFormatToken(0, [token, token.length], 0, getter);
+    }
+
+    addWeekYearFormatToken('gggg',     'weekYear');
+    addWeekYearFormatToken('ggggg',    'weekYear');
+    addWeekYearFormatToken('GGGG',  'isoWeekYear');
+    addWeekYearFormatToken('GGGGG', 'isoWeekYear');
+
+    // ALIASES
+
+    addUnitAlias('weekYear', 'gg');
+    addUnitAlias('isoWeekYear', 'GG');
+
+    // PARSING
+
+    addRegexToken('G',      matchSigned);
+    addRegexToken('g',      matchSigned);
+    addRegexToken('GG',     match1to2, match2);
+    addRegexToken('gg',     match1to2, match2);
+    addRegexToken('GGGG',   match1to4, match4);
+    addRegexToken('gggg',   match1to4, match4);
+    addRegexToken('GGGGG',  match1to6, match6);
+    addRegexToken('ggggg',  match1to6, match6);
+
+    addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
+        week[token.substr(0, 2)] = toInt(input);
+    });
+
+    addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
+        week[token] = utils_hooks__hooks.parseTwoDigitYear(input);
+    });
+
+    // MOMENTS
+
+    function getSetWeekYear (input) {
+        return getSetWeekYearHelper.call(this,
+                input,
+                this.week(),
+                this.weekday(),
+                this.localeData()._week.dow,
+                this.localeData()._week.doy);
+    }
+
+    function getSetISOWeekYear (input) {
+        return getSetWeekYearHelper.call(this,
+                input, this.isoWeek(), this.isoWeekday(), 1, 4);
+    }
+
+    function getISOWeeksInYear () {
+        return weeksInYear(this.year(), 1, 4);
+    }
+
+    function getWeeksInYear () {
+        var weekInfo = this.localeData()._week;
+        return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
+    }
+
+    function getSetWeekYearHelper(input, week, weekday, dow, doy) {
+        var weeksTarget;
+        if (input == null) {
+            return weekOfYear(this, dow, doy).year;
+        } else {
+            weeksTarget = weeksInYear(input, dow, doy);
+            if (week > weeksTarget) {
+                week = weeksTarget;
+            }
+            return setWeekAll.call(this, input, week, weekday, dow, doy);
+        }
+    }
+
+    function setWeekAll(weekYear, week, weekday, dow, doy) {
+        var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
+            date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
+
+        this.year(date.getUTCFullYear());
+        this.month(date.getUTCMonth());
+        this.date(date.getUTCDate());
+        return this;
+    }
+
+    // FORMATTING
+
+    addFormatToken('Q', 0, 'Qo', 'quarter');
+
+    // ALIASES
+
+    addUnitAlias('quarter', 'Q');
+
+    // PARSING
+
+    addRegexToken('Q', match1);
+    addParseToken('Q', function (input, array) {
+        array[MONTH] = (toInt(input) - 1) * 3;
+    });
+
+    // MOMENTS
+
+    function getSetQuarter (input) {
+        return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
+    }
+
+    // FORMATTING
+
+    addFormatToken('w', ['ww', 2], 'wo', 'week');
+    addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
+
+    // ALIASES
+
+    addUnitAlias('week', 'w');
+    addUnitAlias('isoWeek', 'W');
+
+    // PARSING
+
+    addRegexToken('w',  match1to2);
+    addRegexToken('ww', match1to2, match2);
+    addRegexToken('W',  match1to2);
+    addRegexToken('WW', match1to2, match2);
+
+    addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
+        week[token.substr(0, 1)] = toInt(input);
+    });
+
+    // HELPERS
+
+    // LOCALES
+
+    function localeWeek (mom) {
+        return weekOfYear(mom, this._week.dow, this._week.doy).week;
+    }
+
+    var defaultLocaleWeek = {
+        dow : 0, // Sunday is the first day of the week.
+        doy : 6  // The week that contains Jan 1st is the first week of the year.
+    };
+
+    function localeFirstDayOfWeek () {
+        return this._week.dow;
+    }
+
+    function localeFirstDayOfYear () {
+        return this._week.doy;
+    }
+
+    // MOMENTS
+
+    function getSetWeek (input) {
+        var week = this.localeData().week(this);
+        return input == null ? week : this.add((input - week) * 7, 'd');
+    }
+
+    function getSetISOWeek (input) {
+        var week = weekOfYear(this, 1, 4).week;
+        return input == null ? week : this.add((input - week) * 7, 'd');
+    }
+
+    // FORMATTING
+
+    addFormatToken('D', ['DD', 2], 'Do', 'date');
+
+    // ALIASES
+
+    addUnitAlias('date', 'D');
+
+    // PARSING
+
+    addRegexToken('D',  match1to2);
+    addRegexToken('DD', match1to2, match2);
+    addRegexToken('Do', function (isStrict, locale) {
+        return isStrict ? locale._ordinalParse : locale._ordinalParseLenient;
+    });
+
+    addParseToken(['D', 'DD'], DATE);
+    addParseToken('Do', function (input, array) {
+        array[DATE] = toInt(input.match(match1to2)[0], 10);
+    });
+
+    // MOMENTS
+
+    var getSetDayOfMonth = makeGetSet('Date', true);
+
+    // FORMATTING
+
+    addFormatToken('d', 0, 'do', 'day');
+
+    addFormatToken('dd', 0, 0, function (format) {
+        return this.localeData().weekdaysMin(this, format);
+    });
+
+    addFormatToken('ddd', 0, 0, function (format) {
+        return this.localeData().weekdaysShort(this, format);
+    });
+
+    addFormatToken('dddd', 0, 0, function (format) {
+        return this.localeData().weekdays(this, format);
+    });
+
+    addFormatToken('e', 0, 0, 'weekday');
+    addFormatToken('E', 0, 0, 'isoWeekday');
+
+    // ALIASES
+
+    addUnitAlias('day', 'd');
+    addUnitAlias('weekday', 'e');
+    addUnitAlias('isoWeekday', 'E');
+
+    // PARSING
+
+    addRegexToken('d',    match1to2);
+    addRegexToken('e',    match1to2);
+    addRegexToken('E',    match1to2);
+    addRegexToken('dd',   function (isStrict, locale) {
+        return locale.weekdaysMinRegex(isStrict);
+    });
+    addRegexToken('ddd',   function (isStrict, locale) {
+        return locale.weekdaysShortRegex(isStrict);
+    });
+    addRegexToken('dddd',   function (isStrict, locale) {
+        return locale.weekdaysRegex(isStrict);
+    });
+
+    addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
+        var weekday = config._locale.weekdaysParse(input, token, config._strict);
+        // if we didn't get a weekday name, mark the date as invalid
+        if (weekday != null) {
+            week.d = weekday;
+        } else {
+            getParsingFlags(config).invalidWeekday = input;
+        }
+    });
+
+    addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
+        week[token] = toInt(input);
+    });
+
+    // HELPERS
+
+    function parseWeekday(input, locale) {
+        if (typeof input !== 'string') {
+            return input;
+        }
+
+        if (!isNaN(input)) {
+            return parseInt(input, 10);
+        }
+
+        input = locale.weekdaysParse(input);
+        if (typeof input === 'number') {
+            return input;
+        }
+
+        return null;
+    }
+
+    // LOCALES
+
+    var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
+    function localeWeekdays (m, format) {
+        return isArray(this._weekdays) ? this._weekdays[m.day()] :
+            this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
+    }
+
+    var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
+    function localeWeekdaysShort (m) {
+        return this._weekdaysShort[m.day()];
+    }
+
+    var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
+    function localeWeekdaysMin (m) {
+        return this._weekdaysMin[m.day()];
+    }
+
+    function day_of_week__handleStrictParse(weekdayName, format, strict) {
+        var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
+        if (!this._weekdaysParse) {
+            this._weekdaysParse = [];
+            this._shortWeekdaysParse = [];
+            this._minWeekdaysParse = [];
+
+            for (i = 0; i < 7; ++i) {
+                mom = create_utc__createUTC([2000, 1]).day(i);
+                this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
+                this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
+                this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
+            }
+        }
+
+        if (strict) {
+            if (format === 'dddd') {
+                ii = indexOf.call(this._weekdaysParse, llc);
+                return ii !== -1 ? ii : null;
+            } else if (format === 'ddd') {
+                ii = indexOf.call(this._shortWeekdaysParse, llc);
+                return ii !== -1 ? ii : null;
+            } else {
+                ii = indexOf.call(this._minWeekdaysParse, llc);
+                return ii !== -1 ? ii : null;
+            }
+        } else {
+            if (format === 'dddd') {
+                ii = indexOf.call(this._weekdaysParse, llc);
+                if (ii !== -1) {
+                    return ii;
+                }
+                ii = indexOf.call(this._shortWeekdaysParse, llc);
+                if (ii !== -1) {
+                    return ii;
+                }
+                ii = indexOf.call(this._minWeekdaysParse, llc);
+                return ii !== -1 ? ii : null;
+            } else if (format === 'ddd') {
+                ii = indexOf.call(this._shortWeekdaysParse, llc);
+                if (ii !== -1) {
+                    return ii;
+                }
+                ii = indexOf.call(this._weekdaysParse, llc);
+                if (ii !== -1) {
+                    return ii;
+                }
+                ii = indexOf.call(this._minWeekdaysParse, llc);
+                return ii !== -1 ? ii : null;
+            } else {
+                ii = indexOf.call(this._minWeekdaysParse, llc);
+                if (ii !== -1) {
+                    return ii;
+                }
+                ii = indexOf.call(this._weekdaysParse, llc);
+                if (ii !== -1) {
+                    return ii;
+                }
+                ii = indexOf.call(this._shortWeekdaysParse, llc);
+                return ii !== -1 ? ii : null;
+            }
+        }
+    }
+
+    function localeWeekdaysParse (weekdayName, format, strict) {
+        var i, mom, regex;
+
+        if (this._weekdaysParseExact) {
+            return day_of_week__handleStrictParse.call(this, weekdayName, format, strict);
+        }
+
+        if (!this._weekdaysParse) {
+            this._weekdaysParse = [];
+            this._minWeekdaysParse = [];
+            this._shortWeekdaysParse = [];
+            this._fullWeekdaysParse = [];
+        }
+
+        for (i = 0; i < 7; i++) {
+            // make the regex if we don't have it already
+
+            mom = create_utc__createUTC([2000, 1]).day(i);
+            if (strict && !this._fullWeekdaysParse[i]) {
+                this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
+                this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
+                this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
+            }
+            if (!this._weekdaysParse[i]) {
+                regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
+                this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
+            }
+            // test the regex
+            if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
+                return i;
+            } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
+                return i;
+            } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
+                return i;
+            } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
+                return i;
+            }
+        }
+    }
+
+    // MOMENTS
+
+    function getSetDayOfWeek (input) {
+        if (!this.isValid()) {
+            return input != null ? this : NaN;
+        }
+        var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
+        if (input != null) {
+            input = parseWeekday(input, this.localeData());
+            return this.add(input - day, 'd');
+        } else {
+            return day;
+        }
+    }
+
+    function getSetLocaleDayOfWeek (input) {
+        if (!this.isValid()) {
+            return input != null ? this : NaN;
+        }
+        var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
+        return input == null ? weekday : this.add(input - weekday, 'd');
+    }
+
+    function getSetISODayOfWeek (input) {
+        if (!this.isValid()) {
+            return input != null ? this : NaN;
+        }
+        // behaves the same as moment#day except
+        // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
+        // as a setter, sunday should belong to the previous week.
+        return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
+    }
+
+    var defaultWeekdaysRegex = matchWord;
+    function weekdaysRegex (isStrict) {
+        if (this._weekdaysParseExact) {
+            if (!hasOwnProp(this, '_weekdaysRegex')) {
+                computeWeekdaysParse.call(this);
+            }
+            if (isStrict) {
+                return this._weekdaysStrictRegex;
+            } else {
+                return this._weekdaysRegex;
+            }
+        } else {
+            return this._weekdaysStrictRegex && isStrict ?
+                this._weekdaysStrictRegex : this._weekdaysRegex;
+        }
+    }
+
+    var defaultWeekdaysShortRegex = matchWord;
+    function weekdaysShortRegex (isStrict) {
+        if (this._weekdaysParseExact) {
+            if (!hasOwnProp(this, '_weekdaysRegex')) {
+                computeWeekdaysParse.call(this);
+            }
+            if (isStrict) {
+                return this._weekdaysShortStrictRegex;
+            } else {
+                return this._weekdaysShortRegex;
+            }
+        } else {
+            return this._weekdaysShortStrictRegex && isStrict ?
+                this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
+        }
+    }
+
+    var defaultWeekdaysMinRegex = matchWord;
+    function weekdaysMinRegex (isStrict) {
+        if (this._weekdaysParseExact) {
+            if (!hasOwnProp(this, '_weekdaysRegex')) {
+                computeWeekdaysParse.call(this);
+            }
+            if (isStrict) {
+                return this._weekdaysMinStrictRegex;
+            } else {
+                return this._weekdaysMinRegex;
+            }
+        } else {
+            return this._weekdaysMinStrictRegex && isStrict ?
+                this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
+        }
+    }
+
+
+    function computeWeekdaysParse () {
+        function cmpLenRev(a, b) {
+            return b.length - a.length;
+        }
+
+        var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
+            i, mom, minp, shortp, longp;
+        for (i = 0; i < 7; i++) {
+            // make the regex if we don't have it already
+            mom = create_utc__createUTC([2000, 1]).day(i);
+            minp = this.weekdaysMin(mom, '');
+            shortp = this.weekdaysShort(mom, '');
+            longp = this.weekdays(mom, '');
+            minPieces.push(minp);
+            shortPieces.push(shortp);
+            longPieces.push(longp);
+            mixedPieces.push(minp);
+            mixedPieces.push(shortp);
+            mixedPieces.push(longp);
+        }
+        // Sorting makes sure if one weekday (or abbr) is a prefix of another it
+        // will match the longer piece.
+        minPieces.sort(cmpLenRev);
+        shortPieces.sort(cmpLenRev);
+        longPieces.sort(cmpLenRev);
+        mixedPieces.sort(cmpLenRev);
+        for (i = 0; i < 7; i++) {
+            shortPieces[i] = regexEscape(shortPieces[i]);
+            longPieces[i] = regexEscape(longPieces[i]);
+            mixedPieces[i] = regexEscape(mixedPieces[i]);
+        }
+
+        this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
+        this._weekdaysShortRegex = this._weekdaysRegex;
+        this._weekdaysMinRegex = this._weekdaysRegex;
+
+        this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
+        this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
+        this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
+    }
+
+    // FORMATTING
+
+    addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
+
+    // ALIASES
+
+    addUnitAlias('dayOfYear', 'DDD');
+
+    // PARSING
+
+    addRegexToken('DDD',  match1to3);
+    addRegexToken('DDDD', match3);
+    addParseToken(['DDD', 'DDDD'], function (input, array, config) {
+        config._dayOfYear = toInt(input);
+    });
+
+    // HELPERS
+
+    // MOMENTS
+
+    function getSetDayOfYear (input) {
+        var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
+        return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
+    }
+
+    // FORMATTING
+
+    function hFormat() {
+        return this.hours() % 12 || 12;
+    }
+
+    function kFormat() {
+        return this.hours() || 24;
+    }
+
+    addFormatToken('H', ['HH', 2], 0, 'hour');
+    addFormatToken('h', ['hh', 2], 0, hFormat);
+    addFormatToken('k', ['kk', 2], 0, kFormat);
+
+    addFormatToken('hmm', 0, 0, function () {
+        return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
+    });
+
+    addFormatToken('hmmss', 0, 0, function () {
+        return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
+            zeroFill(this.seconds(), 2);
+    });
+
+    addFormatToken('Hmm', 0, 0, function () {
+        return '' + this.hours() + zeroFill(this.minutes(), 2);
+    });
+
+    addFormatToken('Hmmss', 0, 0, function () {
+        return '' + this.hours() + zeroFill(this.minutes(), 2) +
+            zeroFill(this.seconds(), 2);
+    });
+
+    function meridiem (token, lowercase) {
+        addFormatToken(token, 0, 0, function () {
+            return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
+        });
+    }
+
+    meridiem('a', true);
+    meridiem('A', false);
+
+    // ALIASES
+
+    addUnitAlias('hour', 'h');
+
+    // PARSING
+
+    function matchMeridiem (isStrict, locale) {
+        return locale._meridiemParse;
+    }
+
+    addRegexToken('a',  matchMeridiem);
+    addRegexToken('A',  matchMeridiem);
+    addRegexToken('H',  match1to2);
+    addRegexToken('h',  match1to2);
+    addRegexToken('HH', match1to2, match2);
+    addRegexToken('hh', match1to2, match2);
+
+    addRegexToken('hmm', match3to4);
+    addRegexToken('hmmss', match5to6);
+    addRegexToken('Hmm', match3to4);
+    addRegexToken('Hmmss', match5to6);
+
+    addParseToken(['H', 'HH'], HOUR);
+    addParseToken(['a', 'A'], function (input, array, config) {
+        config._isPm = config._locale.isPM(input);
+        config._meridiem = input;
+    });
+    addParseToken(['h', 'hh'], function (input, array, config) {
+        array[HOUR] = toInt(input);
+        getParsingFlags(config).bigHour = true;
+    });
+    addParseToken('hmm', function (input, array, config) {
+        var pos = input.length - 2;
+        array[HOUR] = toInt(input.substr(0, pos));
+        array[MINUTE] = toInt(input.substr(pos));
+        getParsingFlags(config).bigHour = true;
+    });
+    addParseToken('hmmss', function (input, array, config) {
+        var pos1 = input.length - 4;
+        var pos2 = input.length - 2;
+        array[HOUR] = toInt(input.substr(0, pos1));
+        array[MINUTE] = toInt(input.substr(pos1, 2));
+        array[SECOND] = toInt(input.substr(pos2));
+        getParsingFlags(config).bigHour = true;
+    });
+    addParseToken('Hmm', function (input, array, config) {
+        var pos = input.length - 2;
+        array[HOUR] = toInt(input.substr(0, pos));
+        array[MINUTE] = toInt(input.substr(pos));
+    });
+    addParseToken('Hmmss', function (input, array, config) {
+        var pos1 = input.length - 4;
+        var pos2 = input.length - 2;
+        array[HOUR] = toInt(input.substr(0, pos1));
+        array[MINUTE] = toInt(input.substr(pos1, 2));
+        array[SECOND] = toInt(input.substr(pos2));
+    });
+
+    // LOCALES
+
+    function localeIsPM (input) {
+        // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
+        // Using charAt should be more compatible.
+        return ((input + '').toLowerCase().charAt(0) === 'p');
+    }
+
+    var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
+    function localeMeridiem (hours, minutes, isLower) {
+        if (hours > 11) {
+            return isLower ? 'pm' : 'PM';
+        } else {
+            return isLower ? 'am' : 'AM';
+        }
+    }
+
+
+    // MOMENTS
+
+    // Setting the hour should keep the time, because the user explicitly
+    // specified which hour he wants. So trying to maintain the same hour (in
+    // a new timezone) makes sense. Adding/subtracting hours does not follow
+    // this rule.
+    var getSetHour = makeGetSet('Hours', true);
+
+    // FORMATTING
+
+    addFormatToken('m', ['mm', 2], 0, 'minute');
+
+    // ALIASES
+
+    addUnitAlias('minute', 'm');
+
+    // PARSING
+
+    addRegexToken('m',  match1to2);
+    addRegexToken('mm', match1to2, match2);
+    addParseToken(['m', 'mm'], MINUTE);
+
+    // MOMENTS
+
+    var getSetMinute = makeGetSet('Minutes', false);
+
+    // FORMATTING
+
+    addFormatToken('s', ['ss', 2], 0, 'second');
+
+    // ALIASES
+
+    addUnitAlias('second', 's');
+
+    // PARSING
+
+    addRegexToken('s',  match1to2);
+    addRegexToken('ss', match1to2, match2);
+    addParseToken(['s', 'ss'], SECOND);
+
+    // MOMENTS
+
+    var getSetSecond = makeGetSet('Seconds', false);
+
+    // FORMATTING
+
+    addFormatToken('S', 0, 0, function () {
+        return ~~(this.millisecond() / 100);
+    });
+
+    addFormatToken(0, ['SS', 2], 0, function () {
+        return ~~(this.millisecond() / 10);
+    });
+
+    addFormatToken(0, ['SSS', 3], 0, 'millisecond');
+    addFormatToken(0, ['SSSS', 4], 0, function () {
+        return this.millisecond() * 10;
+    });
+    addFormatToken(0, ['SSSSS', 5], 0, function () {
+        return this.millisecond() * 100;
+    });
+    addFormatToken(0, ['SSSSSS', 6], 0, function () {
+        return this.millisecond() * 1000;
+    });
+    addFormatToken(0, ['SSSSSSS', 7], 0, function () {
+        return this.millisecond() * 10000;
+    });
+    addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
+        return this.millisecond() * 100000;
+    });
+    addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
+        return this.millisecond() * 1000000;
+    });
+
+
+    // ALIASES
+
+    addUnitAlias('millisecond', 'ms');
+
+    // PARSING
+
+    addRegexToken('S',    match1to3, match1);
+    addRegexToken('SS',   match1to3, match2);
+    addRegexToken('SSS',  match1to3, match3);
+
+    var token;
+    for (token = 'SSSS'; token.length <= 9; token += 'S') {
+        addRegexToken(token, matchUnsigned);
+    }
+
+    function parseMs(input, array) {
+        array[MILLISECOND] = toInt(('0.' + input) * 1000);
+    }
+
+    for (token = 'S'; token.length <= 9; token += 'S') {
+        addParseToken(token, parseMs);
+    }
+    // MOMENTS
+
+    var getSetMillisecond = makeGetSet('Milliseconds', false);
+
+    // FORMATTING
+
+    addFormatToken('z',  0, 0, 'zoneAbbr');
+    addFormatToken('zz', 0, 0, 'zoneName');
+
+    // MOMENTS
+
+    function getZoneAbbr () {
+        return this._isUTC ? 'UTC' : '';
+    }
+
+    function getZoneName () {
+        return this._isUTC ? 'Coordinated Universal Time' : '';
+    }
+
+    var momentPrototype__proto = Moment.prototype;
+
+    momentPrototype__proto.add               = add_subtract__add;
+    momentPrototype__proto.calendar          = moment_calendar__calendar;
+    momentPrototype__proto.clone             = clone;
+    momentPrototype__proto.diff              = diff;
+    momentPrototype__proto.endOf             = endOf;
+    momentPrototype__proto.format            = format;
+    momentPrototype__proto.from              = from;
+    momentPrototype__proto.fromNow           = fromNow;
+    momentPrototype__proto.to                = to;
+    momentPrototype__proto.toNow             = toNow;
+    momentPrototype__proto.get               = getSet;
+    momentPrototype__proto.invalidAt         = invalidAt;
+    momentPrototype__proto.isAfter           = isAfter;
+    momentPrototype__proto.isBefore          = isBefore;
+    momentPrototype__proto.isBetween         = isBetween;
+    momentPrototype__proto.isSame            = isSame;
+    momentPrototype__proto.isSameOrAfter     = isSameOrAfter;
+    momentPrototype__proto.isSameOrBefore    = isSameOrBefore;
+    momentPrototype__proto.isValid           = moment_valid__isValid;
+    momentPrototype__proto.lang              = lang;
+    momentPrototype__proto.locale            = locale;
+    momentPrototype__proto.localeData        = localeData;
+    momentPrototype__proto.max               = prototypeMax;
+    momentPrototype__proto.min               = prototypeMin;
+    momentPrototype__proto.parsingFlags      = parsingFlags;
+    momentPrototype__proto.set               = getSet;
+    momentPrototype__proto.startOf           = startOf;
+    momentPrototype__proto.subtract          = add_subtract__subtract;
+    momentPrototype__proto.toArray           = toArray;
+    momentPrototype__proto.toObject          = toObject;
+    momentPrototype__proto.toDate            = toDate;
+    momentPrototype__proto.toISOString       = moment_format__toISOString;
+    momentPrototype__proto.toJSON            = toJSON;
+    momentPrototype__proto.toString          = toString;
+    momentPrototype__proto.unix              = unix;
+    momentPrototype__proto.valueOf           = to_type__valueOf;
+    momentPrototype__proto.creationData      = creationData;
+
+    // Year
+    momentPrototype__proto.year       = getSetYear;
+    momentPrototype__proto.isLeapYear = getIsLeapYear;
+
+    // Week Year
+    momentPrototype__proto.weekYear    = getSetWeekYear;
+    momentPrototype__proto.isoWeekYear = getSetISOWeekYear;
+
+    // Quarter
+    momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter;
+
+    // Month
+    momentPrototype__proto.month       = getSetMonth;
+    momentPrototype__proto.daysInMonth = getDaysInMonth;
+
+    // Week
+    momentPrototype__proto.week           = momentPrototype__proto.weeks        = getSetWeek;
+    momentPrototype__proto.isoWeek        = momentPrototype__proto.isoWeeks     = getSetISOWeek;
+    momentPrototype__proto.weeksInYear    = getWeeksInYear;
+    momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear;
+
+    // Day
+    momentPrototype__proto.date       = getSetDayOfMonth;
+    momentPrototype__proto.day        = momentPrototype__proto.days             = getSetDayOfWeek;
+    momentPrototype__proto.weekday    = getSetLocaleDayOfWeek;
+    momentPrototype__proto.isoWeekday = getSetISODayOfWeek;
+    momentPrototype__proto.dayOfYear  = getSetDayOfYear;
+
+    // Hour
+    momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour;
+
+    // Minute
+    momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute;
+
+    // Second
+    momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond;
+
+    // Millisecond
+    momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond;
+
+    // Offset
+    momentPrototype__proto.utcOffset            = getSetOffset;
+    momentPrototype__proto.utc                  = setOffsetToUTC;
+    momentPrototype__proto.local                = setOffsetToLocal;
+    momentPrototype__proto.parseZone            = setOffsetToParsedOffset;
+    momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset;
+    momentPrototype__proto.isDST                = isDaylightSavingTime;
+    momentPrototype__proto.isDSTShifted         = isDaylightSavingTimeShifted;
+    momentPrototype__proto.isLocal              = isLocal;
+    momentPrototype__proto.isUtcOffset          = isUtcOffset;
+    momentPrototype__proto.isUtc                = isUtc;
+    momentPrototype__proto.isUTC                = isUtc;
+
+    // Timezone
+    momentPrototype__proto.zoneAbbr = getZoneAbbr;
+    momentPrototype__proto.zoneName = getZoneName;
+
+    // Deprecations
+    momentPrototype__proto.dates  = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
+    momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
+    momentPrototype__proto.years  = deprecate('years accessor is deprecated. Use year instead', getSetYear);
+    momentPrototype__proto.zone   = deprecate('moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779', getSetZone);
+
+    var momentPrototype = momentPrototype__proto;
+
+    function moment__createUnix (input) {
+        return local__createLocal(input * 1000);
+    }
+
+    function moment__createInZone () {
+        return local__createLocal.apply(null, arguments).parseZone();
+    }
+
+    var defaultCalendar = {
+        sameDay : '[Today at] LT',
+        nextDay : '[Tomorrow at] LT',
+        nextWeek : 'dddd [at] LT',
+        lastDay : '[Yesterday at] LT',
+        lastWeek : '[Last] dddd [at] LT',
+        sameElse : 'L'
+    };
+
+    function locale_calendar__calendar (key, mom, now) {
+        var output = this._calendar[key];
+        return isFunction(output) ? output.call(mom, now) : output;
+    }
+
+    var defaultLongDateFormat = {
+        LTS  : 'h:mm:ss A',
+        LT   : 'h:mm A',
+        L    : 'MM/DD/YYYY',
+        LL   : 'MMMM D, YYYY',
+        LLL  : 'MMMM D, YYYY h:mm A',
+        LLLL : 'dddd, MMMM D, YYYY h:mm A'
+    };
+
+    function longDateFormat (key) {
+        var format = this._longDateFormat[key],
+            formatUpper = this._longDateFormat[key.toUpperCase()];
+
+        if (format || !formatUpper) {
+            return format;
+        }
+
+        this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
+            return val.slice(1);
+        });
+
+        return this._longDateFormat[key];
+    }
+
+    var defaultInvalidDate = 'Invalid date';
+
+    function invalidDate () {
+        return this._invalidDate;
+    }
+
+    var defaultOrdinal = '%d';
+    var defaultOrdinalParse = /\d{1,2}/;
+
+    function ordinal (number) {
+        return this._ordinal.replace('%d', number);
+    }
+
+    function preParsePostFormat (string) {
+        return string;
+    }
+
+    var defaultRelativeTime = {
+        future : 'in %s',
+        past   : '%s ago',
+        s  : 'a few seconds',
+        m  : 'a minute',
+        mm : '%d minutes',
+        h  : 'an hour',
+        hh : '%d hours',
+        d  : 'a day',
+        dd : '%d days',
+        M  : 'a month',
+        MM : '%d months',
+        y  : 'a year',
+        yy : '%d years'
+    };
+
+    function relative__relativeTime (number, withoutSuffix, string, isFuture) {
+        var output = this._relativeTime[string];
+        return (isFunction(output)) ?
+            output(number, withoutSuffix, string, isFuture) :
+            output.replace(/%d/i, number);
+    }
+
+    function pastFuture (diff, output) {
+        var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
+        return isFunction(format) ? format(output) : format.replace(/%s/i, output);
+    }
+
+    var prototype__proto = Locale.prototype;
+
+    prototype__proto._calendar       = defaultCalendar;
+    prototype__proto.calendar        = locale_calendar__calendar;
+    prototype__proto._longDateFormat = defaultLongDateFormat;
+    prototype__proto.longDateFormat  = longDateFormat;
+    prototype__proto._invalidDate    = defaultInvalidDate;
+    prototype__proto.invalidDate     = invalidDate;
+    prototype__proto._ordinal        = defaultOrdinal;
+    prototype__proto.ordinal         = ordinal;
+    prototype__proto._ordinalParse   = defaultOrdinalParse;
+    prototype__proto.preparse        = preParsePostFormat;
+    prototype__proto.postformat      = preParsePostFormat;
+    prototype__proto._relativeTime   = defaultRelativeTime;
+    prototype__proto.relativeTime    = relative__relativeTime;
+    prototype__proto.pastFuture      = pastFuture;
+    prototype__proto.set             = locale_set__set;
+
+    // Month
+    prototype__proto.months            =        localeMonths;
+    prototype__proto._months           = defaultLocaleMonths;
+    prototype__proto.monthsShort       =        localeMonthsShort;
+    prototype__proto._monthsShort      = defaultLocaleMonthsShort;
+    prototype__proto.monthsParse       =        localeMonthsParse;
+    prototype__proto._monthsRegex      = defaultMonthsRegex;
+    prototype__proto.monthsRegex       = monthsRegex;
+    prototype__proto._monthsShortRegex = defaultMonthsShortRegex;
+    prototype__proto.monthsShortRegex  = monthsShortRegex;
+
+    // Week
+    prototype__proto.week = localeWeek;
+    prototype__proto._week = defaultLocaleWeek;
+    prototype__proto.firstDayOfYear = localeFirstDayOfYear;
+    prototype__proto.firstDayOfWeek = localeFirstDayOfWeek;
+
+    // Day of Week
+    prototype__proto.weekdays       =        localeWeekdays;
+    prototype__proto._weekdays      = defaultLocaleWeekdays;
+    prototype__proto.weekdaysMin    =        localeWeekdaysMin;
+    prototype__proto._weekdaysMin   = defaultLocaleWeekdaysMin;
+    prototype__proto.weekdaysShort  =        localeWeekdaysShort;
+    prototype__proto._weekdaysShort = defaultLocaleWeekdaysShort;
+    prototype__proto.weekdaysParse  =        localeWeekdaysParse;
+
+    prototype__proto._weekdaysRegex      = defaultWeekdaysRegex;
+    prototype__proto.weekdaysRegex       =        weekdaysRegex;
+    prototype__proto._weekdaysShortRegex = defaultWeekdaysShortRegex;
+    prototype__proto.weekdaysShortRegex  =        weekdaysShortRegex;
+    prototype__proto._weekdaysMinRegex   = defaultWeekdaysMinRegex;
+    prototype__proto.weekdaysMinRegex    =        weekdaysMinRegex;
+
+    // Hours
+    prototype__proto.isPM = localeIsPM;
+    prototype__proto._meridiemParse = defaultLocaleMeridiemParse;
+    prototype__proto.meridiem = localeMeridiem;
+
+    function lists__get (format, index, field, setter) {
+        var locale = locale_locales__getLocale();
+        var utc = create_utc__createUTC().set(setter, index);
+        return locale[field](utc, format);
+    }
+
+    function listMonthsImpl (format, index, field) {
+        if (typeof format === 'number') {
+            index = format;
+            format = undefined;
+        }
+
+        format = format || '';
+
+        if (index != null) {
+            return lists__get(format, index, field, 'month');
+        }
+
+        var i;
+        var out = [];
+        for (i = 0; i < 12; i++) {
+            out[i] = lists__get(format, i, field, 'month');
+        }
+        return out;
+    }
+
+    // ()
+    // (5)
+    // (fmt, 5)
+    // (fmt)
+    // (true)
+    // (true, 5)
+    // (true, fmt, 5)
+    // (true, fmt)
+    function listWeekdaysImpl (localeSorted, format, index, field) {
+        if (typeof localeSorted === 'boolean') {
+            if (typeof format === 'number') {
+                index = format;
+                format = undefined;
+            }
+
+            format = format || '';
+        } else {
+            format = localeSorted;
+            index = format;
+            localeSorted = false;
+
+            if (typeof format === 'number') {
+                index = format;
+                format = undefined;
+            }
+
+            format = format || '';
+        }
+
+        var locale = locale_locales__getLocale(),
+            shift = localeSorted ? locale._week.dow : 0;
+
+        if (index != null) {
+            return lists__get(format, (index + shift) % 7, field, 'day');
+        }
+
+        var i;
+        var out = [];
+        for (i = 0; i < 7; i++) {
+            out[i] = lists__get(format, (i + shift) % 7, field, 'day');
+        }
+        return out;
+    }
+
+    function lists__listMonths (format, index) {
+        return listMonthsImpl(format, index, 'months');
+    }
+
+    function lists__listMonthsShort (format, index) {
+        return listMonthsImpl(format, index, 'monthsShort');
+    }
+
+    function lists__listWeekdays (localeSorted, format, index) {
+        return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
+    }
+
+    function lists__listWeekdaysShort (localeSorted, format, index) {
+        return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
+    }
+
+    function lists__listWeekdaysMin (localeSorted, format, index) {
+        return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
+    }
+
+    locale_locales__getSetGlobalLocale('en', {
+        ordinalParse: /\d{1,2}(th|st|nd|rd)/,
+        ordinal : function (number) {
+            var b = number % 10,
+                output = (toInt(number % 100 / 10) === 1) ? 'th' :
+                (b === 1) ? 'st' :
+                (b === 2) ? 'nd' :
+                (b === 3) ? 'rd' : 'th';
+            return number + output;
+        }
+    });
+
+    // Side effect imports
+    utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale);
+    utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale);
+
+    var mathAbs = Math.abs;
+
+    function duration_abs__abs () {
+        var data           = this._data;
+
+        this._milliseconds = mathAbs(this._milliseconds);
+        this._days         = mathAbs(this._days);
+        this._months       = mathAbs(this._months);
+
+        data.milliseconds  = mathAbs(data.milliseconds);
+        data.seconds       = mathAbs(data.seconds);
+        data.minutes       = mathAbs(data.minutes);
+        data.hours         = mathAbs(data.hours);
+        data.months        = mathAbs(data.months);
+        data.years         = mathAbs(data.years);
+
+        return this;
+    }
+
+    function duration_add_subtract__addSubtract (duration, input, value, direction) {
+        var other = create__createDuration(input, value);
+
+        duration._milliseconds += direction * other._milliseconds;
+        duration._days         += direction * other._days;
+        duration._months       += direction * other._months;
+
+        return duration._bubble();
+    }
+
+    // supports only 2.0-style add(1, 's') or add(duration)
+    function duration_add_subtract__add (input, value) {
+        return duration_add_subtract__addSubtract(this, input, value, 1);
+    }
+
+    // supports only 2.0-style subtract(1, 's') or subtract(duration)
+    function duration_add_subtract__subtract (input, value) {
+        return duration_add_subtract__addSubtract(this, input, value, -1);
+    }
+
+    function absCeil (number) {
+        if (number < 0) {
+            return Math.floor(number);
+        } else {
+            return Math.ceil(number);
+        }
+    }
+
+    function bubble () {
+        var milliseconds = this._milliseconds;
+        var days         = this._days;
+        var months       = this._months;
+        var data         = this._data;
+        var seconds, minutes, hours, years, monthsFromDays;
+
+        // if we have a mix of positive and negative values, bubble down first
+        // check: https://github.com/moment/moment/issues/2166
+        if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
+                (milliseconds <= 0 && days <= 0 && months <= 0))) {
+            milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
+            days = 0;
+            months = 0;
+        }
+
+        // The following code bubbles up values, see the tests for
+        // examples of what that means.
+        data.milliseconds = milliseconds % 1000;
+
+        seconds           = absFloor(milliseconds / 1000);
+        data.seconds      = seconds % 60;
+
+        minutes           = absFloor(seconds / 60);
+        data.minutes      = minutes % 60;
+
+        hours             = absFloor(minutes / 60);
+        data.hours        = hours % 24;
+
+        days += absFloor(hours / 24);
+
+        // convert days to months
+        monthsFromDays = absFloor(daysToMonths(days));
+        months += monthsFromDays;
+        days -= absCeil(monthsToDays(monthsFromDays));
+
+        // 12 months -> 1 year
+        years = absFloor(months / 12);
+        months %= 12;
+
+        data.days   = days;
+        data.months = months;
+        data.years  = years;
+
+        return this;
+    }
+
+    function daysToMonths (days) {
+        // 400 years have 146097 days (taking into account leap year rules)
+        // 400 years have 12 months === 4800
+        return days * 4800 / 146097;
+    }
+
+    function monthsToDays (months) {
+        // the reverse of daysToMonths
+        return months * 146097 / 4800;
+    }
+
+    function as (units) {
+        var days;
+        var months;
+        var milliseconds = this._milliseconds;
+
+        units = normalizeUnits(units);
+
+        if (units === 'month' || units === 'year') {
+            days   = this._days   + milliseconds / 864e5;
+            months = this._months + daysToMonths(days);
+            return units === 'month' ? months : months / 12;
+        } else {
+            // handle milliseconds separately because of floating point math errors (issue #1867)
+            days = this._days + Math.round(monthsToDays(this._months));
+            switch (units) {
+                case 'week'   : return days / 7     + milliseconds / 6048e5;
+                case 'day'    : return days         + milliseconds / 864e5;
+                case 'hour'   : return days * 24    + milliseconds / 36e5;
+                case 'minute' : return days * 1440  + milliseconds / 6e4;
+                case 'second' : return days * 86400 + milliseconds / 1000;
+                // Math.floor prevents floating point math errors here
+                case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
+                default: throw new Error('Unknown unit ' + units);
+            }
+        }
+    }
+
+    // TODO: Use this.as('ms')?
+    function duration_as__valueOf () {
+        return (
+            this._milliseconds +
+            this._days * 864e5 +
+            (this._months % 12) * 2592e6 +
+            toInt(this._months / 12) * 31536e6
+        );
+    }
+
+    function makeAs (alias) {
+        return function () {
+            return this.as(alias);
+        };
+    }
+
+    var asMilliseconds = makeAs('ms');
+    var asSeconds      = makeAs('s');
+    var asMinutes      = makeAs('m');
+    var asHours        = makeAs('h');
+    var asDays         = makeAs('d');
+    var asWeeks        = makeAs('w');
+    var asMonths       = makeAs('M');
+    var asYears        = makeAs('y');
+
+    function duration_get__get (units) {
+        units = normalizeUnits(units);
+        return this[units + 's']();
+    }
+
+    function makeGetter(name) {
+        return function () {
+            return this._data[name];
+        };
+    }
+
+    var milliseconds = makeGetter('milliseconds');
+    var seconds      = makeGetter('seconds');
+    var minutes      = makeGetter('minutes');
+    var hours        = makeGetter('hours');
+    var days         = makeGetter('days');
+    var months       = makeGetter('months');
+    var years        = makeGetter('years');
+
+    function weeks () {
+        return absFloor(this.days() / 7);
+    }
+
+    var round = Math.round;
+    var thresholds = {
+        s: 45,  // seconds to minute
+        m: 45,  // minutes to hour
+        h: 22,  // hours to day
+        d: 26,  // days to month
+        M: 11   // months to year
+    };
+
+    // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
+    function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
+        return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
+    }
+
+    function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) {
+        var duration = create__createDuration(posNegDuration).abs();
+        var seconds  = round(duration.as('s'));
+        var minutes  = round(duration.as('m'));
+        var hours    = round(duration.as('h'));
+        var days     = round(duration.as('d'));
+        var months   = round(duration.as('M'));
+        var years    = round(duration.as('y'));
+
+        var a = seconds < thresholds.s && ['s', seconds]  ||
+                minutes <= 1           && ['m']           ||
+                minutes < thresholds.m && ['mm', minutes] ||
+                hours   <= 1           && ['h']           ||
+                hours   < thresholds.h && ['hh', hours]   ||
+                days    <= 1           && ['d']           ||
+                days    < thresholds.d && ['dd', days]    ||
+                months  <= 1           && ['M']           ||
+                months  < thresholds.M && ['MM', months]  ||
+                years   <= 1           && ['y']           || ['yy', years];
+
+        a[2] = withoutSuffix;
+        a[3] = +posNegDuration > 0;
+        a[4] = locale;
+        return substituteTimeAgo.apply(null, a);
+    }
+
+    // This function allows you to set a threshold for relative time strings
+    function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) {
+        if (thresholds[threshold] === undefined) {
+            return false;
+        }
+        if (limit === undefined) {
+            return thresholds[threshold];
+        }
+        thresholds[threshold] = limit;
+        return true;
+    }
+
+    function humanize (withSuffix) {
+        var locale = this.localeData();
+        var output = duration_humanize__relativeTime(this, !withSuffix, locale);
+
+        if (withSuffix) {
+            output = locale.pastFuture(+this, output);
+        }
+
+        return locale.postformat(output);
+    }
+
+    var iso_string__abs = Math.abs;
+
+    function iso_string__toISOString() {
+        // for ISO strings we do not use the normal bubbling rules:
+        //  * milliseconds bubble up until they become hours
+        //  * days do not bubble at all
+        //  * months bubble up until they become years
+        // This is because there is no context-free conversion between hours and days
+        // (think of clock changes)
+        // and also not between days and months (28-31 days per month)
+        var seconds = iso_string__abs(this._milliseconds) / 1000;
+        var days         = iso_string__abs(this._days);
+        var months       = iso_string__abs(this._months);
+        var minutes, hours, years;
+
+        // 3600 seconds -> 60 minutes -> 1 hour
+        minutes           = absFloor(seconds / 60);
+        hours             = absFloor(minutes / 60);
+        seconds %= 60;
+        minutes %= 60;
+
+        // 12 months -> 1 year
+        years  = absFloor(months / 12);
+        months %= 12;
+
+
+        // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
+        var Y = years;
+        var M = months;
+        var D = days;
+        var h = hours;
+        var m = minutes;
+        var s = seconds;
+        var total = this.asSeconds();
+
+        if (!total) {
+            // this is the same as C#'s (Noda) and python (isodate)...
+            // but not other JS (goog.date)
+            return 'P0D';
+        }
+
+        return (total < 0 ? '-' : '') +
+            'P' +
+            (Y ? Y + 'Y' : '') +
+            (M ? M + 'M' : '') +
+            (D ? D + 'D' : '') +
+            ((h || m || s) ? 'T' : '') +
+            (h ? h + 'H' : '') +
+            (m ? m + 'M' : '') +
+            (s ? s + 'S' : '');
+    }
+
+    var duration_prototype__proto = Duration.prototype;
+
+    duration_prototype__proto.abs            = duration_abs__abs;
+    duration_prototype__proto.add            = duration_add_subtract__add;
+    duration_prototype__proto.subtract       = duration_add_subtract__subtract;
+    duration_prototype__proto.as             = as;
+    duration_prototype__proto.asMilliseconds = asMilliseconds;
+    duration_prototype__proto.asSeconds      = asSeconds;
+    duration_prototype__proto.asMinutes      = asMinutes;
+    duration_prototype__proto.asHours        = asHours;
+    duration_prototype__proto.asDays         = asDays;
+    duration_prototype__proto.asWeeks        = asWeeks;
+    duration_prototype__proto.asMonths       = asMonths;
+    duration_prototype__proto.asYears        = asYears;
+    duration_prototype__proto.valueOf        = duration_as__valueOf;
+    duration_prototype__proto._bubble        = bubble;
+    duration_prototype__proto.get            = duration_get__get;
+    duration_prototype__proto.milliseconds   = milliseconds;
+    duration_prototype__proto.seconds        = seconds;
+    duration_prototype__proto.minutes        = minutes;
+    duration_prototype__proto.hours          = hours;
+    duration_prototype__proto.days           = days;
+    duration_prototype__proto.weeks          = weeks;
+    duration_prototype__proto.months         = months;
+    duration_prototype__proto.years          = years;
+    duration_prototype__proto.humanize       = humanize;
+    duration_prototype__proto.toISOString    = iso_string__toISOString;
+    duration_prototype__proto.toString       = iso_string__toISOString;
+    duration_prototype__proto.toJSON         = iso_string__toISOString;
+    duration_prototype__proto.locale         = locale;
+    duration_prototype__proto.localeData     = localeData;
+
+    // Deprecations
+    duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString);
+    duration_prototype__proto.lang = lang;
+
+    // Side effect imports
+
+    // FORMATTING
+
+    addFormatToken('X', 0, 0, 'unix');
+    addFormatToken('x', 0, 0, 'valueOf');
+
+    // PARSING
+
+    addRegexToken('x', matchSigned);
+    addRegexToken('X', matchTimestamp);
+    addParseToken('X', function (input, array, config) {
+        config._d = new Date(parseFloat(input, 10) * 1000);
+    });
+    addParseToken('x', function (input, array, config) {
+        config._d = new Date(toInt(input));
+    });
+
+    // Side effect imports
+
+
+    utils_hooks__hooks.version = '2.13.0';
+
+    setHookCallback(local__createLocal);
+
+    utils_hooks__hooks.fn                    = momentPrototype;
+    utils_hooks__hooks.min                   = min;
+    utils_hooks__hooks.max                   = max;
+    utils_hooks__hooks.now                   = now;
+    utils_hooks__hooks.utc                   = create_utc__createUTC;
+    utils_hooks__hooks.unix                  = moment__createUnix;
+    utils_hooks__hooks.months                = lists__listMonths;
+    utils_hooks__hooks.isDate                = isDate;
+    utils_hooks__hooks.locale                = locale_locales__getSetGlobalLocale;
+    utils_hooks__hooks.invalid               = valid__createInvalid;
+    utils_hooks__hooks.duration              = create__createDuration;
+    utils_hooks__hooks.isMoment              = isMoment;
+    utils_hooks__hooks.weekdays              = lists__listWeekdays;
+    utils_hooks__hooks.parseZone             = moment__createInZone;
+    utils_hooks__hooks.localeData            = locale_locales__getLocale;
+    utils_hooks__hooks.isDuration            = isDuration;
+    utils_hooks__hooks.monthsShort           = lists__listMonthsShort;
+    utils_hooks__hooks.weekdaysMin           = lists__listWeekdaysMin;
+    utils_hooks__hooks.defineLocale          = defineLocale;
+    utils_hooks__hooks.updateLocale          = updateLocale;
+    utils_hooks__hooks.locales               = locale_locales__listLocales;
+    utils_hooks__hooks.weekdaysShort         = lists__listWeekdaysShort;
+    utils_hooks__hooks.normalizeUnits        = normalizeUnits;
+    utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold;
+    utils_hooks__hooks.prototype             = momentPrototype;
+
+    var _moment = utils_hooks__hooks;
+
+    return _moment;
+
+}));
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/graph.css b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/css/graph.css
old mode 100644
new mode 100755
similarity index 98%
rename from components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/graph.css
rename to components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/css/graph.css
index 59ff8fa775..9f357f2fa5
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/cdmf.page.device.analytics/public/css/graph.css
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/css/graph.css
@@ -232,6 +232,13 @@
     position: relative;
     left: 40px;
     height: 30px;
+    width: 97%;
+    top: 20px;
+    text-align: right;
+}
+
+.chartWrapper {
+    padding-top: 50px;
 }
 
 /*detail*/
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/js/d3.min.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/js/d3.min.js
new file mode 100755
index 0000000000..43a29e2315
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/js/d3.min.js
@@ -0,0 +1,9470 @@
+!function() {
+    var d3 = {
+        version: "3.5.2"
+    };
+    if (!Date.now) Date.now = function() {
+        return +new Date();
+    };
+    var d3_arraySlice = [].slice, d3_array = function(list) {
+        return d3_arraySlice.call(list);
+    };
+    var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window;
+    try {
+        d3_array(d3_documentElement.childNodes)[0].nodeType;
+    } catch (e) {
+        d3_array = function(list) {
+            var i = list.length, array = new Array(i);
+            while (i--) array[i] = list[i];
+            return array;
+        };
+    }
+    try {
+        d3_document.createElement("div").style.setProperty("opacity", 0, "");
+    } catch (error) {
+        var d3_element_prototype = d3_window.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty;
+        d3_element_prototype.setAttribute = function(name, value) {
+            d3_element_setAttribute.call(this, name, value + "");
+        };
+        d3_element_prototype.setAttributeNS = function(space, local, value) {
+            d3_element_setAttributeNS.call(this, space, local, value + "");
+        };
+        d3_style_prototype.setProperty = function(name, value, priority) {
+            d3_style_setProperty.call(this, name, value + "", priority);
+        };
+    }
+    d3.ascending = d3_ascending;
+    function d3_ascending(a, b) {
+        return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
+    }
+    d3.descending = function(a, b) {
+        return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
+    };
+    d3.min = function(array, f) {
+        var i = -1, n = array.length, a, b;
+        if (arguments.length === 1) {
+            while (++i < n) if ((b = array[i]) != null && b >= b) {
+                a = b;
+                break;
+            }
+            while (++i < n) if ((b = array[i]) != null && a > b) a = b;
+        } else {
+            while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
+                a = b;
+                break;
+            }
+            while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
+        }
+        return a;
+    };
+    d3.max = function(array, f) {
+        var i = -1, n = array.length, a, b;
+        if (arguments.length === 1) {
+            while (++i < n) if ((b = array[i]) != null && b >= b) {
+                a = b;
+                break;
+            }
+            while (++i < n) if ((b = array[i]) != null && b > a) a = b;
+        } else {
+            while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
+                a = b;
+                break;
+            }
+            while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
+        }
+        return a;
+    };
+    d3.extent = function(array, f) {
+        var i = -1, n = array.length, a, b, c;
+        if (arguments.length === 1) {
+            while (++i < n) if ((b = array[i]) != null && b >= b) {
+                a = c = b;
+                break;
+            }
+            while (++i < n) if ((b = array[i]) != null) {
+                if (a > b) a = b;
+                if (c < b) c = b;
+            }
+        } else {
+            while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
+                a = c = b;
+                break;
+            }
+            while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
+                if (a > b) a = b;
+                if (c < b) c = b;
+            }
+        }
+        return [ a, c ];
+    };
+    function d3_number(x) {
+        return x === null ? NaN : +x;
+    }
+    function d3_numeric(x) {
+        return !isNaN(x);
+    }
+    d3.sum = function(array, f) {
+        var s = 0, n = array.length, a, i = -1;
+        if (arguments.length === 1) {
+            while (++i < n) if (d3_numeric(a = +array[i])) s += a;
+        } else {
+            while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
+        }
+        return s;
+    };
+    d3.mean = function(array, f) {
+        var s = 0, n = array.length, a, i = -1, j = n;
+        if (arguments.length === 1) {
+            while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
+        } else {
+            while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
+        }
+        if (j) return s / j;
+    };
+    d3.quantile = function(values, p) {
+        var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
+        return e ? v + e * (values[h] - v) : v;
+    };
+    d3.median = function(array, f) {
+        var numbers = [], n = array.length, a, i = -1;
+        if (arguments.length === 1) {
+            while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
+        } else {
+            while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
+        }
+        if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
+    };
+    d3.variance = function(array, f) {
+        var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0;
+        if (arguments.length === 1) {
+            while (++i < n) {
+                if (d3_numeric(a = d3_number(array[i]))) {
+                    d = a - m;
+                    m += d / ++j;
+                    s += d * (a - m);
+                }
+            }
+        } else {
+            while (++i < n) {
+                if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
+                    d = a - m;
+                    m += d / ++j;
+                    s += d * (a - m);
+                }
+            }
+        }
+        if (j > 1) return s / (j - 1);
+    };
+    d3.deviation = function() {
+        var v = d3.variance.apply(this, arguments);
+        return v ? Math.sqrt(v) : v;
+    };
+    function d3_bisector(compare) {
+        return {
+            left: function(a, x, lo, hi) {
+                if (arguments.length < 3) lo = 0;
+                if (arguments.length < 4) hi = a.length;
+                while (lo < hi) {
+                    var mid = lo + hi >>> 1;
+                    if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid;
+                }
+                return lo;
+            },
+            right: function(a, x, lo, hi) {
+                if (arguments.length < 3) lo = 0;
+                if (arguments.length < 4) hi = a.length;
+                while (lo < hi) {
+                    var mid = lo + hi >>> 1;
+                    if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1;
+                }
+                return lo;
+            }
+        };
+    }
+    var d3_bisect = d3_bisector(d3_ascending);
+    d3.bisectLeft = d3_bisect.left;
+    d3.bisect = d3.bisectRight = d3_bisect.right;
+    d3.bisector = function(f) {
+        return d3_bisector(f.length === 1 ? function(d, x) {
+            return d3_ascending(f(d), x);
+        } : f);
+    };
+    d3.shuffle = function(array, i0, i1) {
+        if ((m = arguments.length) < 3) {
+            i1 = array.length;
+            if (m < 2) i0 = 0;
+        }
+        var m = i1 - i0, t, i;
+        while (m) {
+            i = Math.random() * m-- | 0;
+            t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
+        }
+        return array;
+    };
+    d3.permute = function(array, indexes) {
+        var i = indexes.length, permutes = new Array(i);
+        while (i--) permutes[i] = array[indexes[i]];
+        return permutes;
+    };
+    d3.pairs = function(array) {
+        var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
+        while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
+        return pairs;
+    };
+    d3.zip = function() {
+        if (!(n = arguments.length)) return [];
+        for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) {
+            for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) {
+                zip[j] = arguments[j][i];
+            }
+        }
+        return zips;
+    };
+    function d3_zipLength(d) {
+        return d.length;
+    }
+    d3.transpose = function(matrix) {
+        return d3.zip.apply(d3, matrix);
+    };
+    d3.keys = function(map) {
+        var keys = [];
+        for (var key in map) keys.push(key);
+        return keys;
+    };
+    d3.values = function(map) {
+        var values = [];
+        for (var key in map) values.push(map[key]);
+        return values;
+    };
+    d3.entries = function(map) {
+        var entries = [];
+        for (var key in map) entries.push({
+            key: key,
+            value: map[key]
+        });
+        return entries;
+    };
+    d3.merge = function(arrays) {
+        var n = arrays.length, m, i = -1, j = 0, merged, array;
+        while (++i < n) j += arrays[i].length;
+        merged = new Array(j);
+        while (--n >= 0) {
+            array = arrays[n];
+            m = array.length;
+            while (--m >= 0) {
+                merged[--j] = array[m];
+            }
+        }
+        return merged;
+    };
+    var abs = Math.abs;
+    d3.range = function(start, stop, step) {
+        if (arguments.length < 3) {
+            step = 1;
+            if (arguments.length < 2) {
+                stop = start;
+                start = 0;
+            }
+        }
+        if ((stop - start) / step === Infinity) throw new Error("infinite range");
+        var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
+        start *= k, stop *= k, step *= k;
+        if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
+        return range;
+    };
+    function d3_range_integerScale(x) {
+        var k = 1;
+        while (x * k % 1) k *= 10;
+        return k;
+    }
+    function d3_class(ctor, properties) {
+        for (var key in properties) {
+            Object.defineProperty(ctor.prototype, key, {
+                value: properties[key],
+                enumerable: false
+            });
+        }
+    }
+    d3.map = function(object, f) {
+        var map = new d3_Map();
+        if (object instanceof d3_Map) {
+            object.forEach(function(key, value) {
+                map.set(key, value);
+            });
+        } else if (Array.isArray(object)) {
+            var i = -1, n = object.length, o;
+            if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o);
+        } else {
+            for (var key in object) map.set(key, object[key]);
+        }
+        return map;
+    };
+    function d3_Map() {
+        this._ = Object.create(null);
+    }
+    var d3_map_proto = "__proto__", d3_map_zero = "\x00";
+    d3_class(d3_Map, {
+        has: d3_map_has,
+        get: function(key) {
+            return this._[d3_map_escape(key)];
+        },
+        set: function(key, value) {
+            return this._[d3_map_escape(key)] = value;
+        },
+        remove: d3_map_remove,
+        keys: d3_map_keys,
+        values: function() {
+            var values = [];
+            for (var key in this._) values.push(this._[key]);
+            return values;
+        },
+        entries: function() {
+            var entries = [];
+            for (var key in this._) entries.push({
+                key: d3_map_unescape(key),
+                value: this._[key]
+            });
+            return entries;
+        },
+        size: d3_map_size,
+        empty: d3_map_empty,
+        forEach: function(f) {
+            for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
+        }
+    });
+    function d3_map_escape(key) {
+        return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
+    }
+    function d3_map_unescape(key) {
+        return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
+    }
+    function d3_map_has(key) {
+        return d3_map_escape(key) in this._;
+    }
+    function d3_map_remove(key) {
+        return (key = d3_map_escape(key)) in this._ && delete this._[key];
+    }
+    function d3_map_keys() {
+        var keys = [];
+        for (var key in this._) keys.push(d3_map_unescape(key));
+        return keys;
+    }
+    function d3_map_size() {
+        var size = 0;
+        for (var key in this._) ++size;
+        return size;
+    }
+    function d3_map_empty() {
+        for (var key in this._) return false;
+        return true;
+    }
+    d3.nest = function() {
+        var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
+        function map(mapType, array, depth) {
+            if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
+            var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
+            while (++i < n) {
+                if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
+                    values.push(object);
+                } else {
+                    valuesByKey.set(keyValue, [ object ]);
+                }
+            }
+            if (mapType) {
+                object = mapType();
+                setter = function(keyValue, values) {
+                    object.set(keyValue, map(mapType, values, depth));
+                };
+            } else {
+                object = {};
+                setter = function(keyValue, values) {
+                    object[keyValue] = map(mapType, values, depth);
+                };
+            }
+            valuesByKey.forEach(setter);
+            return object;
+        }
+        function entries(map, depth) {
+            if (depth >= keys.length) return map;
+            var array = [], sortKey = sortKeys[depth++];
+            map.forEach(function(key, keyMap) {
+                array.push({
+                    key: key,
+                    values: entries(keyMap, depth)
+                });
+            });
+            return sortKey ? array.sort(function(a, b) {
+                return sortKey(a.key, b.key);
+            }) : array;
+        }
+        nest.map = function(array, mapType) {
+            return map(mapType, array, 0);
+        };
+        nest.entries = function(array) {
+            return entries(map(d3.map, array, 0), 0);
+        };
+        nest.key = function(d) {
+            keys.push(d);
+            return nest;
+        };
+        nest.sortKeys = function(order) {
+            sortKeys[keys.length - 1] = order;
+            return nest;
+        };
+        nest.sortValues = function(order) {
+            sortValues = order;
+            return nest;
+        };
+        nest.rollup = function(f) {
+            rollup = f;
+            return nest;
+        };
+        return nest;
+    };
+    d3.set = function(array) {
+        var set = new d3_Set();
+        if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
+        return set;
+    };
+    function d3_Set() {
+        this._ = Object.create(null);
+    }
+    d3_class(d3_Set, {
+        has: d3_map_has,
+        add: function(key) {
+            this._[d3_map_escape(key += "")] = true;
+            return key;
+        },
+        remove: d3_map_remove,
+        values: d3_map_keys,
+        size: d3_map_size,
+        empty: d3_map_empty,
+        forEach: function(f) {
+            for (var key in this._) f.call(this, d3_map_unescape(key));
+        }
+    });
+    d3.behavior = {};
+    d3.rebind = function(target, source) {
+        var i = 1, n = arguments.length, method;
+        while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
+        return target;
+    };
+    function d3_rebind(target, source, method) {
+        return function() {
+            var value = method.apply(source, arguments);
+            return value === source ? target : value;
+        };
+    }
+    function d3_vendorSymbol(object, name) {
+        if (name in object) return name;
+        name = name.charAt(0).toUpperCase() + name.slice(1);
+        for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
+            var prefixName = d3_vendorPrefixes[i] + name;
+            if (prefixName in object) return prefixName;
+        }
+    }
+    var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
+    function d3_noop() {}
+    d3.dispatch = function() {
+        var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
+        while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
+        return dispatch;
+    };
+    function d3_dispatch() {}
+    d3_dispatch.prototype.on = function(type, listener) {
+        var i = type.indexOf("."), name = "";
+        if (i >= 0) {
+            name = type.slice(i + 1);
+            type = type.slice(0, i);
+        }
+        if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
+        if (arguments.length === 2) {
+            if (listener == null) for (type in this) {
+                if (this.hasOwnProperty(type)) this[type].on(name, null);
+            }
+            return this;
+        }
+    };
+    function d3_dispatch_event(dispatch) {
+        var listeners = [], listenerByName = new d3_Map();
+        function event() {
+            var z = listeners, i = -1, n = z.length, l;
+            while (++i < n) if (l = z[i].on) l.apply(this, arguments);
+            return dispatch;
+        }
+        event.on = function(name, listener) {
+            var l = listenerByName.get(name), i;
+            if (arguments.length < 2) return l && l.on;
+            if (l) {
+                l.on = null;
+                listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
+                listenerByName.remove(name);
+            }
+            if (listener) listeners.push(listenerByName.set(name, {
+                on: listener
+            }));
+            return dispatch;
+        };
+        return event;
+    }
+    d3.event = null;
+    function d3_eventPreventDefault() {
+        d3.event.preventDefault();
+    }
+    function d3_eventSource() {
+        var e = d3.event, s;
+        while (s = e.sourceEvent) e = s;
+        return e;
+    }
+    function d3_eventDispatch(target) {
+        var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
+        while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
+        dispatch.of = function(thiz, argumentz) {
+            return function(e1) {
+                try {
+                    var e0 = e1.sourceEvent = d3.event;
+                    e1.target = target;
+                    d3.event = e1;
+                    dispatch[e1.type].apply(thiz, argumentz);
+                } finally {
+                    d3.event = e0;
+                }
+            };
+        };
+        return dispatch;
+    }
+    d3.requote = function(s) {
+        return s.replace(d3_requote_re, "\\$&");
+    };
+    var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
+    var d3_subclass = {}.__proto__ ? function(object, prototype) {
+        object.__proto__ = prototype;
+    } : function(object, prototype) {
+        for (var property in prototype) object[property] = prototype[property];
+    };
+    function d3_selection(groups) {
+        d3_subclass(groups, d3_selectionPrototype);
+        return groups;
+    }
+    var d3_select = function(s, n) {
+        return n.querySelector(s);
+    }, d3_selectAll = function(s, n) {
+        return n.querySelectorAll(s);
+    }, d3_selectMatcher = d3_documentElement.matches || d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) {
+        return d3_selectMatcher.call(n, s);
+    };
+    if (typeof Sizzle === "function") {
+        d3_select = function(s, n) {
+            return Sizzle(s, n)[0] || null;
+        };
+        d3_selectAll = Sizzle;
+        d3_selectMatches = Sizzle.matchesSelector;
+    }
+    d3.selection = function() {
+        return d3_selectionRoot;
+    };
+    var d3_selectionPrototype = d3.selection.prototype = [];
+    d3_selectionPrototype.select = function(selector) {
+        var subgroups = [], subgroup, subnode, group, node;
+        selector = d3_selection_selector(selector);
+        for (var j = -1, m = this.length; ++j < m; ) {
+            subgroups.push(subgroup = []);
+            subgroup.parentNode = (group = this[j]).parentNode;
+            for (var i = -1, n = group.length; ++i < n; ) {
+                if (node = group[i]) {
+                    subgroup.push(subnode = selector.call(node, node.__data__, i, j));
+                    if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
+                } else {
+                    subgroup.push(null);
+                }
+            }
+        }
+        return d3_selection(subgroups);
+    };
+    function d3_selection_selector(selector) {
+        return typeof selector === "function" ? selector : function() {
+            return d3_select(selector, this);
+        };
+    }
+    d3_selectionPrototype.selectAll = function(selector) {
+        var subgroups = [], subgroup, node;
+        selector = d3_selection_selectorAll(selector);
+        for (var j = -1, m = this.length; ++j < m; ) {
+            for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+                if (node = group[i]) {
+                    subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
+                    subgroup.parentNode = node;
+                }
+            }
+        }
+        return d3_selection(subgroups);
+    };
+    function d3_selection_selectorAll(selector) {
+        return typeof selector === "function" ? selector : function() {
+            return d3_selectAll(selector, this);
+        };
+    }
+    var d3_nsPrefix = {
+        svg: "http://www.w3.org/2000/svg",
+        xhtml: "http://www.w3.org/1999/xhtml",
+        xlink: "http://www.w3.org/1999/xlink",
+        xml: "http://www.w3.org/XML/1998/namespace",
+        xmlns: "http://www.w3.org/2000/xmlns/"
+    };
+    d3.ns = {
+        prefix: d3_nsPrefix,
+        qualify: function(name) {
+            var i = name.indexOf(":"), prefix = name;
+            if (i >= 0) {
+                prefix = name.slice(0, i);
+                name = name.slice(i + 1);
+            }
+            return d3_nsPrefix.hasOwnProperty(prefix) ? {
+                space: d3_nsPrefix[prefix],
+                local: name
+            } : name;
+        }
+    };
+    d3_selectionPrototype.attr = function(name, value) {
+        if (arguments.length < 2) {
+            if (typeof name === "string") {
+                var node = this.node();
+                name = d3.ns.qualify(name);
+                return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
+            }
+            for (value in name) this.each(d3_selection_attr(value, name[value]));
+            return this;
+        }
+        return this.each(d3_selection_attr(name, value));
+    };
+    function d3_selection_attr(name, value) {
+        name = d3.ns.qualify(name);
+        function attrNull() {
+            this.removeAttribute(name);
+        }
+        function attrNullNS() {
+            this.removeAttributeNS(name.space, name.local);
+        }
+        function attrConstant() {
+            this.setAttribute(name, value);
+        }
+        function attrConstantNS() {
+            this.setAttributeNS(name.space, name.local, value);
+        }
+        function attrFunction() {
+            var x = value.apply(this, arguments);
+            if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
+        }
+        function attrFunctionNS() {
+            var x = value.apply(this, arguments);
+            if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
+        }
+        return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
+    }
+    function d3_collapse(s) {
+        return s.trim().replace(/\s+/g, " ");
+    }
+    d3_selectionPrototype.classed = function(name, value) {
+        if (arguments.length < 2) {
+            if (typeof name === "string") {
+                var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
+                if (value = node.classList) {
+                    while (++i < n) if (!value.contains(name[i])) return false;
+                } else {
+                    value = node.getAttribute("class");
+                    while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
+                }
+                return true;
+            }
+            for (value in name) this.each(d3_selection_classed(value, name[value]));
+            return this;
+        }
+        return this.each(d3_selection_classed(name, value));
+    };
+    function d3_selection_classedRe(name) {
+        return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
+    }
+    function d3_selection_classes(name) {
+        return (name + "").trim().split(/^|\s+/);
+    }
+    function d3_selection_classed(name, value) {
+        name = d3_selection_classes(name).map(d3_selection_classedName);
+        var n = name.length;
+        function classedConstant() {
+            var i = -1;
+            while (++i < n) name[i](this, value);
+        }
+        function classedFunction() {
+            var i = -1, x = value.apply(this, arguments);
+            while (++i < n) name[i](this, x);
+        }
+        return typeof value === "function" ? classedFunction : classedConstant;
+    }
+    function d3_selection_classedName(name) {
+        var re = d3_selection_classedRe(name);
+        return function(node, value) {
+            if (c = node.classList) return value ? c.add(name) : c.remove(name);
+            var c = node.getAttribute("class") || "";
+            if (value) {
+                re.lastIndex = 0;
+                if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
+            } else {
+                node.setAttribute("class", d3_collapse(c.replace(re, " ")));
+            }
+        };
+    }
+    d3_selectionPrototype.style = function(name, value, priority) {
+        var n = arguments.length;
+        if (n < 3) {
+            if (typeof name !== "string") {
+                if (n < 2) value = "";
+                for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
+                return this;
+            }
+            if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name);
+            priority = "";
+        }
+        return this.each(d3_selection_style(name, value, priority));
+    };
+    function d3_selection_style(name, value, priority) {
+        function styleNull() {
+            this.style.removeProperty(name);
+        }
+        function styleConstant() {
+            this.style.setProperty(name, value, priority);
+        }
+        function styleFunction() {
+            var x = value.apply(this, arguments);
+            if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
+        }
+        return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
+    }
+    d3_selectionPrototype.property = function(name, value) {
+        if (arguments.length < 2) {
+            if (typeof name === "string") return this.node()[name];
+            for (value in name) this.each(d3_selection_property(value, name[value]));
+            return this;
+        }
+        return this.each(d3_selection_property(name, value));
+    };
+    function d3_selection_property(name, value) {
+        function propertyNull() {
+            delete this[name];
+        }
+        function propertyConstant() {
+            this[name] = value;
+        }
+        function propertyFunction() {
+            var x = value.apply(this, arguments);
+            if (x == null) delete this[name]; else this[name] = x;
+        }
+        return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
+    }
+    d3_selectionPrototype.text = function(value) {
+        return arguments.length ? this.each(typeof value === "function" ? function() {
+            var v = value.apply(this, arguments);
+            this.textContent = v == null ? "" : v;
+        } : value == null ? function() {
+            this.textContent = "";
+        } : function() {
+            this.textContent = value;
+        }) : this.node().textContent;
+    };
+    d3_selectionPrototype.html = function(value) {
+        return arguments.length ? this.each(typeof value === "function" ? function() {
+            var v = value.apply(this, arguments);
+            this.innerHTML = v == null ? "" : v;
+        } : value == null ? function() {
+            this.innerHTML = "";
+        } : function() {
+            this.innerHTML = value;
+        }) : this.node().innerHTML;
+    };
+    d3_selectionPrototype.append = function(name) {
+        name = d3_selection_creator(name);
+        return this.select(function() {
+            return this.appendChild(name.apply(this, arguments));
+        });
+    };
+    function d3_selection_creator(name) {
+        return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() {
+            return this.ownerDocument.createElementNS(name.space, name.local);
+        } : function() {
+            return this.ownerDocument.createElementNS(this.namespaceURI, name);
+        };
+    }
+    d3_selectionPrototype.insert = function(name, before) {
+        name = d3_selection_creator(name);
+        before = d3_selection_selector(before);
+        return this.select(function() {
+            return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
+        });
+    };
+    d3_selectionPrototype.remove = function() {
+        return this.each(d3_selectionRemove);
+    };
+    function d3_selectionRemove() {
+        var parent = this.parentNode;
+        if (parent) parent.removeChild(this);
+    }
+    d3_selectionPrototype.data = function(value, key) {
+        var i = -1, n = this.length, group, node;
+        if (!arguments.length) {
+            value = new Array(n = (group = this[0]).length);
+            while (++i < n) {
+                if (node = group[i]) {
+                    value[i] = node.__data__;
+                }
+            }
+            return value;
+        }
+        function bind(group, groupData) {
+            var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData;
+            if (key) {
+                var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue;
+                for (i = -1; ++i < n; ) {
+                    if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) {
+                        exitNodes[i] = node;
+                    } else {
+                        nodeByKeyValue.set(keyValue, node);
+                    }
+                    keyValues[i] = keyValue;
+                }
+                for (i = -1; ++i < m; ) {
+                    if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
+                        enterNodes[i] = d3_selection_dataNode(nodeData);
+                    } else if (node !== true) {
+                        updateNodes[i] = node;
+                        node.__data__ = nodeData;
+                    }
+                    nodeByKeyValue.set(keyValue, true);
+                }
+                for (i = -1; ++i < n; ) {
+                    if (nodeByKeyValue.get(keyValues[i]) !== true) {
+                        exitNodes[i] = group[i];
+                    }
+                }
+            } else {
+                for (i = -1; ++i < n0; ) {
+                    node = group[i];
+                    nodeData = groupData[i];
+                    if (node) {
+                        node.__data__ = nodeData;
+                        updateNodes[i] = node;
+                    } else {
+                        enterNodes[i] = d3_selection_dataNode(nodeData);
+                    }
+                }
+                for (;i < m; ++i) {
+                    enterNodes[i] = d3_selection_dataNode(groupData[i]);
+                }
+                for (;i < n; ++i) {
+                    exitNodes[i] = group[i];
+                }
+            }
+            enterNodes.update = updateNodes;
+            enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
+            enter.push(enterNodes);
+            update.push(updateNodes);
+            exit.push(exitNodes);
+        }
+        var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
+        if (typeof value === "function") {
+            while (++i < n) {
+                bind(group = this[i], value.call(group, group.parentNode.__data__, i));
+            }
+        } else {
+            while (++i < n) {
+                bind(group = this[i], value);
+            }
+        }
+        update.enter = function() {
+            return enter;
+        };
+        update.exit = function() {
+            return exit;
+        };
+        return update;
+    };
+    function d3_selection_dataNode(data) {
+        return {
+            __data__: data
+        };
+    }
+    d3_selectionPrototype.datum = function(value) {
+        return arguments.length ? this.property("__data__", value) : this.property("__data__");
+    };
+    d3_selectionPrototype.filter = function(filter) {
+        var subgroups = [], subgroup, group, node;
+        if (typeof filter !== "function") filter = d3_selection_filter(filter);
+        for (var j = 0, m = this.length; j < m; j++) {
+            subgroups.push(subgroup = []);
+            subgroup.parentNode = (group = this[j]).parentNode;
+            for (var i = 0, n = group.length; i < n; i++) {
+                if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
+                    subgroup.push(node);
+                }
+            }
+        }
+        return d3_selection(subgroups);
+    };
+    function d3_selection_filter(selector) {
+        return function() {
+            return d3_selectMatches(this, selector);
+        };
+    }
+    d3_selectionPrototype.order = function() {
+        for (var j = -1, m = this.length; ++j < m; ) {
+            for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
+                if (node = group[i]) {
+                    if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
+                    next = node;
+                }
+            }
+        }
+        return this;
+    };
+    d3_selectionPrototype.sort = function(comparator) {
+        comparator = d3_selection_sortComparator.apply(this, arguments);
+        for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
+        return this.order();
+    };
+    function d3_selection_sortComparator(comparator) {
+        if (!arguments.length) comparator = d3_ascending;
+        return function(a, b) {
+            return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
+        };
+    }
+    d3_selectionPrototype.each = function(callback) {
+        return d3_selection_each(this, function(node, i, j) {
+            callback.call(node, node.__data__, i, j);
+        });
+    };
+    function d3_selection_each(groups, callback) {
+        for (var j = 0, m = groups.length; j < m; j++) {
+            for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
+                if (node = group[i]) callback(node, i, j);
+            }
+        }
+        return groups;
+    }
+    d3_selectionPrototype.call = function(callback) {
+        var args = d3_array(arguments);
+        callback.apply(args[0] = this, args);
+        return this;
+    };
+    d3_selectionPrototype.empty = function() {
+        return !this.node();
+    };
+    d3_selectionPrototype.node = function() {
+        for (var j = 0, m = this.length; j < m; j++) {
+            for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+                var node = group[i];
+                if (node) return node;
+            }
+        }
+        return null;
+    };
+    d3_selectionPrototype.size = function() {
+        var n = 0;
+        d3_selection_each(this, function() {
+            ++n;
+        });
+        return n;
+    };
+    function d3_selection_enter(selection) {
+        d3_subclass(selection, d3_selection_enterPrototype);
+        return selection;
+    }
+    var d3_selection_enterPrototype = [];
+    d3.selection.enter = d3_selection_enter;
+    d3.selection.enter.prototype = d3_selection_enterPrototype;
+    d3_selection_enterPrototype.append = d3_selectionPrototype.append;
+    d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
+    d3_selection_enterPrototype.node = d3_selectionPrototype.node;
+    d3_selection_enterPrototype.call = d3_selectionPrototype.call;
+    d3_selection_enterPrototype.size = d3_selectionPrototype.size;
+    d3_selection_enterPrototype.select = function(selector) {
+        var subgroups = [], subgroup, subnode, upgroup, group, node;
+        for (var j = -1, m = this.length; ++j < m; ) {
+            upgroup = (group = this[j]).update;
+            subgroups.push(subgroup = []);
+            subgroup.parentNode = group.parentNode;
+            for (var i = -1, n = group.length; ++i < n; ) {
+                if (node = group[i]) {
+                    subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
+                    subnode.__data__ = node.__data__;
+                } else {
+                    subgroup.push(null);
+                }
+            }
+        }
+        return d3_selection(subgroups);
+    };
+    d3_selection_enterPrototype.insert = function(name, before) {
+        if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
+        return d3_selectionPrototype.insert.call(this, name, before);
+    };
+    function d3_selection_enterInsertBefore(enter) {
+        var i0, j0;
+        return function(d, i, j) {
+            var group = enter[j].update, n = group.length, node;
+            if (j != j0) j0 = j, i0 = 0;
+            if (i >= i0) i0 = i + 1;
+            while (!(node = group[i0]) && ++i0 < n) ;
+            return node;
+        };
+    }
+    d3.select = function(node) {
+        var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ];
+        group.parentNode = d3_documentElement;
+        return d3_selection([ group ]);
+    };
+    d3.selectAll = function(nodes) {
+        var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
+        group.parentNode = d3_documentElement;
+        return d3_selection([ group ]);
+    };
+    var d3_selectionRoot = d3.select(d3_documentElement);
+    d3_selectionPrototype.on = function(type, listener, capture) {
+        var n = arguments.length;
+        if (n < 3) {
+            if (typeof type !== "string") {
+                if (n < 2) listener = false;
+                for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
+                return this;
+            }
+            if (n < 2) return (n = this.node()["__on" + type]) && n._;
+            capture = false;
+        }
+        return this.each(d3_selection_on(type, listener, capture));
+    };
+    function d3_selection_on(type, listener, capture) {
+        var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
+        if (i > 0) type = type.slice(0, i);
+        var filter = d3_selection_onFilters.get(type);
+        if (filter) type = filter, wrap = d3_selection_onFilter;
+        function onRemove() {
+            var l = this[name];
+            if (l) {
+                this.removeEventListener(type, l, l.$);
+                delete this[name];
+            }
+        }
+        function onAdd() {
+            var l = wrap(listener, d3_array(arguments));
+            onRemove.call(this);
+            this.addEventListener(type, this[name] = l, l.$ = capture);
+            l._ = listener;
+        }
+        function removeAll() {
+            var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
+            for (var name in this) {
+                if (match = name.match(re)) {
+                    var l = this[name];
+                    this.removeEventListener(match[1], l, l.$);
+                    delete this[name];
+                }
+            }
+        }
+        return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
+    }
+    var d3_selection_onFilters = d3.map({
+        mouseenter: "mouseover",
+        mouseleave: "mouseout"
+    });
+    d3_selection_onFilters.forEach(function(k) {
+        if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
+    });
+    function d3_selection_onListener(listener, argumentz) {
+        return function(e) {
+            var o = d3.event;
+            d3.event = e;
+            argumentz[0] = this.__data__;
+            try {
+                listener.apply(this, argumentz);
+            } finally {
+                d3.event = o;
+            }
+        };
+    }
+    function d3_selection_onFilter(listener, argumentz) {
+        var l = d3_selection_onListener(listener, argumentz);
+        return function(e) {
+            var target = this, related = e.relatedTarget;
+            if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
+                l.call(target, e);
+            }
+        };
+    }
+    var d3_event_dragSelect = "onselectstart" in d3_document ? null : d3_vendorSymbol(d3_documentElement.style, "userSelect"), d3_event_dragId = 0;
+    function d3_event_dragSuppress() {
+        var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault);
+        if (d3_event_dragSelect) {
+            var style = d3_documentElement.style, select = style[d3_event_dragSelect];
+            style[d3_event_dragSelect] = "none";
+        }
+        return function(suppressClick) {
+            w.on(name, null);
+            if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
+            if (suppressClick) {
+                var off = function() {
+                    w.on(click, null);
+                };
+                w.on(click, function() {
+                    d3_eventPreventDefault();
+                    off();
+                }, true);
+                setTimeout(off, 0);
+            }
+        };
+    }
+    d3.mouse = function(container) {
+        return d3_mousePoint(container, d3_eventSource());
+    };
+    var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0;
+    function d3_mousePoint(container, e) {
+        if (e.changedTouches) e = e.changedTouches[0];
+        var svg = container.ownerSVGElement || container;
+        if (svg.createSVGPoint) {
+            var point = svg.createSVGPoint();
+            if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) {
+                svg = d3.select("body").append("svg").style({
+                    position: "absolute",
+                    top: 0,
+                    left: 0,
+                    margin: 0,
+                    padding: 0,
+                    border: "none"
+                }, "important");
+                var ctm = svg[0][0].getScreenCTM();
+                d3_mouse_bug44083 = !(ctm.f || ctm.e);
+                svg.remove();
+            }
+            if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX,
+                point.y = e.clientY;
+            point = point.matrixTransform(container.getScreenCTM().inverse());
+            return [ point.x, point.y ];
+        }
+        var rect = container.getBoundingClientRect();
+        return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
+    }
+    d3.touch = function(container, touches, identifier) {
+        if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
+        if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
+            if ((touch = touches[i]).identifier === identifier) {
+                return d3_mousePoint(container, touch);
+            }
+        }
+    };
+    d3.behavior.drag = function() {
+        var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_behavior_dragMouseSubject, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_behavior_dragTouchSubject, "touchmove", "touchend");
+        function drag() {
+            this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
+        }
+        function dragstart(id, position, subject, move, end) {
+            return function() {
+                var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject()).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(), position0 = position(parent, dragId);
+                if (origin) {
+                    dragOffset = origin.apply(that, arguments);
+                    dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ];
+                } else {
+                    dragOffset = [ 0, 0 ];
+                }
+                dispatch({
+                    type: "dragstart"
+                });
+                function moved() {
+                    var position1 = position(parent, dragId), dx, dy;
+                    if (!position1) return;
+                    dx = position1[0] - position0[0];
+                    dy = position1[1] - position0[1];
+                    dragged |= dx | dy;
+                    position0 = position1;
+                    dispatch({
+                        type: "drag",
+                        x: position1[0] + dragOffset[0],
+                        y: position1[1] + dragOffset[1],
+                        dx: dx,
+                        dy: dy
+                    });
+                }
+                function ended() {
+                    if (!position(parent, dragId)) return;
+                    dragSubject.on(move + dragName, null).on(end + dragName, null);
+                    dragRestore(dragged && d3.event.target === target);
+                    dispatch({
+                        type: "dragend"
+                    });
+                }
+            };
+        }
+        drag.origin = function(x) {
+            if (!arguments.length) return origin;
+            origin = x;
+            return drag;
+        };
+        return d3.rebind(drag, event, "on");
+    };
+    function d3_behavior_dragTouchId() {
+        return d3.event.changedTouches[0].identifier;
+    }
+    function d3_behavior_dragTouchSubject() {
+        return d3.event.target;
+    }
+    function d3_behavior_dragMouseSubject() {
+        return d3_window;
+    }
+    d3.touches = function(container, touches) {
+        if (arguments.length < 2) touches = d3_eventSource().touches;
+        return touches ? d3_array(touches).map(function(touch) {
+            var point = d3_mousePoint(container, touch);
+            point.identifier = touch.identifier;
+            return point;
+        }) : [];
+    };
+    var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;
+    function d3_sgn(x) {
+        return x > 0 ? 1 : x < 0 ? -1 : 0;
+    }
+    function d3_cross2d(a, b, c) {
+        return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
+    }
+    function d3_acos(x) {
+        return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
+    }
+    function d3_asin(x) {
+        return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
+    }
+    function d3_sinh(x) {
+        return ((x = Math.exp(x)) - 1 / x) / 2;
+    }
+    function d3_cosh(x) {
+        return ((x = Math.exp(x)) + 1 / x) / 2;
+    }
+    function d3_tanh(x) {
+        return ((x = Math.exp(2 * x)) - 1) / (x + 1);
+    }
+    function d3_haversin(x) {
+        return (x = Math.sin(x / 2)) * x;
+    }
+    var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4;
+    d3.interpolateZoom = function(p0, p1) {
+        var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
+        var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ;
+        function interpolate(t) {
+            var s = t * S;
+            if (dr) {
+                var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
+                return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ];
+            }
+            return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ];
+        }
+        interpolate.duration = S * 1e3;
+        return interpolate;
+    };
+    d3.behavior.zoom = function() {
+        var view = {
+            x: 0,
+            y: 0,
+            k: 1
+        }, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1;
+        function zoom(g) {
+            g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
+        }
+        zoom.event = function(g) {
+            g.each(function() {
+                var dispatch = event.of(this, arguments), view1 = view;
+                if (d3_transitionInheritId) {
+                    d3.select(this).transition().each("start.zoom", function() {
+                        view = this.__chart__ || {
+                                x: 0,
+                                y: 0,
+                                k: 1
+                            };
+                        zoomstarted(dispatch);
+                    }).tween("zoom:zoom", function() {
+                        var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]);
+                        return function(t) {
+                            var l = i(t), k = dx / l[2];
+                            this.__chart__ = view = {
+                                x: cx - l[0] * k,
+                                y: cy - l[1] * k,
+                                k: k
+                            };
+                            zoomed(dispatch);
+                        };
+                    }).each("interrupt.zoom", function() {
+                        zoomended(dispatch);
+                    }).each("end.zoom", function() {
+                        zoomended(dispatch);
+                    });
+                } else {
+                    this.__chart__ = view;
+                    zoomstarted(dispatch);
+                    zoomed(dispatch);
+                    zoomended(dispatch);
+                }
+            });
+        };
+        zoom.translate = function(_) {
+            if (!arguments.length) return [ view.x, view.y ];
+            view = {
+                x: +_[0],
+                y: +_[1],
+                k: view.k
+            };
+            rescale();
+            return zoom;
+        };
+        zoom.scale = function(_) {
+            if (!arguments.length) return view.k;
+            view = {
+                x: view.x,
+                y: view.y,
+                k: +_
+            };
+            rescale();
+            return zoom;
+        };
+        zoom.scaleExtent = function(_) {
+            if (!arguments.length) return scaleExtent;
+            scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
+            return zoom;
+        };
+        zoom.center = function(_) {
+            if (!arguments.length) return center;
+            center = _ && [ +_[0], +_[1] ];
+            return zoom;
+        };
+        zoom.size = function(_) {
+            if (!arguments.length) return size;
+            size = _ && [ +_[0], +_[1] ];
+            return zoom;
+        };
+        zoom.duration = function(_) {
+            if (!arguments.length) return duration;
+            duration = +_;
+            return zoom;
+        };
+        zoom.x = function(z) {
+            if (!arguments.length) return x1;
+            x1 = z;
+            x0 = z.copy();
+            view = {
+                x: 0,
+                y: 0,
+                k: 1
+            };
+            return zoom;
+        };
+        zoom.y = function(z) {
+            if (!arguments.length) return y1;
+            y1 = z;
+            y0 = z.copy();
+            view = {
+                x: 0,
+                y: 0,
+                k: 1
+            };
+            return zoom;
+        };
+        function location(p) {
+            return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
+        }
+        function point(l) {
+            return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
+        }
+        function scaleTo(s) {
+            view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
+        }
+        function translateTo(p, l) {
+            l = point(l);
+            view.x += p[0] - l[0];
+            view.y += p[1] - l[1];
+        }
+        function zoomTo(that, p, l, k) {
+            that.__chart__ = {
+                x: view.x,
+                y: view.y,
+                k: view.k
+            };
+            scaleTo(Math.pow(2, k));
+            translateTo(center0 = p, l);
+            that = d3.select(that);
+            if (duration > 0) that = that.transition().duration(duration);
+            that.call(zoom.event);
+        }
+        function rescale() {
+            if (x1) x1.domain(x0.range().map(function(x) {
+                return (x - view.x) / view.k;
+            }).map(x0.invert));
+            if (y1) y1.domain(y0.range().map(function(y) {
+                return (y - view.y) / view.k;
+            }).map(y0.invert));
+        }
+        function zoomstarted(dispatch) {
+            if (!zooming++) dispatch({
+                type: "zoomstart"
+            });
+        }
+        function zoomed(dispatch) {
+            rescale();
+            dispatch({
+                type: "zoom",
+                scale: view.k,
+                translate: [ view.x, view.y ]
+            });
+        }
+        function zoomended(dispatch) {
+            if (!--zooming) dispatch({
+                type: "zoomend"
+            });
+            center0 = null;
+        }
+        function mousedowned() {
+            var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress();
+            d3_selection_interrupt.call(that);
+            zoomstarted(dispatch);
+            function moved() {
+                dragged = 1;
+                translateTo(d3.mouse(that), location0);
+                zoomed(dispatch);
+            }
+            function ended() {
+                subject.on(mousemove, null).on(mouseup, null);
+                dragRestore(dragged && d3.event.target === target);
+                zoomended(dispatch);
+            }
+        }
+        function touchstarted() {
+            var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress();
+            started();
+            zoomstarted(dispatch);
+            subject.on(mousedown, null).on(touchstart, started);
+            function relocate() {
+                var touches = d3.touches(that);
+                scale0 = view.k;
+                touches.forEach(function(t) {
+                    if (t.identifier in locations0) locations0[t.identifier] = location(t);
+                });
+                return touches;
+            }
+            function started() {
+                var target = d3.event.target;
+                d3.select(target).on(touchmove, moved).on(touchend, ended);
+                targets.push(target);
+                var changed = d3.event.changedTouches;
+                for (var i = 0, n = changed.length; i < n; ++i) {
+                    locations0[changed[i].identifier] = null;
+                }
+                var touches = relocate(), now = Date.now();
+                if (touches.length === 1) {
+                    if (now - touchtime < 500) {
+                        var p = touches[0];
+                        zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
+                        d3_eventPreventDefault();
+                    }
+                    touchtime = now;
+                } else if (touches.length > 1) {
+                    var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
+                    distance0 = dx * dx + dy * dy;
+                }
+            }
+            function moved() {
+                var touches = d3.touches(that), p0, l0, p1, l1;
+                d3_selection_interrupt.call(that);
+                for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
+                    p1 = touches[i];
+                    if (l1 = locations0[p1.identifier]) {
+                        if (l0) break;
+                        p0 = p1, l0 = l1;
+                    }
+                }
+                if (l1) {
+                    var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
+                    p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
+                    l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
+                    scaleTo(scale1 * scale0);
+                }
+                touchtime = null;
+                translateTo(p0, l0);
+                zoomed(dispatch);
+            }
+            function ended() {
+                if (d3.event.touches.length) {
+                    var changed = d3.event.changedTouches;
+                    for (var i = 0, n = changed.length; i < n; ++i) {
+                        delete locations0[changed[i].identifier];
+                    }
+                    for (var identifier in locations0) {
+                        return void relocate();
+                    }
+                }
+                d3.selectAll(targets).on(zoomName, null);
+                subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
+                dragRestore();
+                zoomended(dispatch);
+            }
+        }
+        function mousewheeled() {
+            var dispatch = event.of(this, arguments);
+            if (mousewheelTimer) clearTimeout(mousewheelTimer); else translate0 = location(center0 = center || d3.mouse(this)),
+                d3_selection_interrupt.call(this), zoomstarted(dispatch);
+            mousewheelTimer = setTimeout(function() {
+                mousewheelTimer = null;
+                zoomended(dispatch);
+            }, 50);
+            d3_eventPreventDefault();
+            scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
+            translateTo(center0, translate0);
+            zoomed(dispatch);
+        }
+        function dblclicked() {
+            var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2;
+            zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
+        }
+        return d3.rebind(zoom, event, "on");
+    };
+    var d3_behavior_zoomInfinity = [ 0, Infinity ];
+    var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
+        return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
+    }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
+        return d3.event.wheelDelta;
+    }, "mousewheel") : (d3_behavior_zoomDelta = function() {
+        return -d3.event.detail;
+    }, "MozMousePixelScroll");
+    d3.color = d3_color;
+    function d3_color() {}
+    d3_color.prototype.toString = function() {
+        return this.rgb() + "";
+    };
+    d3.hsl = d3_hsl;
+    function d3_hsl(h, s, l) {
+        return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l);
+    }
+    var d3_hslPrototype = d3_hsl.prototype = new d3_color();
+    d3_hslPrototype.brighter = function(k) {
+        k = Math.pow(.7, arguments.length ? k : 1);
+        return new d3_hsl(this.h, this.s, this.l / k);
+    };
+    d3_hslPrototype.darker = function(k) {
+        k = Math.pow(.7, arguments.length ? k : 1);
+        return new d3_hsl(this.h, this.s, k * this.l);
+    };
+    d3_hslPrototype.rgb = function() {
+        return d3_hsl_rgb(this.h, this.s, this.l);
+    };
+    function d3_hsl_rgb(h, s, l) {
+        var m1, m2;
+        h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
+        s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
+        l = l < 0 ? 0 : l > 1 ? 1 : l;
+        m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
+        m1 = 2 * l - m2;
+        function v(h) {
+            if (h > 360) h -= 360; else if (h < 0) h += 360;
+            if (h < 60) return m1 + (m2 - m1) * h / 60;
+            if (h < 180) return m2;
+            if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
+            return m1;
+        }
+        function vv(h) {
+            return Math.round(v(h) * 255);
+        }
+        return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
+    }
+    d3.hcl = d3_hcl;
+    function d3_hcl(h, c, l) {
+        return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l);
+    }
+    var d3_hclPrototype = d3_hcl.prototype = new d3_color();
+    d3_hclPrototype.brighter = function(k) {
+        return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
+    };
+    d3_hclPrototype.darker = function(k) {
+        return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
+    };
+    d3_hclPrototype.rgb = function() {
+        return d3_hcl_lab(this.h, this.c, this.l).rgb();
+    };
+    function d3_hcl_lab(h, c, l) {
+        if (isNaN(h)) h = 0;
+        if (isNaN(c)) c = 0;
+        return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
+    }
+    d3.lab = d3_lab;
+    function d3_lab(l, a, b) {
+        return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b);
+    }
+    var d3_lab_K = 18;
+    var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
+    var d3_labPrototype = d3_lab.prototype = new d3_color();
+    d3_labPrototype.brighter = function(k) {
+        return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
+    };
+    d3_labPrototype.darker = function(k) {
+        return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
+    };
+    d3_labPrototype.rgb = function() {
+        return d3_lab_rgb(this.l, this.a, this.b);
+    };
+    function d3_lab_rgb(l, a, b) {
+        var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
+        x = d3_lab_xyz(x) * d3_lab_X;
+        y = d3_lab_xyz(y) * d3_lab_Y;
+        z = d3_lab_xyz(z) * d3_lab_Z;
+        return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z));
+    }
+    function d3_lab_hcl(l, a, b) {
+        return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l);
+    }
+    function d3_lab_xyz(x) {
+        return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
+    }
+    function d3_xyz_lab(x) {
+        return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
+    }
+    function d3_xyz_rgb(r) {
+        return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
+    }
+    d3.rgb = d3_rgb;
+    function d3_rgb(r, g, b) {
+        return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b);
+    }
+    function d3_rgbNumber(value) {
+        return new d3_rgb(value >> 16, value >> 8 & 255, value & 255);
+    }
+    function d3_rgbString(value) {
+        return d3_rgbNumber(value) + "";
+    }
+    var d3_rgbPrototype = d3_rgb.prototype = new d3_color();
+    d3_rgbPrototype.brighter = function(k) {
+        k = Math.pow(.7, arguments.length ? k : 1);
+        var r = this.r, g = this.g, b = this.b, i = 30;
+        if (!r && !g && !b) return new d3_rgb(i, i, i);
+        if (r && r < i) r = i;
+        if (g && g < i) g = i;
+        if (b && b < i) b = i;
+        return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
+    };
+    d3_rgbPrototype.darker = function(k) {
+        k = Math.pow(.7, arguments.length ? k : 1);
+        return new d3_rgb(k * this.r, k * this.g, k * this.b);
+    };
+    d3_rgbPrototype.hsl = function() {
+        return d3_rgb_hsl(this.r, this.g, this.b);
+    };
+    d3_rgbPrototype.toString = function() {
+        return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
+    };
+    function d3_rgb_hex(v) {
+        return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
+    }
+    function d3_rgb_parse(format, rgb, hsl) {
+        var r = 0, g = 0, b = 0, m1, m2, color;
+        m1 = /([a-z]+)\((.*)\)/i.exec(format);
+        if (m1) {
+            m2 = m1[2].split(",");
+            switch (m1[1]) {
+                case "hsl":
+                {
+                    return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
+                }
+
+                case "rgb":
+                {
+                    return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
+                }
+            }
+        }
+        if (color = d3_rgb_names.get(format)) return rgb(color.r, color.g, color.b);
+        if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
+            if (format.length === 4) {
+                r = (color & 3840) >> 4;
+                r = r >> 4 | r;
+                g = color & 240;
+                g = g >> 4 | g;
+                b = color & 15;
+                b = b << 4 | b;
+            } else if (format.length === 7) {
+                r = (color & 16711680) >> 16;
+                g = (color & 65280) >> 8;
+                b = color & 255;
+            }
+        }
+        return rgb(r, g, b);
+    }
+    function d3_rgb_hsl(r, g, b) {
+        var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2;
+        if (d) {
+            s = l < .5 ? d / (max + min) : d / (2 - max - min);
+            if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4;
+            h *= 60;
+        } else {
+            h = NaN;
+            s = l > 0 && l < 1 ? 0 : h;
+        }
+        return new d3_hsl(h, s, l);
+    }
+    function d3_rgb_lab(r, g, b) {
+        r = d3_rgb_xyz(r);
+        g = d3_rgb_xyz(g);
+        b = d3_rgb_xyz(b);
+        var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z);
+        return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
+    }
+    function d3_rgb_xyz(r) {
+        return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
+    }
+    function d3_rgb_parseNumber(c) {
+        var f = parseFloat(c);
+        return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
+    }
+    var d3_rgb_names = d3.map({
+        aliceblue: 15792383,
+        antiquewhite: 16444375,
+        aqua: 65535,
+        aquamarine: 8388564,
+        azure: 15794175,
+        beige: 16119260,
+        bisque: 16770244,
+        black: 0,
+        blanchedalmond: 16772045,
+        blue: 255,
+        blueviolet: 9055202,
+        brown: 10824234,
+        burlywood: 14596231,
+        cadetblue: 6266528,
+        chartreuse: 8388352,
+        chocolate: 13789470,
+        coral: 16744272,
+        cornflowerblue: 6591981,
+        cornsilk: 16775388,
+        crimson: 14423100,
+        cyan: 65535,
+        darkblue: 139,
+        darkcyan: 35723,
+        darkgoldenrod: 12092939,
+        darkgray: 11119017,
+        darkgreen: 25600,
+        darkgrey: 11119017,
+        darkkhaki: 12433259,
+        darkmagenta: 9109643,
+        darkolivegreen: 5597999,
+        darkorange: 16747520,
+        darkorchid: 10040012,
+        darkred: 9109504,
+        darksalmon: 15308410,
+        darkseagreen: 9419919,
+        darkslateblue: 4734347,
+        darkslategray: 3100495,
+        darkslategrey: 3100495,
+        darkturquoise: 52945,
+        darkviolet: 9699539,
+        deeppink: 16716947,
+        deepskyblue: 49151,
+        dimgray: 6908265,
+        dimgrey: 6908265,
+        dodgerblue: 2003199,
+        firebrick: 11674146,
+        floralwhite: 16775920,
+        forestgreen: 2263842,
+        fuchsia: 16711935,
+        gainsboro: 14474460,
+        ghostwhite: 16316671,
+        gold: 16766720,
+        goldenrod: 14329120,
+        gray: 8421504,
+        green: 32768,
+        greenyellow: 11403055,
+        grey: 8421504,
+        honeydew: 15794160,
+        hotpink: 16738740,
+        indianred: 13458524,
+        indigo: 4915330,
+        ivory: 16777200,
+        khaki: 15787660,
+        lavender: 15132410,
+        lavenderblush: 16773365,
+        lawngreen: 8190976,
+        lemonchiffon: 16775885,
+        lightblue: 11393254,
+        lightcoral: 15761536,
+        lightcyan: 14745599,
+        lightgoldenrodyellow: 16448210,
+        lightgray: 13882323,
+        lightgreen: 9498256,
+        lightgrey: 13882323,
+        lightpink: 16758465,
+        lightsalmon: 16752762,
+        lightseagreen: 2142890,
+        lightskyblue: 8900346,
+        lightslategray: 7833753,
+        lightslategrey: 7833753,
+        lightsteelblue: 11584734,
+        lightyellow: 16777184,
+        lime: 65280,
+        limegreen: 3329330,
+        linen: 16445670,
+        magenta: 16711935,
+        maroon: 8388608,
+        mediumaquamarine: 6737322,
+        mediumblue: 205,
+        mediumorchid: 12211667,
+        mediumpurple: 9662683,
+        mediumseagreen: 3978097,
+        mediumslateblue: 8087790,
+        mediumspringgreen: 64154,
+        mediumturquoise: 4772300,
+        mediumvioletred: 13047173,
+        midnightblue: 1644912,
+        mintcream: 16121850,
+        mistyrose: 16770273,
+        moccasin: 16770229,
+        navajowhite: 16768685,
+        navy: 128,
+        oldlace: 16643558,
+        olive: 8421376,
+        olivedrab: 7048739,
+        orange: 16753920,
+        orangered: 16729344,
+        orchid: 14315734,
+        palegoldenrod: 15657130,
+        palegreen: 10025880,
+        paleturquoise: 11529966,
+        palevioletred: 14381203,
+        papayawhip: 16773077,
+        peachpuff: 16767673,
+        peru: 13468991,
+        pink: 16761035,
+        plum: 14524637,
+        powderblue: 11591910,
+        purple: 8388736,
+        red: 16711680,
+        rosybrown: 12357519,
+        royalblue: 4286945,
+        saddlebrown: 9127187,
+        salmon: 16416882,
+        sandybrown: 16032864,
+        seagreen: 3050327,
+        seashell: 16774638,
+        sienna: 10506797,
+        silver: 12632256,
+        skyblue: 8900331,
+        slateblue: 6970061,
+        slategray: 7372944,
+        slategrey: 7372944,
+        snow: 16775930,
+        springgreen: 65407,
+        steelblue: 4620980,
+        tan: 13808780,
+        teal: 32896,
+        thistle: 14204888,
+        tomato: 16737095,
+        turquoise: 4251856,
+        violet: 15631086,
+        wheat: 16113331,
+        white: 16777215,
+        whitesmoke: 16119285,
+        yellow: 16776960,
+        yellowgreen: 10145074
+    });
+    d3_rgb_names.forEach(function(key, value) {
+        d3_rgb_names.set(key, d3_rgbNumber(value));
+    });
+    function d3_functor(v) {
+        return typeof v === "function" ? v : function() {
+            return v;
+        };
+    }
+    d3.functor = d3_functor;
+    function d3_identity(d) {
+        return d;
+    }
+    d3.xhr = d3_xhrType(d3_identity);
+    function d3_xhrType(response) {
+        return function(url, mimeType, callback) {
+            if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType,
+                mimeType = null;
+            return d3_xhr(url, mimeType, response, callback);
+        };
+    }
+    function d3_xhr(url, mimeType, response, callback) {
+        var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
+        if (d3_window.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
+        "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
+            request.readyState > 3 && respond();
+        };
+        function respond() {
+            var status = request.status, result;
+            if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
+                try {
+                    result = response.call(xhr, request);
+                } catch (e) {
+                    dispatch.error.call(xhr, e);
+                    return;
+                }
+                dispatch.load.call(xhr, result);
+            } else {
+                dispatch.error.call(xhr, request);
+            }
+        }
+        request.onprogress = function(event) {
+            var o = d3.event;
+            d3.event = event;
+            try {
+                dispatch.progress.call(xhr, request);
+            } finally {
+                d3.event = o;
+            }
+        };
+        xhr.header = function(name, value) {
+            name = (name + "").toLowerCase();
+            if (arguments.length < 2) return headers[name];
+            if (value == null) delete headers[name]; else headers[name] = value + "";
+            return xhr;
+        };
+        xhr.mimeType = function(value) {
+            if (!arguments.length) return mimeType;
+            mimeType = value == null ? null : value + "";
+            return xhr;
+        };
+        xhr.responseType = function(value) {
+            if (!arguments.length) return responseType;
+            responseType = value;
+            return xhr;
+        };
+        xhr.response = function(value) {
+            response = value;
+            return xhr;
+        };
+        [ "get", "post" ].forEach(function(method) {
+            xhr[method] = function() {
+                return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
+            };
+        });
+        xhr.send = function(method, data, callback) {
+            if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
+            request.open(method, url, true);
+            if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
+            if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
+            if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
+            if (responseType != null) request.responseType = responseType;
+            if (callback != null) xhr.on("error", callback).on("load", function(request) {
+                callback(null, request);
+            });
+            dispatch.beforesend.call(xhr, request);
+            request.send(data == null ? null : data);
+            return xhr;
+        };
+        xhr.abort = function() {
+            request.abort();
+            return xhr;
+        };
+        d3.rebind(xhr, dispatch, "on");
+        return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
+    }
+    function d3_xhr_fixCallback(callback) {
+        return callback.length === 1 ? function(error, request) {
+            callback(error == null ? request : null);
+        } : callback;
+    }
+    function d3_xhrHasResponse(request) {
+        var type = request.responseType;
+        return type && type !== "text" ? request.response : request.responseText;
+    }
+    d3.dsv = function(delimiter, mimeType) {
+        var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
+        function dsv(url, row, callback) {
+            if (arguments.length < 3) callback = row, row = null;
+            var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
+            xhr.row = function(_) {
+                return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
+            };
+            return xhr;
+        }
+        function response(request) {
+            return dsv.parse(request.responseText);
+        }
+        function typedResponse(f) {
+            return function(request) {
+                return dsv.parse(request.responseText, f);
+            };
+        }
+        dsv.parse = function(text, f) {
+            var o;
+            return dsv.parseRows(text, function(row, i) {
+                if (o) return o(row, i - 1);
+                var a = new Function("d", "return {" + row.map(function(name, i) {
+                    return JSON.stringify(name) + ": d[" + i + "]";
+                }).join(",") + "}");
+                o = f ? function(row, i) {
+                    return f(a(row), i);
+                } : a;
+            });
+        };
+        dsv.parseRows = function(text, f) {
+            var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
+            function token() {
+                if (I >= N) return EOF;
+                if (eol) return eol = false, EOL;
+                var j = I;
+                if (text.charCodeAt(j) === 34) {
+                    var i = j;
+                    while (i++ < N) {
+                        if (text.charCodeAt(i) === 34) {
+                            if (text.charCodeAt(i + 1) !== 34) break;
+                            ++i;
+                        }
+                    }
+                    I = i + 2;
+                    var c = text.charCodeAt(i + 1);
+                    if (c === 13) {
+                        eol = true;
+                        if (text.charCodeAt(i + 2) === 10) ++I;
+                    } else if (c === 10) {
+                        eol = true;
+                    }
+                    return text.slice(j + 1, i).replace(/""/g, '"');
+                }
+                while (I < N) {
+                    var c = text.charCodeAt(I++), k = 1;
+                    if (c === 10) eol = true; else if (c === 13) {
+                        eol = true;
+                        if (text.charCodeAt(I) === 10) ++I, ++k;
+                    } else if (c !== delimiterCode) continue;
+                    return text.slice(j, I - k);
+                }
+                return text.slice(j);
+            }
+            while ((t = token()) !== EOF) {
+                var a = [];
+                while (t !== EOL && t !== EOF) {
+                    a.push(t);
+                    t = token();
+                }
+                if (f && (a = f(a, n++)) == null) continue;
+                rows.push(a);
+            }
+            return rows;
+        };
+        dsv.format = function(rows) {
+            if (Array.isArray(rows[0])) return dsv.formatRows(rows);
+            var fieldSet = new d3_Set(), fields = [];
+            rows.forEach(function(row) {
+                for (var field in row) {
+                    if (!fieldSet.has(field)) {
+                        fields.push(fieldSet.add(field));
+                    }
+                }
+            });
+            return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
+                return fields.map(function(field) {
+                    return formatValue(row[field]);
+                }).join(delimiter);
+            })).join("\n");
+        };
+        dsv.formatRows = function(rows) {
+            return rows.map(formatRow).join("\n");
+        };
+        function formatRow(row) {
+            return row.map(formatValue).join(delimiter);
+        }
+        function formatValue(text) {
+            return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
+        }
+        return dsv;
+    };
+    d3.csv = d3.dsv(",", "text/csv");
+    d3.tsv = d3.dsv("	", "text/tab-separated-values");
+    var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) {
+            setTimeout(callback, 17);
+        };
+    d3.timer = function(callback, delay, then) {
+        var n = arguments.length;
+        if (n < 2) delay = 0;
+        if (n < 3) then = Date.now();
+        var time = then + delay, timer = {
+            c: callback,
+            t: time,
+            f: false,
+            n: null
+        };
+        if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
+        d3_timer_queueTail = timer;
+        if (!d3_timer_interval) {
+            d3_timer_timeout = clearTimeout(d3_timer_timeout);
+            d3_timer_interval = 1;
+            d3_timer_frame(d3_timer_step);
+        }
+    };
+    function d3_timer_step() {
+        var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
+        if (delay > 24) {
+            if (isFinite(delay)) {
+                clearTimeout(d3_timer_timeout);
+                d3_timer_timeout = setTimeout(d3_timer_step, delay);
+            }
+            d3_timer_interval = 0;
+        } else {
+            d3_timer_interval = 1;
+            d3_timer_frame(d3_timer_step);
+        }
+    }
+    d3.timer.flush = function() {
+        d3_timer_mark();
+        d3_timer_sweep();
+    };
+    function d3_timer_mark() {
+        var now = Date.now();
+        d3_timer_active = d3_timer_queueHead;
+        while (d3_timer_active) {
+            if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
+            d3_timer_active = d3_timer_active.n;
+        }
+        return now;
+    }
+    function d3_timer_sweep() {
+        var t0, t1 = d3_timer_queueHead, time = Infinity;
+        while (t1) {
+            if (t1.f) {
+                t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
+            } else {
+                if (t1.t < time) time = t1.t;
+                t1 = (t0 = t1).n;
+            }
+        }
+        d3_timer_queueTail = t0;
+        return time;
+    }
+    function d3_format_precision(x, p) {
+        return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
+    }
+    d3.round = function(x, n) {
+        return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
+    };
+    var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
+    d3.formatPrefix = function(value, precision) {
+        var i = 0;
+        if (value) {
+            if (value < 0) value *= -1;
+            if (precision) value = d3.round(value, d3_format_precision(value, precision));
+            i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
+            i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3));
+        }
+        return d3_formatPrefixes[8 + i / 3];
+    };
+    function d3_formatPrefix(d, i) {
+        var k = Math.pow(10, abs(8 - i) * 3);
+        return {
+            scale: i > 8 ? function(d) {
+                return d / k;
+            } : function(d) {
+                return d * k;
+            },
+            symbol: d
+        };
+    }
+    function d3_locale_numberFormat(locale) {
+        var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) {
+            var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0;
+            while (i > 0 && g > 0) {
+                if (length + g + 1 > width) g = Math.max(1, width - length);
+                t.push(value.substring(i -= g, i + g));
+                if ((length += g + 1) > width) break;
+                g = locale_grouping[j = (j + 1) % locale_grouping.length];
+            }
+            return t.reverse().join(locale_thousands);
+        } : d3_identity;
+        return function(specifier) {
+            var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true;
+            if (precision) precision = +precision.substring(1);
+            if (zfill || fill === "0" && align === "=") {
+                zfill = fill = "0";
+                align = "=";
+            }
+            switch (type) {
+                case "n":
+                    comma = true;
+                    type = "g";
+                    break;
+
+                case "%":
+                    scale = 100;
+                    suffix = "%";
+                    type = "f";
+                    break;
+
+                case "p":
+                    scale = 100;
+                    suffix = "%";
+                    type = "r";
+                    break;
+
+                case "b":
+                case "o":
+                case "x":
+                case "X":
+                    if (symbol === "#") prefix = "0" + type.toLowerCase();
+
+                case "c":
+                    exponent = false;
+
+                case "d":
+                    integer = true;
+                    precision = 0;
+                    break;
+
+                case "s":
+                    scale = -1;
+                    type = "r";
+                    break;
+            }
+            if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1];
+            if (type == "r" && !precision) type = "g";
+            if (precision != null) {
+                if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision));
+            }
+            type = d3_format_types.get(type) || d3_format_typeDefault;
+            var zcomma = zfill && comma;
+            return function(value) {
+                var fullSuffix = suffix;
+                if (integer && value % 1) return "";
+                var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign;
+                if (scale < 0) {
+                    var unit = d3.formatPrefix(value, precision);
+                    value = unit.scale(value);
+                    fullSuffix = unit.symbol + suffix;
+                } else {
+                    value *= scale;
+                }
+                value = type(value, precision);
+                var i = value.lastIndexOf("."), before, after;
+                if (i < 0) {
+                    var j = exponent ? value.lastIndexOf("e") : -1;
+                    if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j);
+                } else {
+                    before = value.substring(0, i);
+                    after = locale_decimal + value.substring(i + 1);
+                }
+                if (!zfill && comma) before = formatGroup(before, Infinity);
+                var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
+                if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity);
+                negative += prefix;
+                value = before + after;
+                return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix;
+            };
+        };
+    }
+    var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
+    var d3_format_types = d3.map({
+        b: function(x) {
+            return x.toString(2);
+        },
+        c: function(x) {
+            return String.fromCharCode(x);
+        },
+        o: function(x) {
+            return x.toString(8);
+        },
+        x: function(x) {
+            return x.toString(16);
+        },
+        X: function(x) {
+            return x.toString(16).toUpperCase();
+        },
+        g: function(x, p) {
+            return x.toPrecision(p);
+        },
+        e: function(x, p) {
+            return x.toExponential(p);
+        },
+        f: function(x, p) {
+            return x.toFixed(p);
+        },
+        r: function(x, p) {
+            return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p))));
+        }
+    });
+    function d3_format_typeDefault(x) {
+        return x + "";
+    }
+    var d3_time = d3.time = {}, d3_date = Date;
+    function d3_date_utc() {
+        this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
+    }
+    d3_date_utc.prototype = {
+        getDate: function() {
+            return this._.getUTCDate();
+        },
+        getDay: function() {
+            return this._.getUTCDay();
+        },
+        getFullYear: function() {
+            return this._.getUTCFullYear();
+        },
+        getHours: function() {
+            return this._.getUTCHours();
+        },
+        getMilliseconds: function() {
+            return this._.getUTCMilliseconds();
+        },
+        getMinutes: function() {
+            return this._.getUTCMinutes();
+        },
+        getMonth: function() {
+            return this._.getUTCMonth();
+        },
+        getSeconds: function() {
+            return this._.getUTCSeconds();
+        },
+        getTime: function() {
+            return this._.getTime();
+        },
+        getTimezoneOffset: function() {
+            return 0;
+        },
+        valueOf: function() {
+            return this._.valueOf();
+        },
+        setDate: function() {
+            d3_time_prototype.setUTCDate.apply(this._, arguments);
+        },
+        setDay: function() {
+            d3_time_prototype.setUTCDay.apply(this._, arguments);
+        },
+        setFullYear: function() {
+            d3_time_prototype.setUTCFullYear.apply(this._, arguments);
+        },
+        setHours: function() {
+            d3_time_prototype.setUTCHours.apply(this._, arguments);
+        },
+        setMilliseconds: function() {
+            d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
+        },
+        setMinutes: function() {
+            d3_time_prototype.setUTCMinutes.apply(this._, arguments);
+        },
+        setMonth: function() {
+            d3_time_prototype.setUTCMonth.apply(this._, arguments);
+        },
+        setSeconds: function() {
+            d3_time_prototype.setUTCSeconds.apply(this._, arguments);
+        },
+        setTime: function() {
+            d3_time_prototype.setTime.apply(this._, arguments);
+        }
+    };
+    var d3_time_prototype = Date.prototype;
+    function d3_time_interval(local, step, number) {
+        function round(date) {
+            var d0 = local(date), d1 = offset(d0, 1);
+            return date - d0 < d1 - date ? d0 : d1;
+        }
+        function ceil(date) {
+            step(date = local(new d3_date(date - 1)), 1);
+            return date;
+        }
+        function offset(date, k) {
+            step(date = new d3_date(+date), k);
+            return date;
+        }
+        function range(t0, t1, dt) {
+            var time = ceil(t0), times = [];
+            if (dt > 1) {
+                while (time < t1) {
+                    if (!(number(time) % dt)) times.push(new Date(+time));
+                    step(time, 1);
+                }
+            } else {
+                while (time < t1) times.push(new Date(+time)), step(time, 1);
+            }
+            return times;
+        }
+        function range_utc(t0, t1, dt) {
+            try {
+                d3_date = d3_date_utc;
+                var utc = new d3_date_utc();
+                utc._ = t0;
+                return range(utc, t1, dt);
+            } finally {
+                d3_date = Date;
+            }
+        }
+        local.floor = local;
+        local.round = round;
+        local.ceil = ceil;
+        local.offset = offset;
+        local.range = range;
+        var utc = local.utc = d3_time_interval_utc(local);
+        utc.floor = utc;
+        utc.round = d3_time_interval_utc(round);
+        utc.ceil = d3_time_interval_utc(ceil);
+        utc.offset = d3_time_interval_utc(offset);
+        utc.range = range_utc;
+        return local;
+    }
+    function d3_time_interval_utc(method) {
+        return function(date, k) {
+            try {
+                d3_date = d3_date_utc;
+                var utc = new d3_date_utc();
+                utc._ = date;
+                return method(utc, k)._;
+            } finally {
+                d3_date = Date;
+            }
+        };
+    }
+    d3_time.year = d3_time_interval(function(date) {
+        date = d3_time.day(date);
+        date.setMonth(0, 1);
+        return date;
+    }, function(date, offset) {
+        date.setFullYear(date.getFullYear() + offset);
+    }, function(date) {
+        return date.getFullYear();
+    });
+    d3_time.years = d3_time.year.range;
+    d3_time.years.utc = d3_time.year.utc.range;
+    d3_time.day = d3_time_interval(function(date) {
+        var day = new d3_date(2e3, 0);
+        day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
+        return day;
+    }, function(date, offset) {
+        date.setDate(date.getDate() + offset);
+    }, function(date) {
+        return date.getDate() - 1;
+    });
+    d3_time.days = d3_time.day.range;
+    d3_time.days.utc = d3_time.day.utc.range;
+    d3_time.dayOfYear = function(date) {
+        var year = d3_time.year(date);
+        return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
+    };
+    [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) {
+        i = 7 - i;
+        var interval = d3_time[day] = d3_time_interval(function(date) {
+            (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
+            return date;
+        }, function(date, offset) {
+            date.setDate(date.getDate() + Math.floor(offset) * 7);
+        }, function(date) {
+            var day = d3_time.year(date).getDay();
+            return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
+        });
+        d3_time[day + "s"] = interval.range;
+        d3_time[day + "s"].utc = interval.utc.range;
+        d3_time[day + "OfYear"] = function(date) {
+            var day = d3_time.year(date).getDay();
+            return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7);
+        };
+    });
+    d3_time.week = d3_time.sunday;
+    d3_time.weeks = d3_time.sunday.range;
+    d3_time.weeks.utc = d3_time.sunday.utc.range;
+    d3_time.weekOfYear = d3_time.sundayOfYear;
+    function d3_locale_timeFormat(locale) {
+        var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths;
+        function d3_time_format(template) {
+            var n = template.length;
+            function format(date) {
+                var string = [], i = -1, j = 0, c, p, f;
+                while (++i < n) {
+                    if (template.charCodeAt(i) === 37) {
+                        string.push(template.slice(j, i));
+                        if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
+                        if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
+                        string.push(c);
+                        j = i + 1;
+                    }
+                }
+                string.push(template.slice(j, i));
+                return string.join("");
+            }
+            format.parse = function(string) {
+                var d = {
+                    y: 1900,
+                    m: 0,
+                    d: 1,
+                    H: 0,
+                    M: 0,
+                    S: 0,
+                    L: 0,
+                    Z: null
+                }, i = d3_time_parse(d, template, string, 0);
+                if (i != string.length) return null;
+                if ("p" in d) d.H = d.H % 12 + d.p * 12;
+                var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)();
+                if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) {
+                    date.setFullYear(d.y, 0, 1);
+                    date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
+                } else date.setFullYear(d.y, d.m, d.d);
+                date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L);
+                return localZ ? date._ : date;
+            };
+            format.toString = function() {
+                return template;
+            };
+            return format;
+        }
+        function d3_time_parse(date, template, string, j) {
+            var c, p, t, i = 0, n = template.length, m = string.length;
+            while (i < n) {
+                if (j >= m) return -1;
+                c = template.charCodeAt(i++);
+                if (c === 37) {
+                    t = template.charAt(i++);
+                    p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t];
+                    if (!p || (j = p(date, string, j)) < 0) return -1;
+                } else if (c != string.charCodeAt(j++)) {
+                    return -1;
+                }
+            }
+            return j;
+        }
+        d3_time_format.utc = function(template) {
+            var local = d3_time_format(template);
+            function format(date) {
+                try {
+                    d3_date = d3_date_utc;
+                    var utc = new d3_date();
+                    utc._ = date;
+                    return local(utc);
+                } finally {
+                    d3_date = Date;
+                }
+            }
+            format.parse = function(string) {
+                try {
+                    d3_date = d3_date_utc;
+                    var date = local.parse(string);
+                    return date && date._;
+                } finally {
+                    d3_date = Date;
+                }
+            };
+            format.toString = local.toString;
+            return format;
+        };
+        d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti;
+        var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths);
+        locale_periods.forEach(function(p, i) {
+            d3_time_periodLookup.set(p.toLowerCase(), i);
+        });
+        var d3_time_formats = {
+            a: function(d) {
+                return locale_shortDays[d.getDay()];
+            },
+            A: function(d) {
+                return locale_days[d.getDay()];
+            },
+            b: function(d) {
+                return locale_shortMonths[d.getMonth()];
+            },
+            B: function(d) {
+                return locale_months[d.getMonth()];
+            },
+            c: d3_time_format(locale_dateTime),
+            d: function(d, p) {
+                return d3_time_formatPad(d.getDate(), p, 2);
+            },
+            e: function(d, p) {
+                return d3_time_formatPad(d.getDate(), p, 2);
+            },
+            H: function(d, p) {
+                return d3_time_formatPad(d.getHours(), p, 2);
+            },
+            I: function(d, p) {
+                return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
+            },
+            j: function(d, p) {
+                return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3);
+            },
+            L: function(d, p) {
+                return d3_time_formatPad(d.getMilliseconds(), p, 3);
+            },
+            m: function(d, p) {
+                return d3_time_formatPad(d.getMonth() + 1, p, 2);
+            },
+            M: function(d, p) {
+                return d3_time_formatPad(d.getMinutes(), p, 2);
+            },
+            p: function(d) {
+                return locale_periods[+(d.getHours() >= 12)];
+            },
+            S: function(d, p) {
+                return d3_time_formatPad(d.getSeconds(), p, 2);
+            },
+            U: function(d, p) {
+                return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2);
+            },
+            w: function(d) {
+                return d.getDay();
+            },
+            W: function(d, p) {
+                return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2);
+            },
+            x: d3_time_format(locale_date),
+            X: d3_time_format(locale_time),
+            y: function(d, p) {
+                return d3_time_formatPad(d.getFullYear() % 100, p, 2);
+            },
+            Y: function(d, p) {
+                return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
+            },
+            Z: d3_time_zone,
+            "%": function() {
+                return "%";
+            }
+        };
+        var d3_time_parsers = {
+            a: d3_time_parseWeekdayAbbrev,
+            A: d3_time_parseWeekday,
+            b: d3_time_parseMonthAbbrev,
+            B: d3_time_parseMonth,
+            c: d3_time_parseLocaleFull,
+            d: d3_time_parseDay,
+            e: d3_time_parseDay,
+            H: d3_time_parseHour24,
+            I: d3_time_parseHour24,
+            j: d3_time_parseDayOfYear,
+            L: d3_time_parseMilliseconds,
+            m: d3_time_parseMonthNumber,
+            M: d3_time_parseMinutes,
+            p: d3_time_parseAmPm,
+            S: d3_time_parseSeconds,
+            U: d3_time_parseWeekNumberSunday,
+            w: d3_time_parseWeekdayNumber,
+            W: d3_time_parseWeekNumberMonday,
+            x: d3_time_parseLocaleDate,
+            X: d3_time_parseLocaleTime,
+            y: d3_time_parseYear,
+            Y: d3_time_parseFullYear,
+            Z: d3_time_parseZone,
+            "%": d3_time_parseLiteralPercent
+        };
+        function d3_time_parseWeekdayAbbrev(date, string, i) {
+            d3_time_dayAbbrevRe.lastIndex = 0;
+            var n = d3_time_dayAbbrevRe.exec(string.slice(i));
+            return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+        }
+        function d3_time_parseWeekday(date, string, i) {
+            d3_time_dayRe.lastIndex = 0;
+            var n = d3_time_dayRe.exec(string.slice(i));
+            return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+        }
+        function d3_time_parseMonthAbbrev(date, string, i) {
+            d3_time_monthAbbrevRe.lastIndex = 0;
+            var n = d3_time_monthAbbrevRe.exec(string.slice(i));
+            return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+        }
+        function d3_time_parseMonth(date, string, i) {
+            d3_time_monthRe.lastIndex = 0;
+            var n = d3_time_monthRe.exec(string.slice(i));
+            return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+        }
+        function d3_time_parseLocaleFull(date, string, i) {
+            return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
+        }
+        function d3_time_parseLocaleDate(date, string, i) {
+            return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
+        }
+        function d3_time_parseLocaleTime(date, string, i) {
+            return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
+        }
+        function d3_time_parseAmPm(date, string, i) {
+            var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase());
+            return n == null ? -1 : (date.p = n, i);
+        }
+        return d3_time_format;
+    }
+    var d3_time_formatPads = {
+        "-": "",
+        _: " ",
+        "0": "0"
+    }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/;
+    function d3_time_formatPad(value, fill, width) {
+        var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
+        return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
+    }
+    function d3_time_formatRe(names) {
+        return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
+    }
+    function d3_time_formatLookup(names) {
+        var map = new d3_Map(), i = -1, n = names.length;
+        while (++i < n) map.set(names[i].toLowerCase(), i);
+        return map;
+    }
+    function d3_time_parseWeekdayNumber(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 1));
+        return n ? (date.w = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseWeekNumberSunday(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i));
+        return n ? (date.U = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseWeekNumberMonday(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i));
+        return n ? (date.W = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseFullYear(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 4));
+        return n ? (date.y = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseYear(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 2));
+        return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
+    }
+    function d3_time_parseZone(date, string, i) {
+        return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string,
+        i + 5) : -1;
+    }
+    function d3_time_expandYear(d) {
+        return d + (d > 68 ? 1900 : 2e3);
+    }
+    function d3_time_parseMonthNumber(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 2));
+        return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
+    }
+    function d3_time_parseDay(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 2));
+        return n ? (date.d = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseDayOfYear(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 3));
+        return n ? (date.j = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseHour24(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 2));
+        return n ? (date.H = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseMinutes(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 2));
+        return n ? (date.M = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseSeconds(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 2));
+        return n ? (date.S = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_parseMilliseconds(date, string, i) {
+        d3_time_numberRe.lastIndex = 0;
+        var n = d3_time_numberRe.exec(string.slice(i, i + 3));
+        return n ? (date.L = +n[0], i + n[0].length) : -1;
+    }
+    function d3_time_zone(d) {
+        var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60;
+        return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
+    }
+    function d3_time_parseLiteralPercent(date, string, i) {
+        d3_time_percentRe.lastIndex = 0;
+        var n = d3_time_percentRe.exec(string.slice(i, i + 1));
+        return n ? i + n[0].length : -1;
+    }
+    function d3_time_formatMulti(formats) {
+        var n = formats.length, i = -1;
+        while (++i < n) formats[i][0] = this(formats[i][0]);
+        return function(date) {
+            var i = 0, f = formats[i];
+            while (!f[1](date)) f = formats[++i];
+            return f[0](date);
+        };
+    }
+    d3.locale = function(locale) {
+        return {
+            numberFormat: d3_locale_numberFormat(locale),
+            timeFormat: d3_locale_timeFormat(locale)
+        };
+    };
+    var d3_locale_enUS = d3.locale({
+        decimal: ".",
+        thousands: ",",
+        grouping: [ 3 ],
+        currency: [ "$", "" ],
+        dateTime: "%a %b %e %X %Y",
+        date: "%m/%d/%Y",
+        time: "%H:%M:%S",
+        periods: [ "AM", "PM" ],
+        days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
+        shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
+        months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
+        shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
+    });
+    d3.format = d3_locale_enUS.numberFormat;
+    d3.geo = {};
+    function d3_adder() {}
+    d3_adder.prototype = {
+        s: 0,
+        t: 0,
+        add: function(y) {
+            d3_adderSum(y, this.t, d3_adderTemp);
+            d3_adderSum(d3_adderTemp.s, this.s, this);
+            if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
+        },
+        reset: function() {
+            this.s = this.t = 0;
+        },
+        valueOf: function() {
+            return this.s;
+        }
+    };
+    var d3_adderTemp = new d3_adder();
+    function d3_adderSum(a, b, o) {
+        var x = o.s = a + b, bv = x - a, av = x - bv;
+        o.t = a - av + (b - bv);
+    }
+    d3.geo.stream = function(object, listener) {
+        if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
+            d3_geo_streamObjectType[object.type](object, listener);
+        } else {
+            d3_geo_streamGeometry(object, listener);
+        }
+    };
+    function d3_geo_streamGeometry(geometry, listener) {
+        if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
+            d3_geo_streamGeometryType[geometry.type](geometry, listener);
+        }
+    }
+    var d3_geo_streamObjectType = {
+        Feature: function(feature, listener) {
+            d3_geo_streamGeometry(feature.geometry, listener);
+        },
+        FeatureCollection: function(object, listener) {
+            var features = object.features, i = -1, n = features.length;
+            while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
+        }
+    };
+    var d3_geo_streamGeometryType = {
+        Sphere: function(object, listener) {
+            listener.sphere();
+        },
+        Point: function(object, listener) {
+            object = object.coordinates;
+            listener.point(object[0], object[1], object[2]);
+        },
+        MultiPoint: function(object, listener) {
+            var coordinates = object.coordinates, i = -1, n = coordinates.length;
+            while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
+        },
+        LineString: function(object, listener) {
+            d3_geo_streamLine(object.coordinates, listener, 0);
+        },
+        MultiLineString: function(object, listener) {
+            var coordinates = object.coordinates, i = -1, n = coordinates.length;
+            while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
+        },
+        Polygon: function(object, listener) {
+            d3_geo_streamPolygon(object.coordinates, listener);
+        },
+        MultiPolygon: function(object, listener) {
+            var coordinates = object.coordinates, i = -1, n = coordinates.length;
+            while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
+        },
+        GeometryCollection: function(object, listener) {
+            var geometries = object.geometries, i = -1, n = geometries.length;
+            while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
+        }
+    };
+    function d3_geo_streamLine(coordinates, listener, closed) {
+        var i = -1, n = coordinates.length - closed, coordinate;
+        listener.lineStart();
+        while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
+        listener.lineEnd();
+    }
+    function d3_geo_streamPolygon(coordinates, listener) {
+        var i = -1, n = coordinates.length;
+        listener.polygonStart();
+        while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
+        listener.polygonEnd();
+    }
+    d3.geo.area = function(object) {
+        d3_geo_areaSum = 0;
+        d3.geo.stream(object, d3_geo_area);
+        return d3_geo_areaSum;
+    };
+    var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
+    var d3_geo_area = {
+        sphere: function() {
+            d3_geo_areaSum += 4 * π;
+        },
+        point: d3_noop,
+        lineStart: d3_noop,
+        lineEnd: d3_noop,
+        polygonStart: function() {
+            d3_geo_areaRingSum.reset();
+            d3_geo_area.lineStart = d3_geo_areaRingStart;
+        },
+        polygonEnd: function() {
+            var area = 2 * d3_geo_areaRingSum;
+            d3_geo_areaSum += area < 0 ? 4 * π + area : area;
+            d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
+        }
+    };
+    function d3_geo_areaRingStart() {
+        var λ00, φ00, λ0, cosφ0, sinφ0;
+        d3_geo_area.point = function(λ, φ) {
+            d3_geo_area.point = nextPoint;
+            λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4),
+                sinφ0 = Math.sin(φ);
+        };
+        function nextPoint(λ, φ) {
+            λ *= d3_radians;
+            φ = φ * d3_radians / 2 + π / 4;
+            var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ);
+            d3_geo_areaRingSum.add(Math.atan2(v, u));
+            λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
+        }
+        d3_geo_area.lineEnd = function() {
+            nextPoint(λ00, φ00);
+        };
+    }
+    function d3_geo_cartesian(spherical) {
+        var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ);
+        return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ];
+    }
+    function d3_geo_cartesianDot(a, b) {
+        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+    }
+    function d3_geo_cartesianCross(a, b) {
+        return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ];
+    }
+    function d3_geo_cartesianAdd(a, b) {
+        a[0] += b[0];
+        a[1] += b[1];
+        a[2] += b[2];
+    }
+    function d3_geo_cartesianScale(vector, k) {
+        return [ vector[0] * k, vector[1] * k, vector[2] * k ];
+    }
+    function d3_geo_cartesianNormalize(d) {
+        var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
+        d[0] /= l;
+        d[1] /= l;
+        d[2] /= l;
+    }
+    function d3_geo_spherical(cartesian) {
+        return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
+    }
+    function d3_geo_sphericalEqual(a, b) {
+        return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
+    }
+    d3.geo.bounds = function() {
+        var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range;
+        var bound = {
+            point: point,
+            lineStart: lineStart,
+            lineEnd: lineEnd,
+            polygonStart: function() {
+                bound.point = ringPoint;
+                bound.lineStart = ringStart;
+                bound.lineEnd = ringEnd;
+                dλSum = 0;
+                d3_geo_area.polygonStart();
+            },
+            polygonEnd: function() {
+                d3_geo_area.polygonEnd();
+                bound.point = point;
+                bound.lineStart = lineStart;
+                bound.lineEnd = lineEnd;
+                if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90;
+                range[0] = λ0, range[1] = λ1;
+            }
+        };
+        function point(λ, φ) {
+            ranges.push(range = [ λ0 = λ, λ1 = λ ]);
+            if (φ < φ0) φ0 = φ;
+            if (φ > φ1) φ1 = φ;
+        }
+        function linePoint(λ, φ) {
+            var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]);
+            if (p0) {
+                var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
+                d3_geo_cartesianNormalize(inflection);
+                inflection = d3_geo_spherical(inflection);
+                var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180;
+                if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
+                    var φi = inflection[1] * d3_degrees;
+                    if (φi > φ1) φ1 = φi;
+                } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
+                    var φi = -inflection[1] * d3_degrees;
+                    if (φi < φ0) φ0 = φi;
+                } else {
+                    if (φ < φ0) φ0 = φ;
+                    if (φ > φ1) φ1 = φ;
+                }
+                if (antimeridian) {
+                    if (λ < λ_) {
+                        if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
+                    } else {
+                        if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
+                    }
+                } else {
+                    if (λ1 >= λ0) {
+                        if (λ < λ0) λ0 = λ;
+                        if (λ > λ1) λ1 = λ;
+                    } else {
+                        if (λ > λ_) {
+                            if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
+                        } else {
+                            if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
+                        }
+                    }
+                }
+            } else {
+                point(λ, φ);
+            }
+            p0 = p, λ_ = λ;
+        }
+        function lineStart() {
+            bound.point = linePoint;
+        }
+        function lineEnd() {
+            range[0] = λ0, range[1] = λ1;
+            bound.point = point;
+            p0 = null;
+        }
+        function ringPoint(λ, φ) {
+            if (p0) {
+                var dλ = λ - λ_;
+                dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
+            } else λ__ = λ, φ__ = φ;
+            d3_geo_area.point(λ, φ);
+            linePoint(λ, φ);
+        }
+        function ringStart() {
+            d3_geo_area.lineStart();
+        }
+        function ringEnd() {
+            ringPoint(λ__, φ__);
+            d3_geo_area.lineEnd();
+            if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
+            range[0] = λ0, range[1] = λ1;
+            p0 = null;
+        }
+        function angle(λ0, λ1) {
+            return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
+        }
+        function compareRanges(a, b) {
+            return a[0] - b[0];
+        }
+        function withinRange(x, range) {
+            return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
+        }
+        return function(feature) {
+            φ1 = λ1 = -(λ0 = φ0 = Infinity);
+            ranges = [];
+            d3.geo.stream(feature, bound);
+            var n = ranges.length;
+            if (n) {
+                ranges.sort(compareRanges);
+                for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
+                    b = ranges[i];
+                    if (withinRange(b[0], a) || withinRange(b[1], a)) {
+                        if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
+                        if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
+                    } else {
+                        merged.push(a = b);
+                    }
+                }
+                var best = -Infinity, dλ;
+                for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
+                    b = merged[i];
+                    if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
+                }
+            }
+            ranges = range = null;
+            return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ];
+        };
+    }();
+    d3.geo.centroid = function(object) {
+        d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
+        d3.geo.stream(object, d3_geo_centroid);
+        var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
+        if (m < ε2) {
+            x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
+            if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
+            m = x * x + y * y + z * z;
+            if (m < ε2) return [ NaN, NaN ];
+        }
+        return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
+    };
+    var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2;
+    var d3_geo_centroid = {
+        sphere: d3_noop,
+        point: d3_geo_centroidPoint,
+        lineStart: d3_geo_centroidLineStart,
+        lineEnd: d3_geo_centroidLineEnd,
+        polygonStart: function() {
+            d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
+        },
+        polygonEnd: function() {
+            d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
+        }
+    };
+    function d3_geo_centroidPoint(λ, φ) {
+        λ *= d3_radians;
+        var cosφ = Math.cos(φ *= d3_radians);
+        d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
+    }
+    function d3_geo_centroidPointXYZ(x, y, z) {
+        ++d3_geo_centroidW0;
+        d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
+        d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
+        d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
+    }
+    function d3_geo_centroidLineStart() {
+        var x0, y0, z0;
+        d3_geo_centroid.point = function(λ, φ) {
+            λ *= d3_radians;
+            var cosφ = Math.cos(φ *= d3_radians);
+            x0 = cosφ * Math.cos(λ);
+            y0 = cosφ * Math.sin(λ);
+            z0 = Math.sin(φ);
+            d3_geo_centroid.point = nextPoint;
+            d3_geo_centroidPointXYZ(x0, y0, z0);
+        };
+        function nextPoint(λ, φ) {
+            λ *= d3_radians;
+            var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
+            d3_geo_centroidW1 += w;
+            d3_geo_centroidX1 += w * (x0 + (x0 = x));
+            d3_geo_centroidY1 += w * (y0 + (y0 = y));
+            d3_geo_centroidZ1 += w * (z0 + (z0 = z));
+            d3_geo_centroidPointXYZ(x0, y0, z0);
+        }
+    }
+    function d3_geo_centroidLineEnd() {
+        d3_geo_centroid.point = d3_geo_centroidPoint;
+    }
+    function d3_geo_centroidRingStart() {
+        var λ00, φ00, x0, y0, z0;
+        d3_geo_centroid.point = function(λ, φ) {
+            λ00 = λ, φ00 = φ;
+            d3_geo_centroid.point = nextPoint;
+            λ *= d3_radians;
+            var cosφ = Math.cos(φ *= d3_radians);
+            x0 = cosφ * Math.cos(λ);
+            y0 = cosφ * Math.sin(λ);
+            z0 = Math.sin(φ);
+            d3_geo_centroidPointXYZ(x0, y0, z0);
+        };
+        d3_geo_centroid.lineEnd = function() {
+            nextPoint(λ00, φ00);
+            d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
+            d3_geo_centroid.point = d3_geo_centroidPoint;
+        };
+        function nextPoint(λ, φ) {
+            λ *= d3_radians;
+            var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u);
+            d3_geo_centroidX2 += v * cx;
+            d3_geo_centroidY2 += v * cy;
+            d3_geo_centroidZ2 += v * cz;
+            d3_geo_centroidW1 += w;
+            d3_geo_centroidX1 += w * (x0 + (x0 = x));
+            d3_geo_centroidY1 += w * (y0 + (y0 = y));
+            d3_geo_centroidZ1 += w * (z0 + (z0 = z));
+            d3_geo_centroidPointXYZ(x0, y0, z0);
+        }
+    }
+    function d3_geo_compose(a, b) {
+        function compose(x, y) {
+            return x = a(x, y), b(x[0], x[1]);
+        }
+        if (a.invert && b.invert) compose.invert = function(x, y) {
+            return x = b.invert(x, y), x && a.invert(x[0], x[1]);
+        };
+        return compose;
+    }
+    function d3_true() {
+        return true;
+    }
+    function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
+        var subject = [], clip = [];
+        segments.forEach(function(segment) {
+            if ((n = segment.length - 1) <= 0) return;
+            var n, p0 = segment[0], p1 = segment[n];
+            if (d3_geo_sphericalEqual(p0, p1)) {
+                listener.lineStart();
+                for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
+                listener.lineEnd();
+                return;
+            }
+            var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
+            a.o = b;
+            subject.push(a);
+            clip.push(b);
+            a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
+            b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
+            a.o = b;
+            subject.push(a);
+            clip.push(b);
+        });
+        clip.sort(compare);
+        d3_geo_clipPolygonLinkCircular(subject);
+        d3_geo_clipPolygonLinkCircular(clip);
+        if (!subject.length) return;
+        for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
+            clip[i].e = entry = !entry;
+        }
+        var start = subject[0], points, point;
+        while (1) {
+            var current = start, isSubject = true;
+            while (current.v) if ((current = current.n) === start) return;
+            points = current.z;
+            listener.lineStart();
+            do {
+                current.v = current.o.v = true;
+                if (current.e) {
+                    if (isSubject) {
+                        for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
+                    } else {
+                        interpolate(current.x, current.n.x, 1, listener);
+                    }
+                    current = current.n;
+                } else {
+                    if (isSubject) {
+                        points = current.p.z;
+                        for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
+                    } else {
+                        interpolate(current.x, current.p.x, -1, listener);
+                    }
+                    current = current.p;
+                }
+                current = current.o;
+                points = current.z;
+                isSubject = !isSubject;
+            } while (!current.v);
+            listener.lineEnd();
+        }
+    }
+    function d3_geo_clipPolygonLinkCircular(array) {
+        if (!(n = array.length)) return;
+        var n, i = 0, a = array[0], b;
+        while (++i < n) {
+            a.n = b = array[i];
+            b.p = a;
+            a = b;
+        }
+        a.n = b = array[0];
+        b.p = a;
+    }
+    function d3_geo_clipPolygonIntersection(point, points, other, entry) {
+        this.x = point;
+        this.z = points;
+        this.o = other;
+        this.e = entry;
+        this.v = false;
+        this.n = this.p = null;
+    }
+    function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
+        return function(rotate, listener) {
+            var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
+            var clip = {
+                point: point,
+                lineStart: lineStart,
+                lineEnd: lineEnd,
+                polygonStart: function() {
+                    clip.point = pointRing;
+                    clip.lineStart = ringStart;
+                    clip.lineEnd = ringEnd;
+                    segments = [];
+                    polygon = [];
+                },
+                polygonEnd: function() {
+                    clip.point = point;
+                    clip.lineStart = lineStart;
+                    clip.lineEnd = lineEnd;
+                    segments = d3.merge(segments);
+                    var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
+                    if (segments.length) {
+                        if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
+                        d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
+                    } else if (clipStartInside) {
+                        if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
+                        listener.lineStart();
+                        interpolate(null, null, 1, listener);
+                        listener.lineEnd();
+                    }
+                    if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
+                    segments = polygon = null;
+                },
+                sphere: function() {
+                    listener.polygonStart();
+                    listener.lineStart();
+                    interpolate(null, null, 1, listener);
+                    listener.lineEnd();
+                    listener.polygonEnd();
+                }
+            };
+            function point(λ, φ) {
+                var point = rotate(λ, φ);
+                if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
+            }
+            function pointLine(λ, φ) {
+                var point = rotate(λ, φ);
+                line.point(point[0], point[1]);
+            }
+            function lineStart() {
+                clip.point = pointLine;
+                line.lineStart();
+            }
+            function lineEnd() {
+                clip.point = point;
+                line.lineEnd();
+            }
+            var segments;
+            var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring;
+            function pointRing(λ, φ) {
+                ring.push([ λ, φ ]);
+                var point = rotate(λ, φ);
+                ringListener.point(point[0], point[1]);
+            }
+            function ringStart() {
+                ringListener.lineStart();
+                ring = [];
+            }
+            function ringEnd() {
+                pointRing(ring[0][0], ring[0][1]);
+                ringListener.lineEnd();
+                var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
+                ring.pop();
+                polygon.push(ring);
+                ring = null;
+                if (!n) return;
+                if (clean & 1) {
+                    segment = ringSegments[0];
+                    var n = segment.length - 1, i = -1, point;
+                    if (n > 0) {
+                        if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
+                        listener.lineStart();
+                        while (++i < n) listener.point((point = segment[i])[0], point[1]);
+                        listener.lineEnd();
+                    }
+                    return;
+                }
+                if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
+                segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
+            }
+            return clip;
+        };
+    }
+    function d3_geo_clipSegmentLength1(segment) {
+        return segment.length > 1;
+    }
+    function d3_geo_clipBufferListener() {
+        var lines = [], line;
+        return {
+            lineStart: function() {
+                lines.push(line = []);
+            },
+            point: function(λ, φ) {
+                line.push([ λ, φ ]);
+            },
+            lineEnd: d3_noop,
+            buffer: function() {
+                var buffer = lines;
+                lines = [];
+                line = null;
+                return buffer;
+            },
+            rejoin: function() {
+                if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
+            }
+        };
+    }
+    function d3_geo_clipSort(a, b) {
+        return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
+    }
+    var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
+    function d3_geo_clipAntimeridianLine(listener) {
+        var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
+        return {
+            lineStart: function() {
+                listener.lineStart();
+                clean = 1;
+            },
+            point: function(λ1, φ1) {
+                var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0);
+                if (abs(dλ - π) < ε) {
+                    listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
+                    listener.point(sλ0, φ0);
+                    listener.lineEnd();
+                    listener.lineStart();
+                    listener.point(sλ1, φ0);
+                    listener.point(λ1, φ0);
+                    clean = 0;
+                } else if (sλ0 !== sλ1 && dλ >= π) {
+                    if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
+                    if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
+                    φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
+                    listener.point(sλ0, φ0);
+                    listener.lineEnd();
+                    listener.lineStart();
+                    listener.point(sλ1, φ0);
+                    clean = 0;
+                }
+                listener.point(λ0 = λ1, φ0 = φ1);
+                sλ0 = sλ1;
+            },
+            lineEnd: function() {
+                listener.lineEnd();
+                λ0 = φ0 = NaN;
+            },
+            clean: function() {
+                return 2 - clean;
+            }
+        };
+    }
+    function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
+        var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
+        return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2;
+    }
+    function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
+        var φ;
+        if (from == null) {
+            φ = direction * halfπ;
+            listener.point(-π, φ);
+            listener.point(0, φ);
+            listener.point(π, φ);
+            listener.point(π, 0);
+            listener.point(π, -φ);
+            listener.point(0, -φ);
+            listener.point(-π, -φ);
+            listener.point(-π, 0);
+            listener.point(-π, φ);
+        } else if (abs(from[0] - to[0]) > ε) {
+            var s = from[0] < to[0] ? π : -π;
+            φ = direction * s / 2;
+            listener.point(-s, φ);
+            listener.point(0, φ);
+            listener.point(s, φ);
+        } else {
+            listener.point(to[0], to[1]);
+        }
+    }
+    function d3_geo_pointInPolygon(point, polygon) {
+        var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
+        d3_geo_areaRingSum.reset();
+        for (var i = 0, n = polygon.length; i < n; ++i) {
+            var ring = polygon[i], m = ring.length;
+            if (!m) continue;
+            var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
+            while (true) {
+                if (j === m) j = 0;
+                point = ring[j];
+                var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ;
+                d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
+                polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
+                if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
+                    var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
+                    d3_geo_cartesianNormalize(arc);
+                    var intersection = d3_geo_cartesianCross(meridianNormal, arc);
+                    d3_geo_cartesianNormalize(intersection);
+                    var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
+                    if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
+                        winding += antimeridian ^ dλ >= 0 ? 1 : -1;
+                    }
+                }
+                if (!j++) break;
+                λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
+            }
+        }
+        return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1;
+    }
+    function d3_geo_clipCircle(radius) {
+        var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
+        return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]);
+        function visible(λ, φ) {
+            return Math.cos(λ) * Math.cos(φ) > cr;
+        }
+        function clipLine(listener) {
+            var point0, c0, v0, v00, clean;
+            return {
+                lineStart: function() {
+                    v00 = v0 = false;
+                    clean = 1;
+                },
+                point: function(λ, φ) {
+                    var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
+                    if (!point0 && (v00 = v0 = v)) listener.lineStart();
+                    if (v !== v0) {
+                        point2 = intersect(point0, point1);
+                        if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
+                            point1[0] += ε;
+                            point1[1] += ε;
+                            v = visible(point1[0], point1[1]);
+                        }
+                    }
+                    if (v !== v0) {
+                        clean = 0;
+                        if (v) {
+                            listener.lineStart();
+                            point2 = intersect(point1, point0);
+                            listener.point(point2[0], point2[1]);
+                        } else {
+                            point2 = intersect(point0, point1);
+                            listener.point(point2[0], point2[1]);
+                            listener.lineEnd();
+                        }
+                        point0 = point2;
+                    } else if (notHemisphere && point0 && smallRadius ^ v) {
+                        var t;
+                        if (!(c & c0) && (t = intersect(point1, point0, true))) {
+                            clean = 0;
+                            if (smallRadius) {
+                                listener.lineStart();
+                                listener.point(t[0][0], t[0][1]);
+                                listener.point(t[1][0], t[1][1]);
+                                listener.lineEnd();
+                            } else {
+                                listener.point(t[1][0], t[1][1]);
+                                listener.lineEnd();
+                                listener.lineStart();
+                                listener.point(t[0][0], t[0][1]);
+                            }
+                        }
+                    }
+                    if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
+                        listener.point(point1[0], point1[1]);
+                    }
+                    point0 = point1, v0 = v, c0 = c;
+                },
+                lineEnd: function() {
+                    if (v0) listener.lineEnd();
+                    point0 = null;
+                },
+                clean: function() {
+                    return clean | (v00 && v0) << 1;
+                }
+            };
+        }
+        function intersect(a, b, two) {
+            var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
+            var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
+            if (!determinant) return !two && a;
+            var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2);
+            d3_geo_cartesianAdd(A, B);
+            var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
+            if (t2 < 0) return;
+            var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
+            d3_geo_cartesianAdd(q, A);
+            q = d3_geo_spherical(q);
+            if (!two) return q;
+            var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z;
+            if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
+            var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε;
+            if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
+            if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
+                var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
+                d3_geo_cartesianAdd(q1, A);
+                return [ q, d3_geo_spherical(q1) ];
+            }
+        }
+        function code(λ, φ) {
+            var r = smallRadius ? radius : π - radius, code = 0;
+            if (λ < -r) code |= 1; else if (λ > r) code |= 2;
+            if (φ < -r) code |= 4; else if (φ > r) code |= 8;
+            return code;
+        }
+    }
+    function d3_geom_clipLine(x0, y0, x1, y1) {
+        return function(line) {
+            var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r;
+            r = x0 - ax;
+            if (!dx && r > 0) return;
+            r /= dx;
+            if (dx < 0) {
+                if (r < t0) return;
+                if (r < t1) t1 = r;
+            } else if (dx > 0) {
+                if (r > t1) return;
+                if (r > t0) t0 = r;
+            }
+            r = x1 - ax;
+            if (!dx && r < 0) return;
+            r /= dx;
+            if (dx < 0) {
+                if (r > t1) return;
+                if (r > t0) t0 = r;
+            } else if (dx > 0) {
+                if (r < t0) return;
+                if (r < t1) t1 = r;
+            }
+            r = y0 - ay;
+            if (!dy && r > 0) return;
+            r /= dy;
+            if (dy < 0) {
+                if (r < t0) return;
+                if (r < t1) t1 = r;
+            } else if (dy > 0) {
+                if (r > t1) return;
+                if (r > t0) t0 = r;
+            }
+            r = y1 - ay;
+            if (!dy && r < 0) return;
+            r /= dy;
+            if (dy < 0) {
+                if (r > t1) return;
+                if (r > t0) t0 = r;
+            } else if (dy > 0) {
+                if (r < t0) return;
+                if (r < t1) t1 = r;
+            }
+            if (t0 > 0) line.a = {
+                x: ax + t0 * dx,
+                y: ay + t0 * dy
+            };
+            if (t1 < 1) line.b = {
+                x: ax + t1 * dx,
+                y: ay + t1 * dy
+            };
+            return line;
+        };
+    }
+    var d3_geo_clipExtentMAX = 1e9;
+    d3.geo.clipExtent = function() {
+        var x0, y0, x1, y1, stream, clip, clipExtent = {
+            stream: function(output) {
+                if (stream) stream.valid = false;
+                stream = clip(output);
+                stream.valid = true;
+                return stream;
+            },
+            extent: function(_) {
+                if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
+                clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
+                if (stream) stream.valid = false, stream = null;
+                return clipExtent;
+            }
+        };
+        return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]);
+    };
+    function d3_geo_clipExtent(x0, y0, x1, y1) {
+        return function(listener) {
+            var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring;
+            var clip = {
+                point: point,
+                lineStart: lineStart,
+                lineEnd: lineEnd,
+                polygonStart: function() {
+                    listener = bufferListener;
+                    segments = [];
+                    polygon = [];
+                    clean = true;
+                },
+                polygonEnd: function() {
+                    listener = listener_;
+                    segments = d3.merge(segments);
+                    var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length;
+                    if (inside || visible) {
+                        listener.polygonStart();
+                        if (inside) {
+                            listener.lineStart();
+                            interpolate(null, null, 1, listener);
+                            listener.lineEnd();
+                        }
+                        if (visible) {
+                            d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
+                        }
+                        listener.polygonEnd();
+                    }
+                    segments = polygon = ring = null;
+                }
+            };
+            function insidePolygon(p) {
+                var wn = 0, n = polygon.length, y = p[1];
+                for (var i = 0; i < n; ++i) {
+                    for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
+                        b = v[j];
+                        if (a[1] <= y) {
+                            if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn;
+                        } else {
+                            if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
+                        }
+                        a = b;
+                    }
+                }
+                return wn !== 0;
+            }
+            function interpolate(from, to, direction, listener) {
+                var a = 0, a1 = 0;
+                if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
+                    do {
+                        listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
+                    } while ((a = (a + direction + 4) % 4) !== a1);
+                } else {
+                    listener.point(to[0], to[1]);
+                }
+            }
+            function pointVisible(x, y) {
+                return x0 <= x && x <= x1 && y0 <= y && y <= y1;
+            }
+            function point(x, y) {
+                if (pointVisible(x, y)) listener.point(x, y);
+            }
+            var x__, y__, v__, x_, y_, v_, first, clean;
+            function lineStart() {
+                clip.point = linePoint;
+                if (polygon) polygon.push(ring = []);
+                first = true;
+                v_ = false;
+                x_ = y_ = NaN;
+            }
+            function lineEnd() {
+                if (segments) {
+                    linePoint(x__, y__);
+                    if (v__ && v_) bufferListener.rejoin();
+                    segments.push(bufferListener.buffer());
+                }
+                clip.point = point;
+                if (v_) listener.lineEnd();
+            }
+            function linePoint(x, y) {
+                x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
+                y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
+                var v = pointVisible(x, y);
+                if (polygon) ring.push([ x, y ]);
+                if (first) {
+                    x__ = x, y__ = y, v__ = v;
+                    first = false;
+                    if (v) {
+                        listener.lineStart();
+                        listener.point(x, y);
+                    }
+                } else {
+                    if (v && v_) listener.point(x, y); else {
+                        var l = {
+                            a: {
+                                x: x_,
+                                y: y_
+                            },
+                            b: {
+                                x: x,
+                                y: y
+                            }
+                        };
+                        if (clipLine(l)) {
+                            if (!v_) {
+                                listener.lineStart();
+                                listener.point(l.a.x, l.a.y);
+                            }
+                            listener.point(l.b.x, l.b.y);
+                            if (!v) listener.lineEnd();
+                            clean = false;
+                        } else if (v) {
+                            listener.lineStart();
+                            listener.point(x, y);
+                            clean = false;
+                        }
+                    }
+                }
+                x_ = x, y_ = y, v_ = v;
+            }
+            return clip;
+        };
+        function corner(p, direction) {
+            return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2;
+        }
+        function compare(a, b) {
+            return comparePoints(a.x, b.x);
+        }
+        function comparePoints(a, b) {
+            var ca = corner(a, 1), cb = corner(b, 1);
+            return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
+        }
+    }
+    function d3_geo_conic(projectAt) {
+        var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1);
+        p.parallels = function(_) {
+            if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ];
+            return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
+        };
+        return p;
+    }
+    function d3_geo_conicEqualArea(φ0, φ1) {
+        var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n;
+        function forward(λ, φ) {
+            var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
+            return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ];
+        }
+        forward.invert = function(x, y) {
+            var ρ0_y = ρ0 - y;
+            return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ];
+        };
+        return forward;
+    }
+    (d3.geo.conicEqualArea = function() {
+        return d3_geo_conic(d3_geo_conicEqualArea);
+    }).raw = d3_geo_conicEqualArea;
+    d3.geo.albers = function() {
+        return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
+    };
+    d3.geo.albersUsa = function() {
+        var lower48 = d3.geo.albers();
+        var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
+        var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
+        var point, pointStream = {
+            point: function(x, y) {
+                point = [ x, y ];
+            }
+        }, lower48Point, alaskaPoint, hawaiiPoint;
+        function albersUsa(coordinates) {
+            var x = coordinates[0], y = coordinates[1];
+            point = null;
+            (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
+            return point;
+        }
+        albersUsa.invert = function(coordinates) {
+            var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
+            return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
+        };
+        albersUsa.stream = function(stream) {
+            var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
+            return {
+                point: function(x, y) {
+                    lower48Stream.point(x, y);
+                    alaskaStream.point(x, y);
+                    hawaiiStream.point(x, y);
+                },
+                sphere: function() {
+                    lower48Stream.sphere();
+                    alaskaStream.sphere();
+                    hawaiiStream.sphere();
+                },
+                lineStart: function() {
+                    lower48Stream.lineStart();
+                    alaskaStream.lineStart();
+                    hawaiiStream.lineStart();
+                },
+                lineEnd: function() {
+                    lower48Stream.lineEnd();
+                    alaskaStream.lineEnd();
+                    hawaiiStream.lineEnd();
+                },
+                polygonStart: function() {
+                    lower48Stream.polygonStart();
+                    alaskaStream.polygonStart();
+                    hawaiiStream.polygonStart();
+                },
+                polygonEnd: function() {
+                    lower48Stream.polygonEnd();
+                    alaskaStream.polygonEnd();
+                    hawaiiStream.polygonEnd();
+                }
+            };
+        };
+        albersUsa.precision = function(_) {
+            if (!arguments.length) return lower48.precision();
+            lower48.precision(_);
+            alaska.precision(_);
+            hawaii.precision(_);
+            return albersUsa;
+        };
+        albersUsa.scale = function(_) {
+            if (!arguments.length) return lower48.scale();
+            lower48.scale(_);
+            alaska.scale(_ * .35);
+            hawaii.scale(_);
+            return albersUsa.translate(lower48.translate());
+        };
+        albersUsa.translate = function(_) {
+            if (!arguments.length) return lower48.translate();
+            var k = lower48.scale(), x = +_[0], y = +_[1];
+            lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
+            alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
+            hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
+            return albersUsa;
+        };
+        return albersUsa.scale(1070);
+    };
+    var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
+        point: d3_noop,
+        lineStart: d3_noop,
+        lineEnd: d3_noop,
+        polygonStart: function() {
+            d3_geo_pathAreaPolygon = 0;
+            d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
+        },
+        polygonEnd: function() {
+            d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
+            d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
+        }
+    };
+    function d3_geo_pathAreaRingStart() {
+        var x00, y00, x0, y0;
+        d3_geo_pathArea.point = function(x, y) {
+            d3_geo_pathArea.point = nextPoint;
+            x00 = x0 = x, y00 = y0 = y;
+        };
+        function nextPoint(x, y) {
+            d3_geo_pathAreaPolygon += y0 * x - x0 * y;
+            x0 = x, y0 = y;
+        }
+        d3_geo_pathArea.lineEnd = function() {
+            nextPoint(x00, y00);
+        };
+    }
+    var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
+    var d3_geo_pathBounds = {
+        point: d3_geo_pathBoundsPoint,
+        lineStart: d3_noop,
+        lineEnd: d3_noop,
+        polygonStart: d3_noop,
+        polygonEnd: d3_noop
+    };
+    function d3_geo_pathBoundsPoint(x, y) {
+        if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
+        if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
+        if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
+        if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
+    }
+    function d3_geo_pathBuffer() {
+        var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
+        var stream = {
+            point: point,
+            lineStart: function() {
+                stream.point = pointLineStart;
+            },
+            lineEnd: lineEnd,
+            polygonStart: function() {
+                stream.lineEnd = lineEndPolygon;
+            },
+            polygonEnd: function() {
+                stream.lineEnd = lineEnd;
+                stream.point = point;
+            },
+            pointRadius: function(_) {
+                pointCircle = d3_geo_pathBufferCircle(_);
+                return stream;
+            },
+            result: function() {
+                if (buffer.length) {
+                    var result = buffer.join("");
+                    buffer = [];
+                    return result;
+                }
+            }
+        };
+        function point(x, y) {
+            buffer.push("M", x, ",", y, pointCircle);
+        }
+        function pointLineStart(x, y) {
+            buffer.push("M", x, ",", y);
+            stream.point = pointLine;
+        }
+        function pointLine(x, y) {
+            buffer.push("L", x, ",", y);
+        }
+        function lineEnd() {
+            stream.point = point;
+        }
+        function lineEndPolygon() {
+            buffer.push("Z");
+        }
+        return stream;
+    }
+    function d3_geo_pathBufferCircle(radius) {
+        return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
+    }
+    var d3_geo_pathCentroid = {
+        point: d3_geo_pathCentroidPoint,
+        lineStart: d3_geo_pathCentroidLineStart,
+        lineEnd: d3_geo_pathCentroidLineEnd,
+        polygonStart: function() {
+            d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
+        },
+        polygonEnd: function() {
+            d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
+            d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
+            d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
+        }
+    };
+    function d3_geo_pathCentroidPoint(x, y) {
+        d3_geo_centroidX0 += x;
+        d3_geo_centroidY0 += y;
+        ++d3_geo_centroidZ0;
+    }
+    function d3_geo_pathCentroidLineStart() {
+        var x0, y0;
+        d3_geo_pathCentroid.point = function(x, y) {
+            d3_geo_pathCentroid.point = nextPoint;
+            d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+        };
+        function nextPoint(x, y) {
+            var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
+            d3_geo_centroidX1 += z * (x0 + x) / 2;
+            d3_geo_centroidY1 += z * (y0 + y) / 2;
+            d3_geo_centroidZ1 += z;
+            d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+        }
+    }
+    function d3_geo_pathCentroidLineEnd() {
+        d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
+    }
+    function d3_geo_pathCentroidRingStart() {
+        var x00, y00, x0, y0;
+        d3_geo_pathCentroid.point = function(x, y) {
+            d3_geo_pathCentroid.point = nextPoint;
+            d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
+        };
+        function nextPoint(x, y) {
+            var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
+            d3_geo_centroidX1 += z * (x0 + x) / 2;
+            d3_geo_centroidY1 += z * (y0 + y) / 2;
+            d3_geo_centroidZ1 += z;
+            z = y0 * x - x0 * y;
+            d3_geo_centroidX2 += z * (x0 + x);
+            d3_geo_centroidY2 += z * (y0 + y);
+            d3_geo_centroidZ2 += z * 3;
+            d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+        }
+        d3_geo_pathCentroid.lineEnd = function() {
+            nextPoint(x00, y00);
+        };
+    }
+    function d3_geo_pathContext(context) {
+        var pointRadius = 4.5;
+        var stream = {
+            point: point,
+            lineStart: function() {
+                stream.point = pointLineStart;
+            },
+            lineEnd: lineEnd,
+            polygonStart: function() {
+                stream.lineEnd = lineEndPolygon;
+            },
+            polygonEnd: function() {
+                stream.lineEnd = lineEnd;
+                stream.point = point;
+            },
+            pointRadius: function(_) {
+                pointRadius = _;
+                return stream;
+            },
+            result: d3_noop
+        };
+        function point(x, y) {
+            context.moveTo(x + pointRadius, y);
+            context.arc(x, y, pointRadius, 0, τ);
+        }
+        function pointLineStart(x, y) {
+            context.moveTo(x, y);
+            stream.point = pointLine;
+        }
+        function pointLine(x, y) {
+            context.lineTo(x, y);
+        }
+        function lineEnd() {
+            stream.point = point;
+        }
+        function lineEndPolygon() {
+            context.closePath();
+        }
+        return stream;
+    }
+    function d3_geo_resample(project) {
+        var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
+        function resample(stream) {
+            return (maxDepth ? resampleRecursive : resampleNone)(stream);
+        }
+        function resampleNone(stream) {
+            return d3_geo_transformPoint(stream, function(x, y) {
+                x = project(x, y);
+                stream.point(x[0], x[1]);
+            });
+        }
+        function resampleRecursive(stream) {
+            var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
+            var resample = {
+                point: point,
+                lineStart: lineStart,
+                lineEnd: lineEnd,
+                polygonStart: function() {
+                    stream.polygonStart();
+                    resample.lineStart = ringStart;
+                },
+                polygonEnd: function() {
+                    stream.polygonEnd();
+                    resample.lineStart = lineStart;
+                }
+            };
+            function point(x, y) {
+                x = project(x, y);
+                stream.point(x[0], x[1]);
+            }
+            function lineStart() {
+                x0 = NaN;
+                resample.point = linePoint;
+                stream.lineStart();
+            }
+            function linePoint(λ, φ) {
+                var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ);
+                resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
+                stream.point(x0, y0);
+            }
+            function lineEnd() {
+                resample.point = point;
+                stream.lineEnd();
+            }
+            function ringStart() {
+                lineStart();
+                resample.point = ringPoint;
+                resample.lineEnd = ringEnd;
+            }
+            function ringPoint(λ, φ) {
+                linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
+                resample.point = linePoint;
+            }
+            function ringEnd() {
+                resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
+                resample.lineEnd = lineEnd;
+                lineEnd();
+            }
+            return resample;
+        }
+        function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
+            var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
+            if (d2 > 4 * δ2 && depth--) {
+                var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
+                if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
+                    resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
+                    stream.point(x2, y2);
+                    resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
+                }
+            }
+        }
+        resample.precision = function(_) {
+            if (!arguments.length) return Math.sqrt(δ2);
+            maxDepth = (δ2 = _ * _) > 0 && 16;
+            return resample;
+        };
+        return resample;
+    }
+    d3.geo.path = function() {
+        var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
+        function path(object) {
+            if (object) {
+                if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
+                if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
+                d3.geo.stream(object, cacheStream);
+            }
+            return contextStream.result();
+        }
+        path.area = function(object) {
+            d3_geo_pathAreaSum = 0;
+            d3.geo.stream(object, projectStream(d3_geo_pathArea));
+            return d3_geo_pathAreaSum;
+        };
+        path.centroid = function(object) {
+            d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
+            d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
+            return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ];
+        };
+        path.bounds = function(object) {
+            d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
+            d3.geo.stream(object, projectStream(d3_geo_pathBounds));
+            return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
+        };
+        path.projection = function(_) {
+            if (!arguments.length) return projection;
+            projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
+            return reset();
+        };
+        path.context = function(_) {
+            if (!arguments.length) return context;
+            contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
+            if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
+            return reset();
+        };
+        path.pointRadius = function(_) {
+            if (!arguments.length) return pointRadius;
+            pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
+            return path;
+        };
+        function reset() {
+            cacheStream = null;
+            return path;
+        }
+        return path.projection(d3.geo.albersUsa()).context(null);
+    };
+    function d3_geo_pathProjectStream(project) {
+        var resample = d3_geo_resample(function(x, y) {
+            return project([ x * d3_degrees, y * d3_degrees ]);
+        });
+        return function(stream) {
+            return d3_geo_projectionRadians(resample(stream));
+        };
+    }
+    d3.geo.transform = function(methods) {
+        return {
+            stream: function(stream) {
+                var transform = new d3_geo_transform(stream);
+                for (var k in methods) transform[k] = methods[k];
+                return transform;
+            }
+        };
+    };
+    function d3_geo_transform(stream) {
+        this.stream = stream;
+    }
+    d3_geo_transform.prototype = {
+        point: function(x, y) {
+            this.stream.point(x, y);
+        },
+        sphere: function() {
+            this.stream.sphere();
+        },
+        lineStart: function() {
+            this.stream.lineStart();
+        },
+        lineEnd: function() {
+            this.stream.lineEnd();
+        },
+        polygonStart: function() {
+            this.stream.polygonStart();
+        },
+        polygonEnd: function() {
+            this.stream.polygonEnd();
+        }
+    };
+    function d3_geo_transformPoint(stream, point) {
+        return {
+            point: point,
+            sphere: function() {
+                stream.sphere();
+            },
+            lineStart: function() {
+                stream.lineStart();
+            },
+            lineEnd: function() {
+                stream.lineEnd();
+            },
+            polygonStart: function() {
+                stream.polygonStart();
+            },
+            polygonEnd: function() {
+                stream.polygonEnd();
+            }
+        };
+    }
+    d3.geo.projection = d3_geo_projection;
+    d3.geo.projectionMutator = d3_geo_projectionMutator;
+    function d3_geo_projection(project) {
+        return d3_geo_projectionMutator(function() {
+            return project;
+        })();
+    }
+    function d3_geo_projectionMutator(projectAt) {
+        var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
+            x = project(x, y);
+            return [ x[0] * k + δx, δy - x[1] * k ];
+        }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream;
+        function projection(point) {
+            point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
+            return [ point[0] * k + δx, δy - point[1] * k ];
+        }
+        function invert(point) {
+            point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
+            return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
+        }
+        projection.stream = function(output) {
+            if (stream) stream.valid = false;
+            stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
+            stream.valid = true;
+            return stream;
+        };
+        projection.clipAngle = function(_) {
+            if (!arguments.length) return clipAngle;
+            preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
+            return invalidate();
+        };
+        projection.clipExtent = function(_) {
+            if (!arguments.length) return clipExtent;
+            clipExtent = _;
+            postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
+            return invalidate();
+        };
+        projection.scale = function(_) {
+            if (!arguments.length) return k;
+            k = +_;
+            return reset();
+        };
+        projection.translate = function(_) {
+            if (!arguments.length) return [ x, y ];
+            x = +_[0];
+            y = +_[1];
+            return reset();
+        };
+        projection.center = function(_) {
+            if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ];
+            λ = _[0] % 360 * d3_radians;
+            φ = _[1] % 360 * d3_radians;
+            return reset();
+        };
+        projection.rotate = function(_) {
+            if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ];
+            δλ = _[0] % 360 * d3_radians;
+            δφ = _[1] % 360 * d3_radians;
+            δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
+            return reset();
+        };
+        d3.rebind(projection, projectResample, "precision");
+        function reset() {
+            projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
+            var center = project(λ, φ);
+            δx = x - center[0] * k;
+            δy = y + center[1] * k;
+            return invalidate();
+        }
+        function invalidate() {
+            if (stream) stream.valid = false, stream = null;
+            return projection;
+        }
+        return function() {
+            project = projectAt.apply(this, arguments);
+            projection.invert = project.invert && invert;
+            return reset();
+        };
+    }
+    function d3_geo_projectionRadians(stream) {
+        return d3_geo_transformPoint(stream, function(x, y) {
+            stream.point(x * d3_radians, y * d3_radians);
+        });
+    }
+    function d3_geo_equirectangular(λ, φ) {
+        return [ λ, φ ];
+    }
+    (d3.geo.equirectangular = function() {
+        return d3_geo_projection(d3_geo_equirectangular);
+    }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
+    d3.geo.rotation = function(rotate) {
+        rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
+        function forward(coordinates) {
+            coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
+            return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
+        }
+        forward.invert = function(coordinates) {
+            coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
+            return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
+        };
+        return forward;
+    };
+    function d3_geo_identityRotation(λ, φ) {
+        return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
+    }
+    d3_geo_identityRotation.invert = d3_geo_equirectangular;
+    function d3_geo_rotation(δλ, δφ, δγ) {
+        return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation;
+    }
+    function d3_geo_forwardRotationλ(δλ) {
+        return function(λ, φ) {
+            return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
+        };
+    }
+    function d3_geo_rotationλ(δλ) {
+        var rotation = d3_geo_forwardRotationλ(δλ);
+        rotation.invert = d3_geo_forwardRotationλ(-δλ);
+        return rotation;
+    }
+    function d3_geo_rotationφγ(δφ, δγ) {
+        var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ);
+        function rotation(λ, φ) {
+            var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ;
+            return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ];
+        }
+        rotation.invert = function(λ, φ) {
+            var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ;
+            return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ];
+        };
+        return rotation;
+    }
+    d3.geo.circle = function() {
+        var origin = [ 0, 0 ], angle, precision = 6, interpolate;
+        function circle() {
+            var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = [];
+            interpolate(null, null, 1, {
+                point: function(x, y) {
+                    ring.push(x = rotate(x, y));
+                    x[0] *= d3_degrees, x[1] *= d3_degrees;
+                }
+            });
+            return {
+                type: "Polygon",
+                coordinates: [ ring ]
+            };
+        }
+        circle.origin = function(x) {
+            if (!arguments.length) return origin;
+            origin = x;
+            return circle;
+        };
+        circle.angle = function(x) {
+            if (!arguments.length) return angle;
+            interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
+            return circle;
+        };
+        circle.precision = function(_) {
+            if (!arguments.length) return precision;
+            interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
+            return circle;
+        };
+        return circle.angle(90);
+    };
+    function d3_geo_circleInterpolate(radius, precision) {
+        var cr = Math.cos(radius), sr = Math.sin(radius);
+        return function(from, to, direction, listener) {
+            var step = direction * precision;
+            if (from != null) {
+                from = d3_geo_circleAngle(cr, from);
+                to = d3_geo_circleAngle(cr, to);
+                if (direction > 0 ? from < to : from > to) from += direction * τ;
+            } else {
+                from = radius + direction * τ;
+                to = radius - .5 * step;
+            }
+            for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
+                listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
+            }
+        };
+    }
+    function d3_geo_circleAngle(cr, point) {
+        var a = d3_geo_cartesian(point);
+        a[0] -= cr;
+        d3_geo_cartesianNormalize(a);
+        var angle = d3_acos(-a[1]);
+        return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
+    }
+    d3.geo.distance = function(a, b) {
+        var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t;
+        return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ);
+    };
+    d3.geo.graticule = function() {
+        var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
+        function graticule() {
+            return {
+                type: "MultiLineString",
+                coordinates: lines()
+            };
+        }
+        function lines() {
+            return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) {
+                return abs(x % DX) > ε;
+            }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
+                return abs(y % DY) > ε;
+            }).map(y));
+        }
+        graticule.lines = function() {
+            return lines().map(function(coordinates) {
+                return {
+                    type: "LineString",
+                    coordinates: coordinates
+                };
+            });
+        };
+        graticule.outline = function() {
+            return {
+                type: "Polygon",
+                coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
+            };
+        };
+        graticule.extent = function(_) {
+            if (!arguments.length) return graticule.minorExtent();
+            return graticule.majorExtent(_).minorExtent(_);
+        };
+        graticule.majorExtent = function(_) {
+            if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
+            X0 = +_[0][0], X1 = +_[1][0];
+            Y0 = +_[0][1], Y1 = +_[1][1];
+            if (X0 > X1) _ = X0, X0 = X1, X1 = _;
+            if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
+            return graticule.precision(precision);
+        };
+        graticule.minorExtent = function(_) {
+            if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
+            x0 = +_[0][0], x1 = +_[1][0];
+            y0 = +_[0][1], y1 = +_[1][1];
+            if (x0 > x1) _ = x0, x0 = x1, x1 = _;
+            if (y0 > y1) _ = y0, y0 = y1, y1 = _;
+            return graticule.precision(precision);
+        };
+        graticule.step = function(_) {
+            if (!arguments.length) return graticule.minorStep();
+            return graticule.majorStep(_).minorStep(_);
+        };
+        graticule.majorStep = function(_) {
+            if (!arguments.length) return [ DX, DY ];
+            DX = +_[0], DY = +_[1];
+            return graticule;
+        };
+        graticule.minorStep = function(_) {
+            if (!arguments.length) return [ dx, dy ];
+            dx = +_[0], dy = +_[1];
+            return graticule;
+        };
+        graticule.precision = function(_) {
+            if (!arguments.length) return precision;
+            precision = +_;
+            x = d3_geo_graticuleX(y0, y1, 90);
+            y = d3_geo_graticuleY(x0, x1, precision);
+            X = d3_geo_graticuleX(Y0, Y1, 90);
+            Y = d3_geo_graticuleY(X0, X1, precision);
+            return graticule;
+        };
+        return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
+    };
+    function d3_geo_graticuleX(y0, y1, dy) {
+        var y = d3.range(y0, y1 - ε, dy).concat(y1);
+        return function(x) {
+            return y.map(function(y) {
+                return [ x, y ];
+            });
+        };
+    }
+    function d3_geo_graticuleY(x0, x1, dx) {
+        var x = d3.range(x0, x1 - ε, dx).concat(x1);
+        return function(y) {
+            return x.map(function(x) {
+                return [ x, y ];
+            });
+        };
+    }
+    function d3_source(d) {
+        return d.source;
+    }
+    function d3_target(d) {
+        return d.target;
+    }
+    d3.geo.greatArc = function() {
+        var source = d3_source, source_, target = d3_target, target_;
+        function greatArc() {
+            return {
+                type: "LineString",
+                coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
+            };
+        }
+        greatArc.distance = function() {
+            return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
+        };
+        greatArc.source = function(_) {
+            if (!arguments.length) return source;
+            source = _, source_ = typeof _ === "function" ? null : _;
+            return greatArc;
+        };
+        greatArc.target = function(_) {
+            if (!arguments.length) return target;
+            target = _, target_ = typeof _ === "function" ? null : _;
+            return greatArc;
+        };
+        greatArc.precision = function() {
+            return arguments.length ? greatArc : 0;
+        };
+        return greatArc;
+    };
+    d3.geo.interpolate = function(source, target) {
+        return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
+    };
+    function d3_geo_interpolate(x0, y0, x1, y1) {
+        var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d);
+        var interpolate = d ? function(t) {
+            var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1;
+            return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
+        } : function() {
+            return [ x0 * d3_degrees, y0 * d3_degrees ];
+        };
+        interpolate.distance = d;
+        return interpolate;
+    }
+    d3.geo.length = function(object) {
+        d3_geo_lengthSum = 0;
+        d3.geo.stream(object, d3_geo_length);
+        return d3_geo_lengthSum;
+    };
+    var d3_geo_lengthSum;
+    var d3_geo_length = {
+        sphere: d3_noop,
+        point: d3_noop,
+        lineStart: d3_geo_lengthLineStart,
+        lineEnd: d3_noop,
+        polygonStart: d3_noop,
+        polygonEnd: d3_noop
+    };
+    function d3_geo_lengthLineStart() {
+        var λ0, sinφ0, cosφ0;
+        d3_geo_length.point = function(λ, φ) {
+            λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
+            d3_geo_length.point = nextPoint;
+        };
+        d3_geo_length.lineEnd = function() {
+            d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
+        };
+        function nextPoint(λ, φ) {
+            var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t);
+            d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
+            λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
+        }
+    }
+    function d3_geo_azimuthal(scale, angle) {
+        function azimuthal(λ, φ) {
+            var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ);
+            return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ];
+        }
+        azimuthal.invert = function(x, y) {
+            var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c);
+            return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ];
+        };
+        return azimuthal;
+    }
+    var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) {
+        return Math.sqrt(2 / (1 + cosλcosφ));
+    }, function(ρ) {
+        return 2 * Math.asin(ρ / 2);
+    });
+    (d3.geo.azimuthalEqualArea = function() {
+        return d3_geo_projection(d3_geo_azimuthalEqualArea);
+    }).raw = d3_geo_azimuthalEqualArea;
+    var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) {
+        var c = Math.acos(cosλcosφ);
+        return c && c / Math.sin(c);
+    }, d3_identity);
+    (d3.geo.azimuthalEquidistant = function() {
+        return d3_geo_projection(d3_geo_azimuthalEquidistant);
+    }).raw = d3_geo_azimuthalEquidistant;
+    function d3_geo_conicConformal(φ0, φ1) {
+        var cosφ0 = Math.cos(φ0), t = function(φ) {
+            return Math.tan(π / 4 + φ / 2);
+        }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n;
+        if (!n) return d3_geo_mercator;
+        function forward(λ, φ) {
+            if (F > 0) {
+                if (φ < -halfπ + ε) φ = -halfπ + ε;
+            } else {
+                if (φ > halfπ - ε) φ = halfπ - ε;
+            }
+            var ρ = F / Math.pow(t(φ), n);
+            return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ];
+        }
+        forward.invert = function(x, y) {
+            var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y);
+            return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ];
+        };
+        return forward;
+    }
+    (d3.geo.conicConformal = function() {
+        return d3_geo_conic(d3_geo_conicConformal);
+    }).raw = d3_geo_conicConformal;
+    function d3_geo_conicEquidistant(φ0, φ1) {
+        var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0;
+        if (abs(n) < ε) return d3_geo_equirectangular;
+        function forward(λ, φ) {
+            var ρ = G - φ;
+            return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ];
+        }
+        forward.invert = function(x, y) {
+            var ρ0_y = G - y;
+            return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ];
+        };
+        return forward;
+    }
+    (d3.geo.conicEquidistant = function() {
+        return d3_geo_conic(d3_geo_conicEquidistant);
+    }).raw = d3_geo_conicEquidistant;
+    var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) {
+        return 1 / cosλcosφ;
+    }, Math.atan);
+    (d3.geo.gnomonic = function() {
+        return d3_geo_projection(d3_geo_gnomonic);
+    }).raw = d3_geo_gnomonic;
+    function d3_geo_mercator(λ, φ) {
+        return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ];
+    }
+    d3_geo_mercator.invert = function(x, y) {
+        return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ];
+    };
+    function d3_geo_mercatorProjection(project) {
+        var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
+        m.scale = function() {
+            var v = scale.apply(m, arguments);
+            return v === m ? clipAuto ? m.clipExtent(null) : m : v;
+        };
+        m.translate = function() {
+            var v = translate.apply(m, arguments);
+            return v === m ? clipAuto ? m.clipExtent(null) : m : v;
+        };
+        m.clipExtent = function(_) {
+            var v = clipExtent.apply(m, arguments);
+            if (v === m) {
+                if (clipAuto = _ == null) {
+                    var k = π * scale(), t = translate();
+                    clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
+                }
+            } else if (clipAuto) {
+                v = null;
+            }
+            return v;
+        };
+        return m.clipExtent(null);
+    }
+    (d3.geo.mercator = function() {
+        return d3_geo_mercatorProjection(d3_geo_mercator);
+    }).raw = d3_geo_mercator;
+    var d3_geo_orthographic = d3_geo_azimuthal(function() {
+        return 1;
+    }, Math.asin);
+    (d3.geo.orthographic = function() {
+        return d3_geo_projection(d3_geo_orthographic);
+    }).raw = d3_geo_orthographic;
+    var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) {
+        return 1 / (1 + cosλcosφ);
+    }, function(ρ) {
+        return 2 * Math.atan(ρ);
+    });
+    (d3.geo.stereographic = function() {
+        return d3_geo_projection(d3_geo_stereographic);
+    }).raw = d3_geo_stereographic;
+    function d3_geo_transverseMercator(λ, φ) {
+        return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ];
+    }
+    d3_geo_transverseMercator.invert = function(x, y) {
+        return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ];
+    };
+    (d3.geo.transverseMercator = function() {
+        var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate;
+        projection.center = function(_) {
+            return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]);
+        };
+        projection.rotate = function(_) {
+            return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(),
+                [ _[0], _[1], _[2] - 90 ]);
+        };
+        return rotate([ 0, 0, 90 ]);
+    }).raw = d3_geo_transverseMercator;
+    d3.geom = {};
+    function d3_geom_pointX(d) {
+        return d[0];
+    }
+    function d3_geom_pointY(d) {
+        return d[1];
+    }
+    d3.geom.hull = function(vertices) {
+        var x = d3_geom_pointX, y = d3_geom_pointY;
+        if (arguments.length) return hull(vertices);
+        function hull(data) {
+            if (data.length < 3) return [];
+            var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = [];
+            for (i = 0; i < n; i++) {
+                points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]);
+            }
+            points.sort(d3_geom_hullOrder);
+            for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]);
+            var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints);
+            var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = [];
+            for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
+            for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
+            return polygon;
+        }
+        hull.x = function(_) {
+            return arguments.length ? (x = _, hull) : x;
+        };
+        hull.y = function(_) {
+            return arguments.length ? (y = _, hull) : y;
+        };
+        return hull;
+    };
+    function d3_geom_hullUpper(points) {
+        var n = points.length, hull = [ 0, 1 ], hs = 2;
+        for (var i = 2; i < n; i++) {
+            while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs;
+            hull[hs++] = i;
+        }
+        return hull.slice(0, hs);
+    }
+    function d3_geom_hullOrder(a, b) {
+        return a[0] - b[0] || a[1] - b[1];
+    }
+    d3.geom.polygon = function(coordinates) {
+        d3_subclass(coordinates, d3_geom_polygonPrototype);
+        return coordinates;
+    };
+    var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
+    d3_geom_polygonPrototype.area = function() {
+        var i = -1, n = this.length, a, b = this[n - 1], area = 0;
+        while (++i < n) {
+            a = b;
+            b = this[i];
+            area += a[1] * b[0] - a[0] * b[1];
+        }
+        return area * .5;
+    };
+    d3_geom_polygonPrototype.centroid = function(k) {
+        var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
+        if (!arguments.length) k = -1 / (6 * this.area());
+        while (++i < n) {
+            a = b;
+            b = this[i];
+            c = a[0] * b[1] - b[0] * a[1];
+            x += (a[0] + b[0]) * c;
+            y += (a[1] + b[1]) * c;
+        }
+        return [ x * k, y * k ];
+    };
+    d3_geom_polygonPrototype.clip = function(subject) {
+        var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d;
+        while (++i < n) {
+            input = subject.slice();
+            subject.length = 0;
+            b = this[i];
+            c = input[(m = input.length - closed) - 1];
+            j = -1;
+            while (++j < m) {
+                d = input[j];
+                if (d3_geom_polygonInside(d, a, b)) {
+                    if (!d3_geom_polygonInside(c, a, b)) {
+                        subject.push(d3_geom_polygonIntersect(c, d, a, b));
+                    }
+                    subject.push(d);
+                } else if (d3_geom_polygonInside(c, a, b)) {
+                    subject.push(d3_geom_polygonIntersect(c, d, a, b));
+                }
+                c = d;
+            }
+            if (closed) subject.push(subject[0]);
+            a = b;
+        }
+        return subject;
+    };
+    function d3_geom_polygonInside(p, a, b) {
+        return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
+    }
+    function d3_geom_polygonIntersect(c, d, a, b) {
+        var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
+        return [ x1 + ua * x21, y1 + ua * y21 ];
+    }
+    function d3_geom_polygonClosed(coordinates) {
+        var a = coordinates[0], b = coordinates[coordinates.length - 1];
+        return !(a[0] - b[0] || a[1] - b[1]);
+    }
+    var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
+    function d3_geom_voronoiBeach() {
+        d3_geom_voronoiRedBlackNode(this);
+        this.edge = this.site = this.circle = null;
+    }
+    function d3_geom_voronoiCreateBeach(site) {
+        var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
+        beach.site = site;
+        return beach;
+    }
+    function d3_geom_voronoiDetachBeach(beach) {
+        d3_geom_voronoiDetachCircle(beach);
+        d3_geom_voronoiBeaches.remove(beach);
+        d3_geom_voronoiBeachPool.push(beach);
+        d3_geom_voronoiRedBlackNode(beach);
+    }
+    function d3_geom_voronoiRemoveBeach(beach) {
+        var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
+            x: x,
+            y: y
+        }, previous = beach.P, next = beach.N, disappearing = [ beach ];
+        d3_geom_voronoiDetachBeach(beach);
+        var lArc = previous;
+        while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
+            previous = lArc.P;
+            disappearing.unshift(lArc);
+            d3_geom_voronoiDetachBeach(lArc);
+            lArc = previous;
+        }
+        disappearing.unshift(lArc);
+        d3_geom_voronoiDetachCircle(lArc);
+        var rArc = next;
+        while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
+            next = rArc.N;
+            disappearing.push(rArc);
+            d3_geom_voronoiDetachBeach(rArc);
+            rArc = next;
+        }
+        disappearing.push(rArc);
+        d3_geom_voronoiDetachCircle(rArc);
+        var nArcs = disappearing.length, iArc;
+        for (iArc = 1; iArc < nArcs; ++iArc) {
+            rArc = disappearing[iArc];
+            lArc = disappearing[iArc - 1];
+            d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
+        }
+        lArc = disappearing[0];
+        rArc = disappearing[nArcs - 1];
+        rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
+        d3_geom_voronoiAttachCircle(lArc);
+        d3_geom_voronoiAttachCircle(rArc);
+    }
+    function d3_geom_voronoiAddBeach(site) {
+        var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
+        while (node) {
+            dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
+            if (dxl > ε) node = node.L; else {
+                dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
+                if (dxr > ε) {
+                    if (!node.R) {
+                        lArc = node;
+                        break;
+                    }
+                    node = node.R;
+                } else {
+                    if (dxl > -ε) {
+                        lArc = node.P;
+                        rArc = node;
+                    } else if (dxr > -ε) {
+                        lArc = node;
+                        rArc = node.N;
+                    } else {
+                        lArc = rArc = node;
+                    }
+                    break;
+                }
+            }
+        }
+        var newArc = d3_geom_voronoiCreateBeach(site);
+        d3_geom_voronoiBeaches.insert(lArc, newArc);
+        if (!lArc && !rArc) return;
+        if (lArc === rArc) {
+            d3_geom_voronoiDetachCircle(lArc);
+            rArc = d3_geom_voronoiCreateBeach(lArc.site);
+            d3_geom_voronoiBeaches.insert(newArc, rArc);
+            newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
+            d3_geom_voronoiAttachCircle(lArc);
+            d3_geom_voronoiAttachCircle(rArc);
+            return;
+        }
+        if (!rArc) {
+            newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
+            return;
+        }
+        d3_geom_voronoiDetachCircle(lArc);
+        d3_geom_voronoiDetachCircle(rArc);
+        var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = {
+            x: (cy * hb - by * hc) / d + ax,
+            y: (bx * hc - cx * hb) / d + ay
+        };
+        d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
+        newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
+        rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
+        d3_geom_voronoiAttachCircle(lArc);
+        d3_geom_voronoiAttachCircle(rArc);
+    }
+    function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
+        var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
+        if (!pby2) return rfocx;
+        var lArc = arc.P;
+        if (!lArc) return -Infinity;
+        site = lArc.site;
+        var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
+        if (!plby2) return lfocx;
+        var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
+        if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
+        return (rfocx + lfocx) / 2;
+    }
+    function d3_geom_voronoiRightBreakPoint(arc, directrix) {
+        var rArc = arc.N;
+        if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
+        var site = arc.site;
+        return site.y === directrix ? site.x : Infinity;
+    }
+    function d3_geom_voronoiCell(site) {
+        this.site = site;
+        this.edges = [];
+    }
+    d3_geom_voronoiCell.prototype.prepare = function() {
+        var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
+        while (iHalfEdge--) {
+            edge = halfEdges[iHalfEdge].edge;
+            if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
+        }
+        halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
+        return halfEdges.length;
+    };
+    function d3_geom_voronoiCloseCells(extent) {
+        var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end;
+        while (iCell--) {
+            cell = cells[iCell];
+            if (!cell || !cell.prepare()) continue;
+            halfEdges = cell.edges;
+            nHalfEdges = halfEdges.length;
+            iHalfEdge = 0;
+            while (iHalfEdge < nHalfEdges) {
+                end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
+                start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
+                if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
+                    halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
+                        x: x0,
+                        y: abs(x2 - x0) < ε ? y2 : y1
+                    } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
+                        x: abs(y2 - y1) < ε ? x2 : x1,
+                        y: y1
+                    } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
+                        x: x1,
+                        y: abs(x2 - x1) < ε ? y2 : y0
+                    } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
+                        x: abs(y2 - y0) < ε ? x2 : x0,
+                        y: y0
+                    } : null), cell.site, null));
+                    ++nHalfEdges;
+                }
+            }
+        }
+    }
+    function d3_geom_voronoiHalfEdgeOrder(a, b) {
+        return b.angle - a.angle;
+    }
+    function d3_geom_voronoiCircle() {
+        d3_geom_voronoiRedBlackNode(this);
+        this.x = this.y = this.arc = this.site = this.cy = null;
+    }
+    function d3_geom_voronoiAttachCircle(arc) {
+        var lArc = arc.P, rArc = arc.N;
+        if (!lArc || !rArc) return;
+        var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
+        if (lSite === rSite) return;
+        var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
+        var d = 2 * (ax * cy - ay * cx);
+        if (d >= -ε2) return;
+        var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by;
+        var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
+        circle.arc = arc;
+        circle.site = cSite;
+        circle.x = x + bx;
+        circle.y = cy + Math.sqrt(x * x + y * y);
+        circle.cy = cy;
+        arc.circle = circle;
+        var before = null, node = d3_geom_voronoiCircles._;
+        while (node) {
+            if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
+                if (node.L) node = node.L; else {
+                    before = node.P;
+                    break;
+                }
+            } else {
+                if (node.R) node = node.R; else {
+                    before = node;
+                    break;
+                }
+            }
+        }
+        d3_geom_voronoiCircles.insert(before, circle);
+        if (!before) d3_geom_voronoiFirstCircle = circle;
+    }
+    function d3_geom_voronoiDetachCircle(arc) {
+        var circle = arc.circle;
+        if (circle) {
+            if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
+            d3_geom_voronoiCircles.remove(circle);
+            d3_geom_voronoiCirclePool.push(circle);
+            d3_geom_voronoiRedBlackNode(circle);
+            arc.circle = null;
+        }
+    }
+    function d3_geom_voronoiClipEdges(extent) {
+        var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e;
+        while (i--) {
+            e = edges[i];
+            if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
+                e.a = e.b = null;
+                edges.splice(i, 1);
+            }
+        }
+    }
+    function d3_geom_voronoiConnectEdge(edge, extent) {
+        var vb = edge.b;
+        if (vb) return true;
+        var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb;
+        if (ry === ly) {
+            if (fx < x0 || fx >= x1) return;
+            if (lx > rx) {
+                if (!va) va = {
+                    x: fx,
+                    y: y0
+                }; else if (va.y >= y1) return;
+                vb = {
+                    x: fx,
+                    y: y1
+                };
+            } else {
+                if (!va) va = {
+                    x: fx,
+                    y: y1
+                }; else if (va.y < y0) return;
+                vb = {
+                    x: fx,
+                    y: y0
+                };
+            }
+        } else {
+            fm = (lx - rx) / (ry - ly);
+            fb = fy - fm * fx;
+            if (fm < -1 || fm > 1) {
+                if (lx > rx) {
+                    if (!va) va = {
+                        x: (y0 - fb) / fm,
+                        y: y0
+                    }; else if (va.y >= y1) return;
+                    vb = {
+                        x: (y1 - fb) / fm,
+                        y: y1
+                    };
+                } else {
+                    if (!va) va = {
+                        x: (y1 - fb) / fm,
+                        y: y1
+                    }; else if (va.y < y0) return;
+                    vb = {
+                        x: (y0 - fb) / fm,
+                        y: y0
+                    };
+                }
+            } else {
+                if (ly < ry) {
+                    if (!va) va = {
+                        x: x0,
+                        y: fm * x0 + fb
+                    }; else if (va.x >= x1) return;
+                    vb = {
+                        x: x1,
+                        y: fm * x1 + fb
+                    };
+                } else {
+                    if (!va) va = {
+                        x: x1,
+                        y: fm * x1 + fb
+                    }; else if (va.x < x0) return;
+                    vb = {
+                        x: x0,
+                        y: fm * x0 + fb
+                    };
+                }
+            }
+        }
+        edge.a = va;
+        edge.b = vb;
+        return true;
+    }
+    function d3_geom_voronoiEdge(lSite, rSite) {
+        this.l = lSite;
+        this.r = rSite;
+        this.a = this.b = null;
+    }
+    function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
+        var edge = new d3_geom_voronoiEdge(lSite, rSite);
+        d3_geom_voronoiEdges.push(edge);
+        if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
+        if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
+        d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
+        d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
+        return edge;
+    }
+    function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
+        var edge = new d3_geom_voronoiEdge(lSite, null);
+        edge.a = va;
+        edge.b = vb;
+        d3_geom_voronoiEdges.push(edge);
+        return edge;
+    }
+    function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
+        if (!edge.a && !edge.b) {
+            edge.a = vertex;
+            edge.l = lSite;
+            edge.r = rSite;
+        } else if (edge.l === rSite) {
+            edge.b = vertex;
+        } else {
+            edge.a = vertex;
+        }
+    }
+    function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
+        var va = edge.a, vb = edge.b;
+        this.edge = edge;
+        this.site = lSite;
+        this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y);
+    }
+    d3_geom_voronoiHalfEdge.prototype = {
+        start: function() {
+            return this.edge.l === this.site ? this.edge.a : this.edge.b;
+        },
+        end: function() {
+            return this.edge.l === this.site ? this.edge.b : this.edge.a;
+        }
+    };
+    function d3_geom_voronoiRedBlackTree() {
+        this._ = null;
+    }
+    function d3_geom_voronoiRedBlackNode(node) {
+        node.U = node.C = node.L = node.R = node.P = node.N = null;
+    }
+    d3_geom_voronoiRedBlackTree.prototype = {
+        insert: function(after, node) {
+            var parent, grandpa, uncle;
+            if (after) {
+                node.P = after;
+                node.N = after.N;
+                if (after.N) after.N.P = node;
+                after.N = node;
+                if (after.R) {
+                    after = after.R;
+                    while (after.L) after = after.L;
+                    after.L = node;
+                } else {
+                    after.R = node;
+                }
+                parent = after;
+            } else if (this._) {
+                after = d3_geom_voronoiRedBlackFirst(this._);
+                node.P = null;
+                node.N = after;
+                after.P = after.L = node;
+                parent = after;
+            } else {
+                node.P = node.N = null;
+                this._ = node;
+                parent = null;
+            }
+            node.L = node.R = null;
+            node.U = parent;
+            node.C = true;
+            after = node;
+            while (parent && parent.C) {
+                grandpa = parent.U;
+                if (parent === grandpa.L) {
+                    uncle = grandpa.R;
+                    if (uncle && uncle.C) {
+                        parent.C = uncle.C = false;
+                        grandpa.C = true;
+                        after = grandpa;
+                    } else {
+                        if (after === parent.R) {
+                            d3_geom_voronoiRedBlackRotateLeft(this, parent);
+                            after = parent;
+                            parent = after.U;
+                        }
+                        parent.C = false;
+                        grandpa.C = true;
+                        d3_geom_voronoiRedBlackRotateRight(this, grandpa);
+                    }
+                } else {
+                    uncle = grandpa.L;
+                    if (uncle && uncle.C) {
+                        parent.C = uncle.C = false;
+                        grandpa.C = true;
+                        after = grandpa;
+                    } else {
+                        if (after === parent.L) {
+                            d3_geom_voronoiRedBlackRotateRight(this, parent);
+                            after = parent;
+                            parent = after.U;
+                        }
+                        parent.C = false;
+                        grandpa.C = true;
+                        d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
+                    }
+                }
+                parent = after.U;
+            }
+            this._.C = false;
+        },
+        remove: function(node) {
+            if (node.N) node.N.P = node.P;
+            if (node.P) node.P.N = node.N;
+            node.N = node.P = null;
+            var parent = node.U, sibling, left = node.L, right = node.R, next, red;
+            if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
+            if (parent) {
+                if (parent.L === node) parent.L = next; else parent.R = next;
+            } else {
+                this._ = next;
+            }
+            if (left && right) {
+                red = next.C;
+                next.C = node.C;
+                next.L = left;
+                left.U = next;
+                if (next !== right) {
+                    parent = next.U;
+                    next.U = node.U;
+                    node = next.R;
+                    parent.L = node;
+                    next.R = right;
+                    right.U = next;
+                } else {
+                    next.U = parent;
+                    parent = next;
+                    node = next.R;
+                }
+            } else {
+                red = node.C;
+                node = next;
+            }
+            if (node) node.U = parent;
+            if (red) return;
+            if (node && node.C) {
+                node.C = false;
+                return;
+            }
+            do {
+                if (node === this._) break;
+                if (node === parent.L) {
+                    sibling = parent.R;
+                    if (sibling.C) {
+                        sibling.C = false;
+                        parent.C = true;
+                        d3_geom_voronoiRedBlackRotateLeft(this, parent);
+                        sibling = parent.R;
+                    }
+                    if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
+                        if (!sibling.R || !sibling.R.C) {
+                            sibling.L.C = false;
+                            sibling.C = true;
+                            d3_geom_voronoiRedBlackRotateRight(this, sibling);
+                            sibling = parent.R;
+                        }
+                        sibling.C = parent.C;
+                        parent.C = sibling.R.C = false;
+                        d3_geom_voronoiRedBlackRotateLeft(this, parent);
+                        node = this._;
+                        break;
+                    }
+                } else {
+                    sibling = parent.L;
+                    if (sibling.C) {
+                        sibling.C = false;
+                        parent.C = true;
+                        d3_geom_voronoiRedBlackRotateRight(this, parent);
+                        sibling = parent.L;
+                    }
+                    if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
+                        if (!sibling.L || !sibling.L.C) {
+                            sibling.R.C = false;
+                            sibling.C = true;
+                            d3_geom_voronoiRedBlackRotateLeft(this, sibling);
+                            sibling = parent.L;
+                        }
+                        sibling.C = parent.C;
+                        parent.C = sibling.L.C = false;
+                        d3_geom_voronoiRedBlackRotateRight(this, parent);
+                        node = this._;
+                        break;
+                    }
+                }
+                sibling.C = true;
+                node = parent;
+                parent = parent.U;
+            } while (!node.C);
+            if (node) node.C = false;
+        }
+    };
+    function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
+        var p = node, q = node.R, parent = p.U;
+        if (parent) {
+            if (parent.L === p) parent.L = q; else parent.R = q;
+        } else {
+            tree._ = q;
+        }
+        q.U = parent;
+        p.U = q;
+        p.R = q.L;
+        if (p.R) p.R.U = p;
+        q.L = p;
+    }
+    function d3_geom_voronoiRedBlackRotateRight(tree, node) {
+        var p = node, q = node.L, parent = p.U;
+        if (parent) {
+            if (parent.L === p) parent.L = q; else parent.R = q;
+        } else {
+            tree._ = q;
+        }
+        q.U = parent;
+        p.U = q;
+        p.L = q.R;
+        if (p.L) p.L.U = p;
+        q.R = p;
+    }
+    function d3_geom_voronoiRedBlackFirst(node) {
+        while (node.L) node = node.L;
+        return node;
+    }
+    function d3_geom_voronoi(sites, bbox) {
+        var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
+        d3_geom_voronoiEdges = [];
+        d3_geom_voronoiCells = new Array(sites.length);
+        d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
+        d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
+        while (true) {
+            circle = d3_geom_voronoiFirstCircle;
+            if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
+                if (site.x !== x0 || site.y !== y0) {
+                    d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
+                    d3_geom_voronoiAddBeach(site);
+                    x0 = site.x, y0 = site.y;
+                }
+                site = sites.pop();
+            } else if (circle) {
+                d3_geom_voronoiRemoveBeach(circle.arc);
+            } else {
+                break;
+            }
+        }
+        if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
+        var diagram = {
+            cells: d3_geom_voronoiCells,
+            edges: d3_geom_voronoiEdges
+        };
+        d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
+        return diagram;
+    }
+    function d3_geom_voronoiVertexOrder(a, b) {
+        return b.y - a.y || b.x - a.x;
+    }
+    d3.geom.voronoi = function(points) {
+        var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
+        if (points) return voronoi(points);
+        function voronoi(data) {
+            var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
+            d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
+                var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
+                    var s = e.start();
+                    return [ s.x, s.y ];
+                }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
+                polygon.point = data[i];
+            });
+            return polygons;
+        }
+        function sites(data) {
+            return data.map(function(d, i) {
+                return {
+                    x: Math.round(fx(d, i) / ε) * ε,
+                    y: Math.round(fy(d, i) / ε) * ε,
+                    i: i
+                };
+            });
+        }
+        voronoi.links = function(data) {
+            return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
+                return edge.l && edge.r;
+            }).map(function(edge) {
+                return {
+                    source: data[edge.l.i],
+                    target: data[edge.r.i]
+                };
+            });
+        };
+        voronoi.triangles = function(data) {
+            var triangles = [];
+            d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
+                var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l;
+                while (++j < m) {
+                    e0 = e1;
+                    s0 = s1;
+                    e1 = edges[j].edge;
+                    s1 = e1.l === site ? e1.r : e1.l;
+                    if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
+                        triangles.push([ data[i], data[s0.i], data[s1.i] ]);
+                    }
+                }
+            });
+            return triangles;
+        };
+        voronoi.x = function(_) {
+            return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
+        };
+        voronoi.y = function(_) {
+            return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
+        };
+        voronoi.clipExtent = function(_) {
+            if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
+            clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
+            return voronoi;
+        };
+        voronoi.size = function(_) {
+            if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
+            return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
+        };
+        return voronoi;
+    };
+    var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
+    function d3_geom_voronoiTriangleArea(a, b, c) {
+        return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
+    }
+    d3.geom.delaunay = function(vertices) {
+        return d3.geom.voronoi().triangles(vertices);
+    };
+    d3.geom.quadtree = function(points, x1, y1, x2, y2) {
+        var x = d3_geom_pointX, y = d3_geom_pointY, compat;
+        if (compat = arguments.length) {
+            x = d3_geom_quadtreeCompatX;
+            y = d3_geom_quadtreeCompatY;
+            if (compat === 3) {
+                y2 = y1;
+                x2 = x1;
+                y1 = x1 = 0;
+            }
+            return quadtree(points);
+        }
+        function quadtree(data) {
+            var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
+            if (x1 != null) {
+                x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
+            } else {
+                x2_ = y2_ = -(x1_ = y1_ = Infinity);
+                xs = [], ys = [];
+                n = data.length;
+                if (compat) for (i = 0; i < n; ++i) {
+                    d = data[i];
+                    if (d.x < x1_) x1_ = d.x;
+                    if (d.y < y1_) y1_ = d.y;
+                    if (d.x > x2_) x2_ = d.x;
+                    if (d.y > y2_) y2_ = d.y;
+                    xs.push(d.x);
+                    ys.push(d.y);
+                } else for (i = 0; i < n; ++i) {
+                    var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
+                    if (x_ < x1_) x1_ = x_;
+                    if (y_ < y1_) y1_ = y_;
+                    if (x_ > x2_) x2_ = x_;
+                    if (y_ > y2_) y2_ = y_;
+                    xs.push(x_);
+                    ys.push(y_);
+                }
+            }
+            var dx = x2_ - x1_, dy = y2_ - y1_;
+            if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
+            function insert(n, d, x, y, x1, y1, x2, y2) {
+                if (isNaN(x) || isNaN(y)) return;
+                if (n.leaf) {
+                    var nx = n.x, ny = n.y;
+                    if (nx != null) {
+                        if (abs(nx - x) + abs(ny - y) < .01) {
+                            insertChild(n, d, x, y, x1, y1, x2, y2);
+                        } else {
+                            var nPoint = n.point;
+                            n.x = n.y = n.point = null;
+                            insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
+                            insertChild(n, d, x, y, x1, y1, x2, y2);
+                        }
+                    } else {
+                        n.x = x, n.y = y, n.point = d;
+                    }
+                } else {
+                    insertChild(n, d, x, y, x1, y1, x2, y2);
+                }
+            }
+            function insertChild(n, d, x, y, x1, y1, x2, y2) {
+                var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right;
+                n.leaf = false;
+                n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
+                if (right) x1 = xm; else x2 = xm;
+                if (below) y1 = ym; else y2 = ym;
+                insert(n, d, x, y, x1, y1, x2, y2);
+            }
+            var root = d3_geom_quadtreeNode();
+            root.add = function(d) {
+                insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
+            };
+            root.visit = function(f) {
+                d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
+            };
+            root.find = function(point) {
+                return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_);
+            };
+            i = -1;
+            if (x1 == null) {
+                while (++i < n) {
+                    insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
+                }
+                --i;
+            } else data.forEach(root.add);
+            xs = ys = data = d = null;
+            return root;
+        }
+        quadtree.x = function(_) {
+            return arguments.length ? (x = _, quadtree) : x;
+        };
+        quadtree.y = function(_) {
+            return arguments.length ? (y = _, quadtree) : y;
+        };
+        quadtree.extent = function(_) {
+            if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
+            if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0],
+                y2 = +_[1][1];
+            return quadtree;
+        };
+        quadtree.size = function(_) {
+            if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
+            if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
+            return quadtree;
+        };
+        return quadtree;
+    };
+    function d3_geom_quadtreeCompatX(d) {
+        return d.x;
+    }
+    function d3_geom_quadtreeCompatY(d) {
+        return d.y;
+    }
+    function d3_geom_quadtreeNode() {
+        return {
+            leaf: true,
+            nodes: [],
+            point: null,
+            x: null,
+            y: null
+        };
+    }
+    function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
+        if (!f(node, x1, y1, x2, y2)) {
+            var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
+            if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
+            if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
+            if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
+            if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
+        }
+    }
+    function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) {
+        var minDistance2 = Infinity, closestPoint;
+        (function find(node, x1, y1, x2, y2) {
+            if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return;
+            if (point = node.point) {
+                var point, dx = x - point[0], dy = y - point[1], distance2 = dx * dx + dy * dy;
+                if (distance2 < minDistance2) {
+                    var distance = Math.sqrt(minDistance2 = distance2);
+                    x0 = x - distance, y0 = y - distance;
+                    x3 = x + distance, y3 = y + distance;
+                    closestPoint = point;
+                }
+            }
+            var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym;
+            for (var i = below << 1 | right, j = i + 4; i < j; ++i) {
+                if (node = children[i & 3]) switch (i & 3) {
+                    case 0:
+                        find(node, x1, y1, xm, ym);
+                        break;
+
+                    case 1:
+                        find(node, xm, y1, x2, ym);
+                        break;
+
+                    case 2:
+                        find(node, x1, ym, xm, y2);
+                        break;
+
+                    case 3:
+                        find(node, xm, ym, x2, y2);
+                        break;
+                }
+            }
+        })(root, x0, y0, x3, y3);
+        return closestPoint;
+    }
+    d3.interpolateRgb = d3_interpolateRgb;
+    function d3_interpolateRgb(a, b) {
+        a = d3.rgb(a);
+        b = d3.rgb(b);
+        var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
+        return function(t) {
+            return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t));
+        };
+    }
+    d3.interpolateObject = d3_interpolateObject;
+    function d3_interpolateObject(a, b) {
+        var i = {}, c = {}, k;
+        for (k in a) {
+            if (k in b) {
+                i[k] = d3_interpolate(a[k], b[k]);
+            } else {
+                c[k] = a[k];
+            }
+        }
+        for (k in b) {
+            if (!(k in a)) {
+                c[k] = b[k];
+            }
+        }
+        return function(t) {
+            for (k in i) c[k] = i[k](t);
+            return c;
+        };
+    }
+    d3.interpolateNumber = d3_interpolateNumber;
+    function d3_interpolateNumber(a, b) {
+        a = +a, b = +b;
+        return function(t) {
+            return a * (1 - t) + b * t;
+        };
+    }
+    d3.interpolateString = d3_interpolateString;
+    function d3_interpolateString(a, b) {
+        var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = [];
+        a = a + "", b = b + "";
+        while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) {
+            if ((bs = bm.index) > bi) {
+                bs = b.slice(bi, bs);
+                if (s[i]) s[i] += bs; else s[++i] = bs;
+            }
+            if ((am = am[0]) === (bm = bm[0])) {
+                if (s[i]) s[i] += bm; else s[++i] = bm;
+            } else {
+                s[++i] = null;
+                q.push({
+                    i: i,
+                    x: d3_interpolateNumber(am, bm)
+                });
+            }
+            bi = d3_interpolate_numberB.lastIndex;
+        }
+        if (bi < b.length) {
+            bs = b.slice(bi);
+            if (s[i]) s[i] += bs; else s[++i] = bs;
+        }
+        return s.length < 2 ? q[0] ? (b = q[0].x, function(t) {
+            return b(t) + "";
+        }) : function() {
+            return b;
+        } : (b = q.length, function(t) {
+            for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
+            return s.join("");
+        });
+    }
+    var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
+    d3.interpolate = d3_interpolate;
+    function d3_interpolate(a, b) {
+        var i = d3.interpolators.length, f;
+        while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
+        return f;
+    }
+    d3.interpolators = [ function(a, b) {
+        var t = typeof b;
+        return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b);
+    } ];
+    d3.interpolateArray = d3_interpolateArray;
+    function d3_interpolateArray(a, b) {
+        var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
+        for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
+        for (;i < na; ++i) c[i] = a[i];
+        for (;i < nb; ++i) c[i] = b[i];
+        return function(t) {
+            for (i = 0; i < n0; ++i) c[i] = x[i](t);
+            return c;
+        };
+    }
+    var d3_ease_default = function() {
+        return d3_identity;
+    };
+    var d3_ease = d3.map({
+        linear: d3_ease_default,
+        poly: d3_ease_poly,
+        quad: function() {
+            return d3_ease_quad;
+        },
+        cubic: function() {
+            return d3_ease_cubic;
+        },
+        sin: function() {
+            return d3_ease_sin;
+        },
+        exp: function() {
+            return d3_ease_exp;
+        },
+        circle: function() {
+            return d3_ease_circle;
+        },
+        elastic: d3_ease_elastic,
+        back: d3_ease_back,
+        bounce: function() {
+            return d3_ease_bounce;
+        }
+    });
+    var d3_ease_mode = d3.map({
+        "in": d3_identity,
+        out: d3_ease_reverse,
+        "in-out": d3_ease_reflect,
+        "out-in": function(f) {
+            return d3_ease_reflect(d3_ease_reverse(f));
+        }
+    });
+    d3.ease = function(name) {
+        var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in";
+        t = d3_ease.get(t) || d3_ease_default;
+        m = d3_ease_mode.get(m) || d3_identity;
+        return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
+    };
+    function d3_ease_clamp(f) {
+        return function(t) {
+            return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
+        };
+    }
+    function d3_ease_reverse(f) {
+        return function(t) {
+            return 1 - f(1 - t);
+        };
+    }
+    function d3_ease_reflect(f) {
+        return function(t) {
+            return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
+        };
+    }
+    function d3_ease_quad(t) {
+        return t * t;
+    }
+    function d3_ease_cubic(t) {
+        return t * t * t;
+    }
+    function d3_ease_cubicInOut(t) {
+        if (t <= 0) return 0;
+        if (t >= 1) return 1;
+        var t2 = t * t, t3 = t2 * t;
+        return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
+    }
+    function d3_ease_poly(e) {
+        return function(t) {
+            return Math.pow(t, e);
+        };
+    }
+    function d3_ease_sin(t) {
+        return 1 - Math.cos(t * halfπ);
+    }
+    function d3_ease_exp(t) {
+        return Math.pow(2, 10 * (t - 1));
+    }
+    function d3_ease_circle(t) {
+        return 1 - Math.sqrt(1 - t * t);
+    }
+    function d3_ease_elastic(a, p) {
+        var s;
+        if (arguments.length < 2) p = .45;
+        if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4;
+        return function(t) {
+            return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
+        };
+    }
+    function d3_ease_back(s) {
+        if (!s) s = 1.70158;
+        return function(t) {
+            return t * t * ((s + 1) * t - s);
+        };
+    }
+    function d3_ease_bounce(t) {
+        return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
+    }
+    d3.interpolateHcl = d3_interpolateHcl;
+    function d3_interpolateHcl(a, b) {
+        a = d3.hcl(a);
+        b = d3.hcl(b);
+        var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
+        if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
+        if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
+        return function(t) {
+            return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
+        };
+    }
+    d3.interpolateHsl = d3_interpolateHsl;
+    function d3_interpolateHsl(a, b) {
+        a = d3.hsl(a);
+        b = d3.hsl(b);
+        var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
+        if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
+        if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
+        return function(t) {
+            return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
+        };
+    }
+    d3.interpolateLab = d3_interpolateLab;
+    function d3_interpolateLab(a, b) {
+        a = d3.lab(a);
+        b = d3.lab(b);
+        var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
+        return function(t) {
+            return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
+        };
+    }
+    d3.interpolateRound = d3_interpolateRound;
+    function d3_interpolateRound(a, b) {
+        b -= a;
+        return function(t) {
+            return Math.round(a + b * t);
+        };
+    }
+    d3.transform = function(string) {
+        var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
+        return (d3.transform = function(string) {
+            if (string != null) {
+                g.setAttribute("transform", string);
+                var t = g.transform.baseVal.consolidate();
+            }
+            return new d3_transform(t ? t.matrix : d3_transformIdentity);
+        })(string);
+    };
+    function d3_transform(m) {
+        var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
+        if (r0[0] * r1[1] < r1[0] * r0[1]) {
+            r0[0] *= -1;
+            r0[1] *= -1;
+            kx *= -1;
+            kz *= -1;
+        }
+        this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
+        this.translate = [ m.e, m.f ];
+        this.scale = [ kx, ky ];
+        this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
+    }
+    d3_transform.prototype.toString = function() {
+        return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
+    };
+    function d3_transformDot(a, b) {
+        return a[0] * b[0] + a[1] * b[1];
+    }
+    function d3_transformNormalize(a) {
+        var k = Math.sqrt(d3_transformDot(a, a));
+        if (k) {
+            a[0] /= k;
+            a[1] /= k;
+        }
+        return k;
+    }
+    function d3_transformCombine(a, b, k) {
+        a[0] += k * b[0];
+        a[1] += k * b[1];
+        return a;
+    }
+    var d3_transformIdentity = {
+        a: 1,
+        b: 0,
+        c: 0,
+        d: 1,
+        e: 0,
+        f: 0
+    };
+    d3.interpolateTransform = d3_interpolateTransform;
+    function d3_interpolateTransform(a, b) {
+        var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale;
+        if (ta[0] != tb[0] || ta[1] != tb[1]) {
+            s.push("translate(", null, ",", null, ")");
+            q.push({
+                i: 1,
+                x: d3_interpolateNumber(ta[0], tb[0])
+            }, {
+                i: 3,
+                x: d3_interpolateNumber(ta[1], tb[1])
+            });
+        } else if (tb[0] || tb[1]) {
+            s.push("translate(" + tb + ")");
+        } else {
+            s.push("");
+        }
+        if (ra != rb) {
+            if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
+            q.push({
+                i: s.push(s.pop() + "rotate(", null, ")") - 2,
+                x: d3_interpolateNumber(ra, rb)
+            });
+        } else if (rb) {
+            s.push(s.pop() + "rotate(" + rb + ")");
+        }
+        if (wa != wb) {
+            q.push({
+                i: s.push(s.pop() + "skewX(", null, ")") - 2,
+                x: d3_interpolateNumber(wa, wb)
+            });
+        } else if (wb) {
+            s.push(s.pop() + "skewX(" + wb + ")");
+        }
+        if (ka[0] != kb[0] || ka[1] != kb[1]) {
+            n = s.push(s.pop() + "scale(", null, ",", null, ")");
+            q.push({
+                i: n - 4,
+                x: d3_interpolateNumber(ka[0], kb[0])
+            }, {
+                i: n - 2,
+                x: d3_interpolateNumber(ka[1], kb[1])
+            });
+        } else if (kb[0] != 1 || kb[1] != 1) {
+            s.push(s.pop() + "scale(" + kb + ")");
+        }
+        n = q.length;
+        return function(t) {
+            var i = -1, o;
+            while (++i < n) s[(o = q[i]).i] = o.x(t);
+            return s.join("");
+        };
+    }
+    function d3_uninterpolateNumber(a, b) {
+        b = (b -= a = +a) || 1 / b;
+        return function(x) {
+            return (x - a) / b;
+        };
+    }
+    function d3_uninterpolateClamp(a, b) {
+        b = (b -= a = +a) || 1 / b;
+        return function(x) {
+            return Math.max(0, Math.min(1, (x - a) / b));
+        };
+    }
+    d3.layout = {};
+    d3.layout.bundle = function() {
+        return function(links) {
+            var paths = [], i = -1, n = links.length;
+            while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
+            return paths;
+        };
+    };
+    function d3_layout_bundlePath(link) {
+        var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
+        while (start !== lca) {
+            start = start.parent;
+            points.push(start);
+        }
+        var k = points.length;
+        while (end !== lca) {
+            points.splice(k, 0, end);
+            end = end.parent;
+        }
+        return points;
+    }
+    function d3_layout_bundleAncestors(node) {
+        var ancestors = [], parent = node.parent;
+        while (parent != null) {
+            ancestors.push(node);
+            node = parent;
+            parent = parent.parent;
+        }
+        ancestors.push(node);
+        return ancestors;
+    }
+    function d3_layout_bundleLeastCommonAncestor(a, b) {
+        if (a === b) return a;
+        var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
+        while (aNode === bNode) {
+            sharedNode = aNode;
+            aNode = aNodes.pop();
+            bNode = bNodes.pop();
+        }
+        return sharedNode;
+    }
+    d3.layout.chord = function() {
+        var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
+        function relayout() {
+            var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
+            chords = [];
+            groups = [];
+            k = 0, i = -1;
+            while (++i < n) {
+                x = 0, j = -1;
+                while (++j < n) {
+                    x += matrix[i][j];
+                }
+                groupSums.push(x);
+                subgroupIndex.push(d3.range(n));
+                k += x;
+            }
+            if (sortGroups) {
+                groupIndex.sort(function(a, b) {
+                    return sortGroups(groupSums[a], groupSums[b]);
+                });
+            }
+            if (sortSubgroups) {
+                subgroupIndex.forEach(function(d, i) {
+                    d.sort(function(a, b) {
+                        return sortSubgroups(matrix[i][a], matrix[i][b]);
+                    });
+                });
+            }
+            k = (τ - padding * n) / k;
+            x = 0, i = -1;
+            while (++i < n) {
+                x0 = x, j = -1;
+                while (++j < n) {
+                    var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
+                    subgroups[di + "-" + dj] = {
+                        index: di,
+                        subindex: dj,
+                        startAngle: a0,
+                        endAngle: a1,
+                        value: v
+                    };
+                }
+                groups[di] = {
+                    index: di,
+                    startAngle: x0,
+                    endAngle: x,
+                    value: (x - x0) / k
+                };
+                x += padding;
+            }
+            i = -1;
+            while (++i < n) {
+                j = i - 1;
+                while (++j < n) {
+                    var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
+                    if (source.value || target.value) {
+                        chords.push(source.value < target.value ? {
+                            source: target,
+                            target: source
+                        } : {
+                            source: source,
+                            target: target
+                        });
+                    }
+                }
+            }
+            if (sortChords) resort();
+        }
+        function resort() {
+            chords.sort(function(a, b) {
+                return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
+            });
+        }
+        chord.matrix = function(x) {
+            if (!arguments.length) return matrix;
+            n = (matrix = x) && matrix.length;
+            chords = groups = null;
+            return chord;
+        };
+        chord.padding = function(x) {
+            if (!arguments.length) return padding;
+            padding = x;
+            chords = groups = null;
+            return chord;
+        };
+        chord.sortGroups = function(x) {
+            if (!arguments.length) return sortGroups;
+            sortGroups = x;
+            chords = groups = null;
+            return chord;
+        };
+        chord.sortSubgroups = function(x) {
+            if (!arguments.length) return sortSubgroups;
+            sortSubgroups = x;
+            chords = null;
+            return chord;
+        };
+        chord.sortChords = function(x) {
+            if (!arguments.length) return sortChords;
+            sortChords = x;
+            if (chords) resort();
+            return chord;
+        };
+        chord.chords = function() {
+            if (!chords) relayout();
+            return chords;
+        };
+        chord.groups = function() {
+            if (!groups) relayout();
+            return groups;
+        };
+        return chord;
+    };
+    d3.layout.force = function() {
+        var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges;
+        function repulse(node) {
+            return function(quad, x1, _, x2) {
+                if (quad.point !== node) {
+                    var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy;
+                    if (dw * dw / theta2 < dn) {
+                        if (dn < chargeDistance2) {
+                            var k = quad.charge / dn;
+                            node.px -= dx * k;
+                            node.py -= dy * k;
+                        }
+                        return true;
+                    }
+                    if (quad.point && dn && dn < chargeDistance2) {
+                        var k = quad.pointCharge / dn;
+                        node.px -= dx * k;
+                        node.py -= dy * k;
+                    }
+                }
+                return !quad.charge;
+            };
+        }
+        force.tick = function() {
+            if ((alpha *= .99) < .005) {
+                event.end({
+                    type: "end",
+                    alpha: alpha = 0
+                });
+                return true;
+            }
+            var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
+            for (i = 0; i < m; ++i) {
+                o = links[i];
+                s = o.source;
+                t = o.target;
+                x = t.x - s.x;
+                y = t.y - s.y;
+                if (l = x * x + y * y) {
+                    l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
+                    x *= l;
+                    y *= l;
+                    t.x -= x * (k = s.weight / (t.weight + s.weight));
+                    t.y -= y * k;
+                    s.x += x * (k = 1 - k);
+                    s.y += y * k;
+                }
+            }
+            if (k = alpha * gravity) {
+                x = size[0] / 2;
+                y = size[1] / 2;
+                i = -1;
+                if (k) while (++i < n) {
+                    o = nodes[i];
+                    o.x += (x - o.x) * k;
+                    o.y += (y - o.y) * k;
+                }
+            }
+            if (charge) {
+                d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
+                i = -1;
+                while (++i < n) {
+                    if (!(o = nodes[i]).fixed) {
+                        q.visit(repulse(o));
+                    }
+                }
+            }
+            i = -1;
+            while (++i < n) {
+                o = nodes[i];
+                if (o.fixed) {
+                    o.x = o.px;
+                    o.y = o.py;
+                } else {
+                    o.x -= (o.px - (o.px = o.x)) * friction;
+                    o.y -= (o.py - (o.py = o.y)) * friction;
+                }
+            }
+            event.tick({
+                type: "tick",
+                alpha: alpha
+            });
+        };
+        force.nodes = function(x) {
+            if (!arguments.length) return nodes;
+            nodes = x;
+            return force;
+        };
+        force.links = function(x) {
+            if (!arguments.length) return links;
+            links = x;
+            return force;
+        };
+        force.size = function(x) {
+            if (!arguments.length) return size;
+            size = x;
+            return force;
+        };
+        force.linkDistance = function(x) {
+            if (!arguments.length) return linkDistance;
+            linkDistance = typeof x === "function" ? x : +x;
+            return force;
+        };
+        force.distance = force.linkDistance;
+        force.linkStrength = function(x) {
+            if (!arguments.length) return linkStrength;
+            linkStrength = typeof x === "function" ? x : +x;
+            return force;
+        };
+        force.friction = function(x) {
+            if (!arguments.length) return friction;
+            friction = +x;
+            return force;
+        };
+        force.charge = function(x) {
+            if (!arguments.length) return charge;
+            charge = typeof x === "function" ? x : +x;
+            return force;
+        };
+        force.chargeDistance = function(x) {
+            if (!arguments.length) return Math.sqrt(chargeDistance2);
+            chargeDistance2 = x * x;
+            return force;
+        };
+        force.gravity = function(x) {
+            if (!arguments.length) return gravity;
+            gravity = +x;
+            return force;
+        };
+        force.theta = function(x) {
+            if (!arguments.length) return Math.sqrt(theta2);
+            theta2 = x * x;
+            return force;
+        };
+        force.alpha = function(x) {
+            if (!arguments.length) return alpha;
+            x = +x;
+            if (alpha) {
+                if (x > 0) alpha = x; else alpha = 0;
+            } else if (x > 0) {
+                event.start({
+                    type: "start",
+                    alpha: alpha = x
+                });
+                d3.timer(force.tick);
+            }
+            return force;
+        };
+        force.start = function() {
+            var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
+            for (i = 0; i < n; ++i) {
+                (o = nodes[i]).index = i;
+                o.weight = 0;
+            }
+            for (i = 0; i < m; ++i) {
+                o = links[i];
+                if (typeof o.source == "number") o.source = nodes[o.source];
+                if (typeof o.target == "number") o.target = nodes[o.target];
+                ++o.source.weight;
+                ++o.target.weight;
+            }
+            for (i = 0; i < n; ++i) {
+                o = nodes[i];
+                if (isNaN(o.x)) o.x = position("x", w);
+                if (isNaN(o.y)) o.y = position("y", h);
+                if (isNaN(o.px)) o.px = o.x;
+                if (isNaN(o.py)) o.py = o.y;
+            }
+            distances = [];
+            if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance;
+            strengths = [];
+            if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength;
+            charges = [];
+            if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge;
+            function position(dimension, size) {
+                if (!neighbors) {
+                    neighbors = new Array(n);
+                    for (j = 0; j < n; ++j) {
+                        neighbors[j] = [];
+                    }
+                    for (j = 0; j < m; ++j) {
+                        var o = links[j];
+                        neighbors[o.source.index].push(o.target);
+                        neighbors[o.target.index].push(o.source);
+                    }
+                }
+                var candidates = neighbors[i], j = -1, m = candidates.length, x;
+                while (++j < m) if (!isNaN(x = candidates[j][dimension])) return x;
+                return Math.random() * size;
+            }
+            return force.resume();
+        };
+        force.resume = function() {
+            return force.alpha(.1);
+        };
+        force.stop = function() {
+            return force.alpha(0);
+        };
+        force.drag = function() {
+            if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend);
+            if (!arguments.length) return drag;
+            this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
+        };
+        function dragmove(d) {
+            d.px = d3.event.x, d.py = d3.event.y;
+            force.resume();
+        }
+        return d3.rebind(force, event, "on");
+    };
+    function d3_layout_forceDragstart(d) {
+        d.fixed |= 2;
+    }
+    function d3_layout_forceDragend(d) {
+        d.fixed &= ~6;
+    }
+    function d3_layout_forceMouseover(d) {
+        d.fixed |= 4;
+        d.px = d.x, d.py = d.y;
+    }
+    function d3_layout_forceMouseout(d) {
+        d.fixed &= ~4;
+    }
+    function d3_layout_forceAccumulate(quad, alpha, charges) {
+        var cx = 0, cy = 0;
+        quad.charge = 0;
+        if (!quad.leaf) {
+            var nodes = quad.nodes, n = nodes.length, i = -1, c;
+            while (++i < n) {
+                c = nodes[i];
+                if (c == null) continue;
+                d3_layout_forceAccumulate(c, alpha, charges);
+                quad.charge += c.charge;
+                cx += c.charge * c.cx;
+                cy += c.charge * c.cy;
+            }
+        }
+        if (quad.point) {
+            if (!quad.leaf) {
+                quad.point.x += Math.random() - .5;
+                quad.point.y += Math.random() - .5;
+            }
+            var k = alpha * charges[quad.point.index];
+            quad.charge += quad.pointCharge = k;
+            cx += k * quad.point.x;
+            cy += k * quad.point.y;
+        }
+        quad.cx = cx / quad.charge;
+        quad.cy = cy / quad.charge;
+    }
+    var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
+    d3.layout.hierarchy = function() {
+        var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
+        function hierarchy(root) {
+            var stack = [ root ], nodes = [], node;
+            root.depth = 0;
+            while ((node = stack.pop()) != null) {
+                nodes.push(node);
+                if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) {
+                    var n, childs, child;
+                    while (--n >= 0) {
+                        stack.push(child = childs[n]);
+                        child.parent = node;
+                        child.depth = node.depth + 1;
+                    }
+                    if (value) node.value = 0;
+                    node.children = childs;
+                } else {
+                    if (value) node.value = +value.call(hierarchy, node, node.depth) || 0;
+                    delete node.children;
+                }
+            }
+            d3_layout_hierarchyVisitAfter(root, function(node) {
+                var childs, parent;
+                if (sort && (childs = node.children)) childs.sort(sort);
+                if (value && (parent = node.parent)) parent.value += node.value;
+            });
+            return nodes;
+        }
+        hierarchy.sort = function(x) {
+            if (!arguments.length) return sort;
+            sort = x;
+            return hierarchy;
+        };
+        hierarchy.children = function(x) {
+            if (!arguments.length) return children;
+            children = x;
+            return hierarchy;
+        };
+        hierarchy.value = function(x) {
+            if (!arguments.length) return value;
+            value = x;
+            return hierarchy;
+        };
+        hierarchy.revalue = function(root) {
+            if (value) {
+                d3_layout_hierarchyVisitBefore(root, function(node) {
+                    if (node.children) node.value = 0;
+                });
+                d3_layout_hierarchyVisitAfter(root, function(node) {
+                    var parent;
+                    if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0;
+                    if (parent = node.parent) parent.value += node.value;
+                });
+            }
+            return root;
+        };
+        return hierarchy;
+    };
+    function d3_layout_hierarchyRebind(object, hierarchy) {
+        d3.rebind(object, hierarchy, "sort", "children", "value");
+        object.nodes = object;
+        object.links = d3_layout_hierarchyLinks;
+        return object;
+    }
+    function d3_layout_hierarchyVisitBefore(node, callback) {
+        var nodes = [ node ];
+        while ((node = nodes.pop()) != null) {
+            callback(node);
+            if ((children = node.children) && (n = children.length)) {
+                var n, children;
+                while (--n >= 0) nodes.push(children[n]);
+            }
+        }
+    }
+    function d3_layout_hierarchyVisitAfter(node, callback) {
+        var nodes = [ node ], nodes2 = [];
+        while ((node = nodes.pop()) != null) {
+            nodes2.push(node);
+            if ((children = node.children) && (n = children.length)) {
+                var i = -1, n, children;
+                while (++i < n) nodes.push(children[i]);
+            }
+        }
+        while ((node = nodes2.pop()) != null) {
+            callback(node);
+        }
+    }
+    function d3_layout_hierarchyChildren(d) {
+        return d.children;
+    }
+    function d3_layout_hierarchyValue(d) {
+        return d.value;
+    }
+    function d3_layout_hierarchySort(a, b) {
+        return b.value - a.value;
+    }
+    function d3_layout_hierarchyLinks(nodes) {
+        return d3.merge(nodes.map(function(parent) {
+            return (parent.children || []).map(function(child) {
+                return {
+                    source: parent,
+                    target: child
+                };
+            });
+        }));
+    }
+    d3.layout.partition = function() {
+        var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
+        function position(node, x, dx, dy) {
+            var children = node.children;
+            node.x = x;
+            node.y = node.depth * dy;
+            node.dx = dx;
+            node.dy = dy;
+            if (children && (n = children.length)) {
+                var i = -1, n, c, d;
+                dx = node.value ? dx / node.value : 0;
+                while (++i < n) {
+                    position(c = children[i], x, d = c.value * dx, dy);
+                    x += d;
+                }
+            }
+        }
+        function depth(node) {
+            var children = node.children, d = 0;
+            if (children && (n = children.length)) {
+                var i = -1, n;
+                while (++i < n) d = Math.max(d, depth(children[i]));
+            }
+            return 1 + d;
+        }
+        function partition(d, i) {
+            var nodes = hierarchy.call(this, d, i);
+            position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
+            return nodes;
+        }
+        partition.size = function(x) {
+            if (!arguments.length) return size;
+            size = x;
+            return partition;
+        };
+        return d3_layout_hierarchyRebind(partition, hierarchy);
+    };
+    d3.layout.pie = function() {
+        var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0;
+        function pie(data) {
+            var n = data.length, values = data.map(function(d, i) {
+                return +value.call(pie, d, i);
+            }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), k = (da - n * pa) / d3.sum(values), index = d3.range(n), arcs = [], v;
+            if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
+                return values[j] - values[i];
+            } : function(i, j) {
+                return sort(data[i], data[j]);
+            });
+            index.forEach(function(i) {
+                arcs[i] = {
+                    data: data[i],
+                    value: v = values[i],
+                    startAngle: a,
+                    endAngle: a += v * k + pa,
+                    padAngle: p
+                };
+            });
+            return arcs;
+        }
+        pie.value = function(_) {
+            if (!arguments.length) return value;
+            value = _;
+            return pie;
+        };
+        pie.sort = function(_) {
+            if (!arguments.length) return sort;
+            sort = _;
+            return pie;
+        };
+        pie.startAngle = function(_) {
+            if (!arguments.length) return startAngle;
+            startAngle = _;
+            return pie;
+        };
+        pie.endAngle = function(_) {
+            if (!arguments.length) return endAngle;
+            endAngle = _;
+            return pie;
+        };
+        pie.padAngle = function(_) {
+            if (!arguments.length) return padAngle;
+            padAngle = _;
+            return pie;
+        };
+        return pie;
+    };
+    var d3_layout_pieSortByValue = {};
+    d3.layout.stack = function() {
+        var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
+        function stack(data, index) {
+            if (!(n = data.length)) return data;
+            var series = data.map(function(d, i) {
+                return values.call(stack, d, i);
+            });
+            var points = series.map(function(d) {
+                return d.map(function(v, i) {
+                    return [ x.call(stack, v, i), y.call(stack, v, i) ];
+                });
+            });
+            var orders = order.call(stack, points, index);
+            series = d3.permute(series, orders);
+            points = d3.permute(points, orders);
+            var offsets = offset.call(stack, points, index);
+            var m = series[0].length, n, i, j, o;
+            for (j = 0; j < m; ++j) {
+                out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
+                for (i = 1; i < n; ++i) {
+                    out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
+                }
+            }
+            return data;
+        }
+        stack.values = function(x) {
+            if (!arguments.length) return values;
+            values = x;
+            return stack;
+        };
+        stack.order = function(x) {
+            if (!arguments.length) return order;
+            order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
+            return stack;
+        };
+        stack.offset = function(x) {
+            if (!arguments.length) return offset;
+            offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
+            return stack;
+        };
+        stack.x = function(z) {
+            if (!arguments.length) return x;
+            x = z;
+            return stack;
+        };
+        stack.y = function(z) {
+            if (!arguments.length) return y;
+            y = z;
+            return stack;
+        };
+        stack.out = function(z) {
+            if (!arguments.length) return out;
+            out = z;
+            return stack;
+        };
+        return stack;
+    };
+    function d3_layout_stackX(d) {
+        return d.x;
+    }
+    function d3_layout_stackY(d) {
+        return d.y;
+    }
+    function d3_layout_stackOut(d, y0, y) {
+        d.y0 = y0;
+        d.y = y;
+    }
+    var d3_layout_stackOrders = d3.map({
+        "inside-out": function(data) {
+            var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) {
+                return max[a] - max[b];
+            }), top = 0, bottom = 0, tops = [], bottoms = [];
+            for (i = 0; i < n; ++i) {
+                j = index[i];
+                if (top < bottom) {
+                    top += sums[j];
+                    tops.push(j);
+                } else {
+                    bottom += sums[j];
+                    bottoms.push(j);
+                }
+            }
+            return bottoms.reverse().concat(tops);
+        },
+        reverse: function(data) {
+            return d3.range(data.length).reverse();
+        },
+        "default": d3_layout_stackOrderDefault
+    });
+    var d3_layout_stackOffsets = d3.map({
+        silhouette: function(data) {
+            var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
+            for (j = 0; j < m; ++j) {
+                for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
+                if (o > max) max = o;
+                sums.push(o);
+            }
+            for (j = 0; j < m; ++j) {
+                y0[j] = (max - sums[j]) / 2;
+            }
+            return y0;
+        },
+        wiggle: function(data) {
+            var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
+            y0[0] = o = o0 = 0;
+            for (j = 1; j < m; ++j) {
+                for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
+                for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
+                    for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
+                        s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
+                    }
+                    s2 += s3 * data[i][j][1];
+                }
+                y0[j] = o -= s1 ? s2 / s1 * dx : 0;
+                if (o < o0) o0 = o;
+            }
+            for (j = 0; j < m; ++j) y0[j] -= o0;
+            return y0;
+        },
+        expand: function(data) {
+            var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
+            for (j = 0; j < m; ++j) {
+                for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
+                if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k;
+            }
+            for (j = 0; j < m; ++j) y0[j] = 0;
+            return y0;
+        },
+        zero: d3_layout_stackOffsetZero
+    });
+    function d3_layout_stackOrderDefault(data) {
+        return d3.range(data.length);
+    }
+    function d3_layout_stackOffsetZero(data) {
+        var j = -1, m = data[0].length, y0 = [];
+        while (++j < m) y0[j] = 0;
+        return y0;
+    }
+    function d3_layout_stackMaxIndex(array) {
+        var i = 1, j = 0, v = array[0][1], k, n = array.length;
+        for (;i < n; ++i) {
+            if ((k = array[i][1]) > v) {
+                j = i;
+                v = k;
+            }
+        }
+        return j;
+    }
+    function d3_layout_stackReduceSum(d) {
+        return d.reduce(d3_layout_stackSum, 0);
+    }
+    function d3_layout_stackSum(p, d) {
+        return p + d[1];
+    }
+    d3.layout.histogram = function() {
+        var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
+        function histogram(data, i) {
+            var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x;
+            while (++i < m) {
+                bin = bins[i] = [];
+                bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
+                bin.y = 0;
+            }
+            if (m > 0) {
+                i = -1;
+                while (++i < n) {
+                    x = values[i];
+                    if (x >= range[0] && x <= range[1]) {
+                        bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
+                        bin.y += k;
+                        bin.push(data[i]);
+                    }
+                }
+            }
+            return bins;
+        }
+        histogram.value = function(x) {
+            if (!arguments.length) return valuer;
+            valuer = x;
+            return histogram;
+        };
+        histogram.range = function(x) {
+            if (!arguments.length) return ranger;
+            ranger = d3_functor(x);
+            return histogram;
+        };
+        histogram.bins = function(x) {
+            if (!arguments.length) return binner;
+            binner = typeof x === "number" ? function(range) {
+                return d3_layout_histogramBinFixed(range, x);
+            } : d3_functor(x);
+            return histogram;
+        };
+        histogram.frequency = function(x) {
+            if (!arguments.length) return frequency;
+            frequency = !!x;
+            return histogram;
+        };
+        return histogram;
+    };
+    function d3_layout_histogramBinSturges(range, values) {
+        return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
+    }
+    function d3_layout_histogramBinFixed(range, n) {
+        var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
+        while (++x <= n) f[x] = m * x + b;
+        return f;
+    }
+    function d3_layout_histogramRange(values) {
+        return [ d3.min(values), d3.max(values) ];
+    }
+    d3.layout.pack = function() {
+        var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
+        function pack(d, i) {
+            var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() {
+                return radius;
+            };
+            root.x = root.y = 0;
+            d3_layout_hierarchyVisitAfter(root, function(d) {
+                d.r = +r(d.value);
+            });
+            d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
+            if (padding) {
+                var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
+                d3_layout_hierarchyVisitAfter(root, function(d) {
+                    d.r += dr;
+                });
+                d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
+                d3_layout_hierarchyVisitAfter(root, function(d) {
+                    d.r -= dr;
+                });
+            }
+            d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
+            return nodes;
+        }
+        pack.size = function(_) {
+            if (!arguments.length) return size;
+            size = _;
+            return pack;
+        };
+        pack.radius = function(_) {
+            if (!arguments.length) return radius;
+            radius = _ == null || typeof _ === "function" ? _ : +_;
+            return pack;
+        };
+        pack.padding = function(_) {
+            if (!arguments.length) return padding;
+            padding = +_;
+            return pack;
+        };
+        return d3_layout_hierarchyRebind(pack, hierarchy);
+    };
+    function d3_layout_packSort(a, b) {
+        return a.value - b.value;
+    }
+    function d3_layout_packInsert(a, b) {
+        var c = a._pack_next;
+        a._pack_next = b;
+        b._pack_prev = a;
+        b._pack_next = c;
+        c._pack_prev = b;
+    }
+    function d3_layout_packSplice(a, b) {
+        a._pack_next = b;
+        b._pack_prev = a;
+    }
+    function d3_layout_packIntersects(a, b) {
+        var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
+        return .999 * dr * dr > dx * dx + dy * dy;
+    }
+    function d3_layout_packSiblings(node) {
+        if (!(nodes = node.children) || !(n = nodes.length)) return;
+        var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
+        function bound(node) {
+            xMin = Math.min(node.x - node.r, xMin);
+            xMax = Math.max(node.x + node.r, xMax);
+            yMin = Math.min(node.y - node.r, yMin);
+            yMax = Math.max(node.y + node.r, yMax);
+        }
+        nodes.forEach(d3_layout_packLink);
+        a = nodes[0];
+        a.x = -a.r;
+        a.y = 0;
+        bound(a);
+        if (n > 1) {
+            b = nodes[1];
+            b.x = b.r;
+            b.y = 0;
+            bound(b);
+            if (n > 2) {
+                c = nodes[2];
+                d3_layout_packPlace(a, b, c);
+                bound(c);
+                d3_layout_packInsert(a, c);
+                a._pack_prev = c;
+                d3_layout_packInsert(c, b);
+                b = a._pack_next;
+                for (i = 3; i < n; i++) {
+                    d3_layout_packPlace(a, b, c = nodes[i]);
+                    var isect = 0, s1 = 1, s2 = 1;
+                    for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
+                        if (d3_layout_packIntersects(j, c)) {
+                            isect = 1;
+                            break;
+                        }
+                    }
+                    if (isect == 1) {
+                        for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
+                            if (d3_layout_packIntersects(k, c)) {
+                                break;
+                            }
+                        }
+                    }
+                    if (isect) {
+                        if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
+                        i--;
+                    } else {
+                        d3_layout_packInsert(a, c);
+                        b = c;
+                        bound(c);
+                    }
+                }
+            }
+        }
+        var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
+        for (i = 0; i < n; i++) {
+            c = nodes[i];
+            c.x -= cx;
+            c.y -= cy;
+            cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
+        }
+        node.r = cr;
+        nodes.forEach(d3_layout_packUnlink);
+    }
+    function d3_layout_packLink(node) {
+        node._pack_next = node._pack_prev = node;
+    }
+    function d3_layout_packUnlink(node) {
+        delete node._pack_next;
+        delete node._pack_prev;
+    }
+    function d3_layout_packTransform(node, x, y, k) {
+        var children = node.children;
+        node.x = x += k * node.x;
+        node.y = y += k * node.y;
+        node.r *= k;
+        if (children) {
+            var i = -1, n = children.length;
+            while (++i < n) d3_layout_packTransform(children[i], x, y, k);
+        }
+    }
+    function d3_layout_packPlace(a, b, c) {
+        var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
+        if (db && (dx || dy)) {
+            var da = b.r + c.r, dc = dx * dx + dy * dy;
+            da *= da;
+            db *= db;
+            var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
+            c.x = a.x + x * dx + y * dy;
+            c.y = a.y + x * dy - y * dx;
+        } else {
+            c.x = a.x + db;
+            c.y = a.y;
+        }
+    }
+    d3.layout.tree = function() {
+        var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null;
+        function tree(d, i) {
+            var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0);
+            d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z;
+            d3_layout_hierarchyVisitBefore(root1, secondWalk);
+            if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else {
+                var left = root0, right = root0, bottom = root0;
+                d3_layout_hierarchyVisitBefore(root0, function(node) {
+                    if (node.x < left.x) left = node;
+                    if (node.x > right.x) right = node;
+                    if (node.depth > bottom.depth) bottom = node;
+                });
+                var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1);
+                d3_layout_hierarchyVisitBefore(root0, function(node) {
+                    node.x = (node.x + tx) * kx;
+                    node.y = node.depth * ky;
+                });
+            }
+            return nodes;
+        }
+        function wrapTree(root0) {
+            var root1 = {
+                A: null,
+                children: [ root0 ]
+            }, queue = [ root1 ], node1;
+            while ((node1 = queue.pop()) != null) {
+                for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) {
+                    queue.push((children[i] = child = {
+                        _: children[i],
+                        parent: node1,
+                        children: (child = children[i].children) && child.slice() || [],
+                        A: null,
+                        a: null,
+                        z: 0,
+                        m: 0,
+                        c: 0,
+                        s: 0,
+                        t: null,
+                        i: i
+                    }).a = child);
+                }
+            }
+            return root1.children[0];
+        }
+        function firstWalk(v) {
+            var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null;
+            if (children.length) {
+                d3_layout_treeShift(v);
+                var midpoint = (children[0].z + children[children.length - 1].z) / 2;
+                if (w) {
+                    v.z = w.z + separation(v._, w._);
+                    v.m = v.z - midpoint;
+                } else {
+                    v.z = midpoint;
+                }
+            } else if (w) {
+                v.z = w.z + separation(v._, w._);
+            }
+            v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
+        }
+        function secondWalk(v) {
+            v._.x = v.z + v.parent.m;
+            v.m += v.parent.m;
+        }
+        function apportion(v, w, ancestor) {
+            if (w) {
+                var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift;
+                while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
+                    vom = d3_layout_treeLeft(vom);
+                    vop = d3_layout_treeRight(vop);
+                    vop.a = v;
+                    shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
+                    if (shift > 0) {
+                        d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift);
+                        sip += shift;
+                        sop += shift;
+                    }
+                    sim += vim.m;
+                    sip += vip.m;
+                    som += vom.m;
+                    sop += vop.m;
+                }
+                if (vim && !d3_layout_treeRight(vop)) {
+                    vop.t = vim;
+                    vop.m += sim - sop;
+                }
+                if (vip && !d3_layout_treeLeft(vom)) {
+                    vom.t = vip;
+                    vom.m += sip - som;
+                    ancestor = v;
+                }
+            }
+            return ancestor;
+        }
+        function sizeNode(node) {
+            node.x *= size[0];
+            node.y = node.depth * size[1];
+        }
+        tree.separation = function(x) {
+            if (!arguments.length) return separation;
+            separation = x;
+            return tree;
+        };
+        tree.size = function(x) {
+            if (!arguments.length) return nodeSize ? null : size;
+            nodeSize = (size = x) == null ? sizeNode : null;
+            return tree;
+        };
+        tree.nodeSize = function(x) {
+            if (!arguments.length) return nodeSize ? size : null;
+            nodeSize = (size = x) == null ? null : sizeNode;
+            return tree;
+        };
+        return d3_layout_hierarchyRebind(tree, hierarchy);
+    };
+    function d3_layout_treeSeparation(a, b) {
+        return a.parent == b.parent ? 1 : 2;
+    }
+    function d3_layout_treeLeft(v) {
+        var children = v.children;
+        return children.length ? children[0] : v.t;
+    }
+    function d3_layout_treeRight(v) {
+        var children = v.children, n;
+        return (n = children.length) ? children[n - 1] : v.t;
+    }
+    function d3_layout_treeMove(wm, wp, shift) {
+        var change = shift / (wp.i - wm.i);
+        wp.c -= change;
+        wp.s += shift;
+        wm.c += change;
+        wp.z += shift;
+        wp.m += shift;
+    }
+    function d3_layout_treeShift(v) {
+        var shift = 0, change = 0, children = v.children, i = children.length, w;
+        while (--i >= 0) {
+            w = children[i];
+            w.z += shift;
+            w.m += shift;
+            shift += w.s + (change += w.c);
+        }
+    }
+    function d3_layout_treeAncestor(vim, v, ancestor) {
+        return vim.a.parent === v.parent ? vim.a : ancestor;
+    }
+    d3.layout.cluster = function() {
+        var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
+        function cluster(d, i) {
+            var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
+            d3_layout_hierarchyVisitAfter(root, function(node) {
+                var children = node.children;
+                if (children && children.length) {
+                    node.x = d3_layout_clusterX(children);
+                    node.y = d3_layout_clusterY(children);
+                } else {
+                    node.x = previousNode ? x += separation(node, previousNode) : 0;
+                    node.y = 0;
+                    previousNode = node;
+                }
+            });
+            var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2;
+            d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) {
+                node.x = (node.x - root.x) * size[0];
+                node.y = (root.y - node.y) * size[1];
+            } : function(node) {
+                node.x = (node.x - x0) / (x1 - x0) * size[0];
+                node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
+            });
+            return nodes;
+        }
+        cluster.separation = function(x) {
+            if (!arguments.length) return separation;
+            separation = x;
+            return cluster;
+        };
+        cluster.size = function(x) {
+            if (!arguments.length) return nodeSize ? null : size;
+            nodeSize = (size = x) == null;
+            return cluster;
+        };
+        cluster.nodeSize = function(x) {
+            if (!arguments.length) return nodeSize ? size : null;
+            nodeSize = (size = x) != null;
+            return cluster;
+        };
+        return d3_layout_hierarchyRebind(cluster, hierarchy);
+    };
+    function d3_layout_clusterY(children) {
+        return 1 + d3.max(children, function(child) {
+                return child.y;
+            });
+    }
+    function d3_layout_clusterX(children) {
+        return children.reduce(function(x, child) {
+                return x + child.x;
+            }, 0) / children.length;
+    }
+    function d3_layout_clusterLeft(node) {
+        var children = node.children;
+        return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
+    }
+    function d3_layout_clusterRight(node) {
+        var children = node.children, n;
+        return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
+    }
+    d3.layout.treemap = function() {
+        var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5));
+        function scale(children, k) {
+            var i = -1, n = children.length, child, area;
+            while (++i < n) {
+                area = (child = children[i]).value * (k < 0 ? 0 : k);
+                child.area = isNaN(area) || area <= 0 ? 0 : area;
+            }
+        }
+        function squarify(node) {
+            var children = node.children;
+            if (children && children.length) {
+                var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n;
+                scale(remaining, rect.dx * rect.dy / node.value);
+                row.area = 0;
+                while ((n = remaining.length) > 0) {
+                    row.push(child = remaining[n - 1]);
+                    row.area += child.area;
+                    if (mode !== "squarify" || (score = worst(row, u)) <= best) {
+                        remaining.pop();
+                        best = score;
+                    } else {
+                        row.area -= row.pop().area;
+                        position(row, u, rect, false);
+                        u = Math.min(rect.dx, rect.dy);
+                        row.length = row.area = 0;
+                        best = Infinity;
+                    }
+                }
+                if (row.length) {
+                    position(row, u, rect, true);
+                    row.length = row.area = 0;
+                }
+                children.forEach(squarify);
+            }
+        }
+        function stickify(node) {
+            var children = node.children;
+            if (children && children.length) {
+                var rect = pad(node), remaining = children.slice(), child, row = [];
+                scale(remaining, rect.dx * rect.dy / node.value);
+                row.area = 0;
+                while (child = remaining.pop()) {
+                    row.push(child);
+                    row.area += child.area;
+                    if (child.z != null) {
+                        position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
+                        row.length = row.area = 0;
+                    }
+                }
+                children.forEach(stickify);
+            }
+        }
+        function worst(row, u) {
+            var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
+            while (++i < n) {
+                if (!(r = row[i].area)) continue;
+                if (r < rmin) rmin = r;
+                if (r > rmax) rmax = r;
+            }
+            s *= s;
+            u *= u;
+            return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
+        }
+        function position(row, u, rect, flush) {
+            var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
+            if (u == rect.dx) {
+                if (flush || v > rect.dy) v = rect.dy;
+                while (++i < n) {
+                    o = row[i];
+                    o.x = x;
+                    o.y = y;
+                    o.dy = v;
+                    x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
+                }
+                o.z = true;
+                o.dx += rect.x + rect.dx - x;
+                rect.y += v;
+                rect.dy -= v;
+            } else {
+                if (flush || v > rect.dx) v = rect.dx;
+                while (++i < n) {
+                    o = row[i];
+                    o.x = x;
+                    o.y = y;
+                    o.dx = v;
+                    y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
+                }
+                o.z = false;
+                o.dy += rect.y + rect.dy - y;
+                rect.x += v;
+                rect.dx -= v;
+            }
+        }
+        function treemap(d) {
+            var nodes = stickies || hierarchy(d), root = nodes[0];
+            root.x = 0;
+            root.y = 0;
+            root.dx = size[0];
+            root.dy = size[1];
+            if (stickies) hierarchy.revalue(root);
+            scale([ root ], root.dx * root.dy / root.value);
+            (stickies ? stickify : squarify)(root);
+            if (sticky) stickies = nodes;
+            return nodes;
+        }
+        treemap.size = function(x) {
+            if (!arguments.length) return size;
+            size = x;
+            return treemap;
+        };
+        treemap.padding = function(x) {
+            if (!arguments.length) return padding;
+            function padFunction(node) {
+                var p = x.call(treemap, node, node.depth);
+                return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
+            }
+            function padConstant(node) {
+                return d3_layout_treemapPad(node, x);
+            }
+            var type;
+            pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ],
+                padConstant) : padConstant;
+            return treemap;
+        };
+        treemap.round = function(x) {
+            if (!arguments.length) return round != Number;
+            round = x ? Math.round : Number;
+            return treemap;
+        };
+        treemap.sticky = function(x) {
+            if (!arguments.length) return sticky;
+            sticky = x;
+            stickies = null;
+            return treemap;
+        };
+        treemap.ratio = function(x) {
+            if (!arguments.length) return ratio;
+            ratio = x;
+            return treemap;
+        };
+        treemap.mode = function(x) {
+            if (!arguments.length) return mode;
+            mode = x + "";
+            return treemap;
+        };
+        return d3_layout_hierarchyRebind(treemap, hierarchy);
+    };
+    function d3_layout_treemapPadNull(node) {
+        return {
+            x: node.x,
+            y: node.y,
+            dx: node.dx,
+            dy: node.dy
+        };
+    }
+    function d3_layout_treemapPad(node, padding) {
+        var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2];
+        if (dx < 0) {
+            x += dx / 2;
+            dx = 0;
+        }
+        if (dy < 0) {
+            y += dy / 2;
+            dy = 0;
+        }
+        return {
+            x: x,
+            y: y,
+            dx: dx,
+            dy: dy
+        };
+    }
+    d3.random = {
+        normal: function(µ, σ) {
+            var n = arguments.length;
+            if (n < 2) σ = 1;
+            if (n < 1) µ = 0;
+            return function() {
+                var x, y, r;
+                do {
+                    x = Math.random() * 2 - 1;
+                    y = Math.random() * 2 - 1;
+                    r = x * x + y * y;
+                } while (!r || r > 1);
+                return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r);
+            };
+        },
+        logNormal: function() {
+            var random = d3.random.normal.apply(d3, arguments);
+            return function() {
+                return Math.exp(random());
+            };
+        },
+        bates: function(m) {
+            var random = d3.random.irwinHall(m);
+            return function() {
+                return random() / m;
+            };
+        },
+        irwinHall: function(m) {
+            return function() {
+                for (var s = 0, j = 0; j < m; j++) s += Math.random();
+                return s;
+            };
+        }
+    };
+    d3.scale = {};
+    function d3_scaleExtent(domain) {
+        var start = domain[0], stop = domain[domain.length - 1];
+        return start < stop ? [ start, stop ] : [ stop, start ];
+    }
+    function d3_scaleRange(scale) {
+        return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
+    }
+    function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
+        var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
+        return function(x) {
+            return i(u(x));
+        };
+    }
+    function d3_scale_nice(domain, nice) {
+        var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
+        if (x1 < x0) {
+            dx = i0, i0 = i1, i1 = dx;
+            dx = x0, x0 = x1, x1 = dx;
+        }
+        domain[i0] = nice.floor(x0);
+        domain[i1] = nice.ceil(x1);
+        return domain;
+    }
+    function d3_scale_niceStep(step) {
+        return step ? {
+            floor: function(x) {
+                return Math.floor(x / step) * step;
+            },
+            ceil: function(x) {
+                return Math.ceil(x / step) * step;
+            }
+        } : d3_scale_niceIdentity;
+    }
+    var d3_scale_niceIdentity = {
+        floor: d3_identity,
+        ceil: d3_identity
+    };
+    function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
+        var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
+        if (domain[k] < domain[0]) {
+            domain = domain.slice().reverse();
+            range = range.slice().reverse();
+        }
+        while (++j <= k) {
+            u.push(uninterpolate(domain[j - 1], domain[j]));
+            i.push(interpolate(range[j - 1], range[j]));
+        }
+        return function(x) {
+            var j = d3.bisect(domain, x, 1, k) - 1;
+            return i[j](u[j](x));
+        };
+    }
+    d3.scale.linear = function() {
+        return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
+    };
+    function d3_scale_linear(domain, range, interpolate, clamp) {
+        var output, input;
+        function rescale() {
+            var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
+            output = linear(domain, range, uninterpolate, interpolate);
+            input = linear(range, domain, uninterpolate, d3_interpolate);
+            return scale;
+        }
+        function scale(x) {
+            return output(x);
+        }
+        scale.invert = function(y) {
+            return input(y);
+        };
+        scale.domain = function(x) {
+            if (!arguments.length) return domain;
+            domain = x.map(Number);
+            return rescale();
+        };
+        scale.range = function(x) {
+            if (!arguments.length) return range;
+            range = x;
+            return rescale();
+        };
+        scale.rangeRound = function(x) {
+            return scale.range(x).interpolate(d3_interpolateRound);
+        };
+        scale.clamp = function(x) {
+            if (!arguments.length) return clamp;
+            clamp = x;
+            return rescale();
+        };
+        scale.interpolate = function(x) {
+            if (!arguments.length) return interpolate;
+            interpolate = x;
+            return rescale();
+        };
+        scale.ticks = function(m) {
+            return d3_scale_linearTicks(domain, m);
+        };
+        scale.tickFormat = function(m, format) {
+            return d3_scale_linearTickFormat(domain, m, format);
+        };
+        scale.nice = function(m) {
+            d3_scale_linearNice(domain, m);
+            return rescale();
+        };
+        scale.copy = function() {
+            return d3_scale_linear(domain, range, interpolate, clamp);
+        };
+        return rescale();
+    }
+    function d3_scale_linearRebind(scale, linear) {
+        return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
+    }
+    function d3_scale_linearNice(domain, m) {
+        return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
+    }
+    function d3_scale_linearTickRange(domain, m) {
+        if (m == null) m = 10;
+        var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step;
+        if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
+        extent[0] = Math.ceil(extent[0] / step) * step;
+        extent[1] = Math.floor(extent[1] / step) * step + step * .5;
+        extent[2] = step;
+        return extent;
+    }
+    function d3_scale_linearTicks(domain, m) {
+        return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
+    }
+    function d3_scale_linearTickFormat(domain, m, format) {
+        var range = d3_scale_linearTickRange(domain, m);
+        if (format) {
+            var match = d3_format_re.exec(format);
+            match.shift();
+            if (match[8] === "s") {
+                var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1])));
+                if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2]));
+                match[8] = "f";
+                format = d3.format(match.join(""));
+                return function(d) {
+                    return format(prefix.scale(d)) + prefix.symbol;
+                };
+            }
+            if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range);
+            format = match.join("");
+        } else {
+            format = ",." + d3_scale_linearPrecision(range[2]) + "f";
+        }
+        return d3.format(format);
+    }
+    var d3_scale_linearFormatSignificant = {
+        s: 1,
+        g: 1,
+        p: 1,
+        r: 1,
+        e: 1
+    };
+    function d3_scale_linearPrecision(value) {
+        return -Math.floor(Math.log(value) / Math.LN10 + .01);
+    }
+    function d3_scale_linearFormatPrecision(type, range) {
+        var p = d3_scale_linearPrecision(range[2]);
+        return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2;
+    }
+    d3.scale.log = function() {
+        return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
+    };
+    function d3_scale_log(linear, base, positive, domain) {
+        function log(x) {
+            return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
+        }
+        function pow(x) {
+            return positive ? Math.pow(base, x) : -Math.pow(base, -x);
+        }
+        function scale(x) {
+            return linear(log(x));
+        }
+        scale.invert = function(x) {
+            return pow(linear.invert(x));
+        };
+        scale.domain = function(x) {
+            if (!arguments.length) return domain;
+            positive = x[0] >= 0;
+            linear.domain((domain = x.map(Number)).map(log));
+            return scale;
+        };
+        scale.base = function(_) {
+            if (!arguments.length) return base;
+            base = +_;
+            linear.domain(domain.map(log));
+            return scale;
+        };
+        scale.nice = function() {
+            var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
+            linear.domain(niced);
+            domain = niced.map(pow);
+            return scale;
+        };
+        scale.ticks = function() {
+            var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base;
+            if (isFinite(j - i)) {
+                if (positive) {
+                    for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
+                    ticks.push(pow(i));
+                } else {
+                    ticks.push(pow(i));
+                    for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
+                }
+                for (i = 0; ticks[i] < u; i++) {}
+                for (j = ticks.length; ticks[j - 1] > v; j--) {}
+                ticks = ticks.slice(i, j);
+            }
+            return ticks;
+        };
+        scale.tickFormat = function(n, format) {
+            if (!arguments.length) return d3_scale_logFormat;
+            if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
+            var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12,
+                Math.floor), e;
+            return function(d) {
+                return d / pow(f(log(d) + e)) <= k ? format(d) : "";
+            };
+        };
+        scale.copy = function() {
+            return d3_scale_log(linear.copy(), base, positive, domain);
+        };
+        return d3_scale_linearRebind(scale, linear);
+    }
+    var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
+        floor: function(x) {
+            return -Math.ceil(-x);
+        },
+        ceil: function(x) {
+            return -Math.floor(-x);
+        }
+    };
+    d3.scale.pow = function() {
+        return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
+    };
+    function d3_scale_pow(linear, exponent, domain) {
+        var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
+        function scale(x) {
+            return linear(powp(x));
+        }
+        scale.invert = function(x) {
+            return powb(linear.invert(x));
+        };
+        scale.domain = function(x) {
+            if (!arguments.length) return domain;
+            linear.domain((domain = x.map(Number)).map(powp));
+            return scale;
+        };
+        scale.ticks = function(m) {
+            return d3_scale_linearTicks(domain, m);
+        };
+        scale.tickFormat = function(m, format) {
+            return d3_scale_linearTickFormat(domain, m, format);
+        };
+        scale.nice = function(m) {
+            return scale.domain(d3_scale_linearNice(domain, m));
+        };
+        scale.exponent = function(x) {
+            if (!arguments.length) return exponent;
+            powp = d3_scale_powPow(exponent = x);
+            powb = d3_scale_powPow(1 / exponent);
+            linear.domain(domain.map(powp));
+            return scale;
+        };
+        scale.copy = function() {
+            return d3_scale_pow(linear.copy(), exponent, domain);
+        };
+        return d3_scale_linearRebind(scale, linear);
+    }
+    function d3_scale_powPow(e) {
+        return function(x) {
+            return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
+        };
+    }
+    d3.scale.sqrt = function() {
+        return d3.scale.pow().exponent(.5);
+    };
+    d3.scale.ordinal = function() {
+        return d3_scale_ordinal([], {
+            t: "range",
+            a: [ [] ]
+        });
+    };
+    function d3_scale_ordinal(domain, ranger) {
+        var index, range, rangeBand;
+        function scale(x) {
+            return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length];
+        }
+        function steps(start, step) {
+            return d3.range(domain.length).map(function(i) {
+                return start + step * i;
+            });
+        }
+        scale.domain = function(x) {
+            if (!arguments.length) return domain;
+            domain = [];
+            index = new d3_Map();
+            var i = -1, n = x.length, xi;
+            while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
+            return scale[ranger.t].apply(scale, ranger.a);
+        };
+        scale.range = function(x) {
+            if (!arguments.length) return range;
+            range = x;
+            rangeBand = 0;
+            ranger = {
+                t: "range",
+                a: arguments
+            };
+            return scale;
+        };
+        scale.rangePoints = function(x, padding) {
+            if (arguments.length < 2) padding = 0;
+            var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2,
+                0) : (stop - start) / (domain.length - 1 + padding);
+            range = steps(start + step * padding / 2, step);
+            rangeBand = 0;
+            ranger = {
+                t: "rangePoints",
+                a: arguments
+            };
+            return scale;
+        };
+        scale.rangeRoundPoints = function(x, padding) {
+            if (arguments.length < 2) padding = 0;
+            var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2),
+                0) : (stop - start) / (domain.length - 1 + padding) | 0;
+            range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step);
+            rangeBand = 0;
+            ranger = {
+                t: "rangeRoundPoints",
+                a: arguments
+            };
+            return scale;
+        };
+        scale.rangeBands = function(x, padding, outerPadding) {
+            if (arguments.length < 2) padding = 0;
+            if (arguments.length < 3) outerPadding = padding;
+            var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
+            range = steps(start + step * outerPadding, step);
+            if (reverse) range.reverse();
+            rangeBand = step * (1 - padding);
+            ranger = {
+                t: "rangeBands",
+                a: arguments
+            };
+            return scale;
+        };
+        scale.rangeRoundBands = function(x, padding, outerPadding) {
+            if (arguments.length < 2) padding = 0;
+            if (arguments.length < 3) outerPadding = padding;
+            var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding));
+            range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step);
+            if (reverse) range.reverse();
+            rangeBand = Math.round(step * (1 - padding));
+            ranger = {
+                t: "rangeRoundBands",
+                a: arguments
+            };
+            return scale;
+        };
+        scale.rangeBand = function() {
+            return rangeBand;
+        };
+        scale.rangeExtent = function() {
+            return d3_scaleExtent(ranger.a[0]);
+        };
+        scale.copy = function() {
+            return d3_scale_ordinal(domain, ranger);
+        };
+        return scale.domain(domain);
+    }
+    d3.scale.category10 = function() {
+        return d3.scale.ordinal().range(d3_category10);
+    };
+    d3.scale.category20 = function() {
+        return d3.scale.ordinal().range(d3_category20);
+    };
+    d3.scale.category20b = function() {
+        return d3.scale.ordinal().range(d3_category20b);
+    };
+    d3.scale.category20c = function() {
+        return d3.scale.ordinal().range(d3_category20c);
+    };
+    var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
+    var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString);
+    var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString);
+    var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString);
+    d3.scale.quantile = function() {
+        return d3_scale_quantile([], []);
+    };
+    function d3_scale_quantile(domain, range) {
+        var thresholds;
+        function rescale() {
+            var k = 0, q = range.length;
+            thresholds = [];
+            while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
+            return scale;
+        }
+        function scale(x) {
+            if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
+        }
+        scale.domain = function(x) {
+            if (!arguments.length) return domain;
+            domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending);
+            return rescale();
+        };
+        scale.range = function(x) {
+            if (!arguments.length) return range;
+            range = x;
+            return rescale();
+        };
+        scale.quantiles = function() {
+            return thresholds;
+        };
+        scale.invertExtent = function(y) {
+            y = range.indexOf(y);
+            return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
+        };
+        scale.copy = function() {
+            return d3_scale_quantile(domain, range);
+        };
+        return rescale();
+    }
+    d3.scale.quantize = function() {
+        return d3_scale_quantize(0, 1, [ 0, 1 ]);
+    };
+    function d3_scale_quantize(x0, x1, range) {
+        var kx, i;
+        function scale(x) {
+            return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
+        }
+        function rescale() {
+            kx = range.length / (x1 - x0);
+            i = range.length - 1;
+            return scale;
+        }
+        scale.domain = function(x) {
+            if (!arguments.length) return [ x0, x1 ];
+            x0 = +x[0];
+            x1 = +x[x.length - 1];
+            return rescale();
+        };
+        scale.range = function(x) {
+            if (!arguments.length) return range;
+            range = x;
+            return rescale();
+        };
+        scale.invertExtent = function(y) {
+            y = range.indexOf(y);
+            y = y < 0 ? NaN : y / kx + x0;
+            return [ y, y + 1 / kx ];
+        };
+        scale.copy = function() {
+            return d3_scale_quantize(x0, x1, range);
+        };
+        return rescale();
+    }
+    d3.scale.threshold = function() {
+        return d3_scale_threshold([ .5 ], [ 0, 1 ]);
+    };
+    function d3_scale_threshold(domain, range) {
+        function scale(x) {
+            if (x <= x) return range[d3.bisect(domain, x)];
+        }
+        scale.domain = function(_) {
+            if (!arguments.length) return domain;
+            domain = _;
+            return scale;
+        };
+        scale.range = function(_) {
+            if (!arguments.length) return range;
+            range = _;
+            return scale;
+        };
+        scale.invertExtent = function(y) {
+            y = range.indexOf(y);
+            return [ domain[y - 1], domain[y] ];
+        };
+        scale.copy = function() {
+            return d3_scale_threshold(domain, range);
+        };
+        return scale;
+    }
+    d3.scale.identity = function() {
+        return d3_scale_identity([ 0, 1 ]);
+    };
+    function d3_scale_identity(domain) {
+        function identity(x) {
+            return +x;
+        }
+        identity.invert = identity;
+        identity.domain = identity.range = function(x) {
+            if (!arguments.length) return domain;
+            domain = x.map(identity);
+            return identity;
+        };
+        identity.ticks = function(m) {
+            return d3_scale_linearTicks(domain, m);
+        };
+        identity.tickFormat = function(m, format) {
+            return d3_scale_linearTickFormat(domain, m, format);
+        };
+        identity.copy = function() {
+            return d3_scale_identity(domain);
+        };
+        return identity;
+    }
+    d3.svg = {};
+    function d3_zero() {
+        return 0;
+    }
+    d3.svg.arc = function() {
+        var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle;
+        function arc() {
+            var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfπ, a1 = endAngle.apply(this, arguments) - halfπ, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1;
+            if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
+            if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z";
+            var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = [];
+            if (ap = (+padAngle.apply(this, arguments) || 0) / 2) {
+                rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments);
+                if (!cw) p1 *= -1;
+                if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap));
+                if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap));
+            }
+            if (r1) {
+                x0 = r1 * Math.cos(a0 + p1);
+                y0 = r1 * Math.sin(a0 + p1);
+                x1 = r1 * Math.cos(a1 - p1);
+                y1 = r1 * Math.sin(a1 - p1);
+                var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1;
+                if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) {
+                    var h1 = (a0 + a1) / 2;
+                    x0 = r1 * Math.cos(h1);
+                    y0 = r1 * Math.sin(h1);
+                    x1 = y1 = null;
+                }
+            } else {
+                x0 = y0 = 0;
+            }
+            if (r0) {
+                x2 = r0 * Math.cos(a1 - p0);
+                y2 = r0 * Math.sin(a1 - p0);
+                x3 = r0 * Math.cos(a0 + p0);
+                y3 = r0 * Math.sin(a0 + p0);
+                var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1;
+                if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) {
+                    var h0 = (a0 + a1) / 2;
+                    x2 = r0 * Math.cos(h0);
+                    y2 = r0 * Math.sin(h0);
+                    x3 = y3 = null;
+                }
+            } else {
+                x2 = y2 = 0;
+            }
+            if ((rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) {
+                cr = r0 < r1 ^ cw ? 0 : 1;
+                var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]);
+                if (x1 != null) {
+                    var rc1 = Math.min(rc, (r1 - lc) / (kc + 1)), t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw);
+                    if (rc === rc1) {
+                        path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]);
+                    } else {
+                        path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]);
+                    }
+                } else {
+                    path.push("M", x0, ",", y0);
+                }
+                if (x3 != null) {
+                    var rc0 = Math.min(rc, (r0 - lc) / (kc - 1)), t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw);
+                    if (rc === rc0) {
+                        path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
+                    } else {
+                        path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
+                    }
+                } else {
+                    path.push("L", x2, ",", y2);
+                }
+            } else {
+                path.push("M", x0, ",", y0);
+                if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1);
+                path.push("L", x2, ",", y2);
+                if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3);
+            }
+            path.push("Z");
+            return path.join("");
+        }
+        function circleSegment(r1, cw) {
+            return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1;
+        }
+        arc.innerRadius = function(v) {
+            if (!arguments.length) return innerRadius;
+            innerRadius = d3_functor(v);
+            return arc;
+        };
+        arc.outerRadius = function(v) {
+            if (!arguments.length) return outerRadius;
+            outerRadius = d3_functor(v);
+            return arc;
+        };
+        arc.cornerRadius = function(v) {
+            if (!arguments.length) return cornerRadius;
+            cornerRadius = d3_functor(v);
+            return arc;
+        };
+        arc.padRadius = function(v) {
+            if (!arguments.length) return padRadius;
+            padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v);
+            return arc;
+        };
+        arc.startAngle = function(v) {
+            if (!arguments.length) return startAngle;
+            startAngle = d3_functor(v);
+            return arc;
+        };
+        arc.endAngle = function(v) {
+            if (!arguments.length) return endAngle;
+            endAngle = d3_functor(v);
+            return arc;
+        };
+        arc.padAngle = function(v) {
+            if (!arguments.length) return padAngle;
+            padAngle = d3_functor(v);
+            return arc;
+        };
+        arc.centroid = function() {
+            var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ;
+            return [ Math.cos(a) * r, Math.sin(a) * r ];
+        };
+        return arc;
+    };
+    var d3_svg_arcAuto = "auto";
+    function d3_svg_arcInnerRadius(d) {
+        return d.innerRadius;
+    }
+    function d3_svg_arcOuterRadius(d) {
+        return d.outerRadius;
+    }
+    function d3_svg_arcStartAngle(d) {
+        return d.startAngle;
+    }
+    function d3_svg_arcEndAngle(d) {
+        return d.endAngle;
+    }
+    function d3_svg_arcPadAngle(d) {
+        return d && d.padAngle;
+    }
+    function d3_svg_arcSweep(x0, y0, x1, y1) {
+        return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1;
+    }
+    function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) {
+        var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(r * r * d2 - D * D), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3;
+        if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
+        return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ];
+    }
+    function d3_svg_line(projection) {
+        var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
+        function line(data) {
+            var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
+            function segment() {
+                segments.push("M", interpolate(projection(points), tension));
+            }
+            while (++i < n) {
+                if (defined.call(this, d = data[i], i)) {
+                    points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
+                } else if (points.length) {
+                    segment();
+                    points = [];
+                }
+            }
+            if (points.length) segment();
+            return segments.length ? segments.join("") : null;
+        }
+        line.x = function(_) {
+            if (!arguments.length) return x;
+            x = _;
+            return line;
+        };
+        line.y = function(_) {
+            if (!arguments.length) return y;
+            y = _;
+            return line;
+        };
+        line.defined = function(_) {
+            if (!arguments.length) return defined;
+            defined = _;
+            return line;
+        };
+        line.interpolate = function(_) {
+            if (!arguments.length) return interpolateKey;
+            if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
+            return line;
+        };
+        line.tension = function(_) {
+            if (!arguments.length) return tension;
+            tension = _;
+            return line;
+        };
+        return line;
+    }
+    d3.svg.line = function() {
+        return d3_svg_line(d3_identity);
+    };
+    var d3_svg_lineInterpolators = d3.map({
+        linear: d3_svg_lineLinear,
+        "linear-closed": d3_svg_lineLinearClosed,
+        step: d3_svg_lineStep,
+        "step-before": d3_svg_lineStepBefore,
+        "step-after": d3_svg_lineStepAfter,
+        basis: d3_svg_lineBasis,
+        "basis-open": d3_svg_lineBasisOpen,
+        "basis-closed": d3_svg_lineBasisClosed,
+        bundle: d3_svg_lineBundle,
+        cardinal: d3_svg_lineCardinal,
+        "cardinal-open": d3_svg_lineCardinalOpen,
+        "cardinal-closed": d3_svg_lineCardinalClosed,
+        monotone: d3_svg_lineMonotone
+    });
+    d3_svg_lineInterpolators.forEach(function(key, value) {
+        value.key = key;
+        value.closed = /-closed$/.test(key);
+    });
+    function d3_svg_lineLinear(points) {
+        return points.join("L");
+    }
+    function d3_svg_lineLinearClosed(points) {
+        return d3_svg_lineLinear(points) + "Z";
+    }
+    function d3_svg_lineStep(points) {
+        var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+        while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
+        if (n > 1) path.push("H", p[0]);
+        return path.join("");
+    }
+    function d3_svg_lineStepBefore(points) {
+        var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+        while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
+        return path.join("");
+    }
+    function d3_svg_lineStepAfter(points) {
+        var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+        while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
+        return path.join("");
+    }
+    function d3_svg_lineCardinalOpen(points, tension) {
+        return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension));
+    }
+    function d3_svg_lineCardinalClosed(points, tension) {
+        return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
+            points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
+    }
+    function d3_svg_lineCardinal(points, tension) {
+        return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
+    }
+    function d3_svg_lineHermite(points, tangents) {
+        if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
+            return d3_svg_lineLinear(points);
+        }
+        var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
+        if (quad) {
+            path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
+            p0 = points[1];
+            pi = 2;
+        }
+        if (tangents.length > 1) {
+            t = tangents[1];
+            p = points[pi];
+            pi++;
+            path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
+            for (var i = 2; i < tangents.length; i++, pi++) {
+                p = points[pi];
+                t = tangents[i];
+                path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
+            }
+        }
+        if (quad) {
+            var lp = points[pi];
+            path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
+        }
+        return path;
+    }
+    function d3_svg_lineCardinalTangents(points, tension) {
+        var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
+        while (++i < n) {
+            p0 = p1;
+            p1 = p2;
+            p2 = points[i];
+            tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
+        }
+        return tangents;
+    }
+    function d3_svg_lineBasis(points) {
+        if (points.length < 3) return d3_svg_lineLinear(points);
+        var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
+        points.push(points[n - 1]);
+        while (++i <= n) {
+            pi = points[i];
+            px.shift();
+            px.push(pi[0]);
+            py.shift();
+            py.push(pi[1]);
+            d3_svg_lineBasisBezier(path, px, py);
+        }
+        points.pop();
+        path.push("L", pi);
+        return path.join("");
+    }
+    function d3_svg_lineBasisOpen(points) {
+        if (points.length < 4) return d3_svg_lineLinear(points);
+        var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
+        while (++i < 3) {
+            pi = points[i];
+            px.push(pi[0]);
+            py.push(pi[1]);
+        }
+        path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
+        --i;
+        while (++i < n) {
+            pi = points[i];
+            px.shift();
+            px.push(pi[0]);
+            py.shift();
+            py.push(pi[1]);
+            d3_svg_lineBasisBezier(path, px, py);
+        }
+        return path.join("");
+    }
+    function d3_svg_lineBasisClosed(points) {
+        var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
+        while (++i < 4) {
+            pi = points[i % n];
+            px.push(pi[0]);
+            py.push(pi[1]);
+        }
+        path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
+        --i;
+        while (++i < m) {
+            pi = points[i % n];
+            px.shift();
+            px.push(pi[0]);
+            py.shift();
+            py.push(pi[1]);
+            d3_svg_lineBasisBezier(path, px, py);
+        }
+        return path.join("");
+    }
+    function d3_svg_lineBundle(points, tension) {
+        var n = points.length - 1;
+        if (n) {
+            var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
+            while (++i <= n) {
+                p = points[i];
+                t = i / n;
+                p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
+                p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
+            }
+        }
+        return d3_svg_lineBasis(points);
+    }
+    function d3_svg_lineDot4(a, b) {
+        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
+    }
+    var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
+    function d3_svg_lineBasisBezier(path, x, y) {
+        path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
+    }
+    function d3_svg_lineSlope(p0, p1) {
+        return (p1[1] - p0[1]) / (p1[0] - p0[0]);
+    }
+    function d3_svg_lineFiniteDifferences(points) {
+        var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
+        while (++i < j) {
+            m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
+        }
+        m[i] = d;
+        return m;
+    }
+    function d3_svg_lineMonotoneTangents(points) {
+        var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
+        while (++i < j) {
+            d = d3_svg_lineSlope(points[i], points[i + 1]);
+            if (abs(d) < ε) {
+                m[i] = m[i + 1] = 0;
+            } else {
+                a = m[i] / d;
+                b = m[i + 1] / d;
+                s = a * a + b * b;
+                if (s > 9) {
+                    s = d * 3 / Math.sqrt(s);
+                    m[i] = s * a;
+                    m[i + 1] = s * b;
+                }
+            }
+        }
+        i = -1;
+        while (++i <= j) {
+            s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
+            tangents.push([ s || 0, m[i] * s || 0 ]);
+        }
+        return tangents;
+    }
+    function d3_svg_lineMonotone(points) {
+        return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
+    }
+    d3.svg.line.radial = function() {
+        var line = d3_svg_line(d3_svg_lineRadial);
+        line.radius = line.x, delete line.x;
+        line.angle = line.y, delete line.y;
+        return line;
+    };
+    function d3_svg_lineRadial(points) {
+        var point, i = -1, n = points.length, r, a;
+        while (++i < n) {
+            point = points[i];
+            r = point[0];
+            a = point[1] - halfπ;
+            point[0] = r * Math.cos(a);
+            point[1] = r * Math.sin(a);
+        }
+        return points;
+    }
+    function d3_svg_area(projection) {
+        var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7;
+        function area(data) {
+            var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
+                return x;
+            } : d3_functor(x1), fy1 = y0 === y1 ? function() {
+                return y;
+            } : d3_functor(y1), x, y;
+            function segment() {
+                segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
+            }
+            while (++i < n) {
+                if (defined.call(this, d = data[i], i)) {
+                    points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
+                    points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
+                } else if (points0.length) {
+                    segment();
+                    points0 = [];
+                    points1 = [];
+                }
+            }
+            if (points0.length) segment();
+            return segments.length ? segments.join("") : null;
+        }
+        area.x = function(_) {
+            if (!arguments.length) return x1;
+            x0 = x1 = _;
+            return area;
+        };
+        area.x0 = function(_) {
+            if (!arguments.length) return x0;
+            x0 = _;
+            return area;
+        };
+        area.x1 = function(_) {
+            if (!arguments.length) return x1;
+            x1 = _;
+            return area;
+        };
+        area.y = function(_) {
+            if (!arguments.length) return y1;
+            y0 = y1 = _;
+            return area;
+        };
+        area.y0 = function(_) {
+            if (!arguments.length) return y0;
+            y0 = _;
+            return area;
+        };
+        area.y1 = function(_) {
+            if (!arguments.length) return y1;
+            y1 = _;
+            return area;
+        };
+        area.defined = function(_) {
+            if (!arguments.length) return defined;
+            defined = _;
+            return area;
+        };
+        area.interpolate = function(_) {
+            if (!arguments.length) return interpolateKey;
+            if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
+            interpolateReverse = interpolate.reverse || interpolate;
+            L = interpolate.closed ? "M" : "L";
+            return area;
+        };
+        area.tension = function(_) {
+            if (!arguments.length) return tension;
+            tension = _;
+            return area;
+        };
+        return area;
+    }
+    d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
+    d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
+    d3.svg.area = function() {
+        return d3_svg_area(d3_identity);
+    };
+    d3.svg.area.radial = function() {
+        var area = d3_svg_area(d3_svg_lineRadial);
+        area.radius = area.x, delete area.x;
+        area.innerRadius = area.x0, delete area.x0;
+        area.outerRadius = area.x1, delete area.x1;
+        area.angle = area.y, delete area.y;
+        area.startAngle = area.y0, delete area.y0;
+        area.endAngle = area.y1, delete area.y1;
+        return area;
+    };
+    d3.svg.chord = function() {
+        var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
+        function chord(d, i) {
+            var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
+            return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
+        }
+        function subgroup(self, f, d, i) {
+            var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfπ, a1 = endAngle.call(self, subgroup, i) - halfπ;
+            return {
+                r: r,
+                a0: a0,
+                a1: a1,
+                p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
+                p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
+            };
+        }
+        function equals(a, b) {
+            return a.a0 == b.a0 && a.a1 == b.a1;
+        }
+        function arc(r, p, a) {
+            return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
+        }
+        function curve(r0, p0, r1, p1) {
+            return "Q 0,0 " + p1;
+        }
+        chord.radius = function(v) {
+            if (!arguments.length) return radius;
+            radius = d3_functor(v);
+            return chord;
+        };
+        chord.source = function(v) {
+            if (!arguments.length) return source;
+            source = d3_functor(v);
+            return chord;
+        };
+        chord.target = function(v) {
+            if (!arguments.length) return target;
+            target = d3_functor(v);
+            return chord;
+        };
+        chord.startAngle = function(v) {
+            if (!arguments.length) return startAngle;
+            startAngle = d3_functor(v);
+            return chord;
+        };
+        chord.endAngle = function(v) {
+            if (!arguments.length) return endAngle;
+            endAngle = d3_functor(v);
+            return chord;
+        };
+        return chord;
+    };
+    function d3_svg_chordRadius(d) {
+        return d.radius;
+    }
+    d3.svg.diagonal = function() {
+        var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
+        function diagonal(d, i) {
+            var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
+                x: p0.x,
+                y: m
+            }, {
+                x: p3.x,
+                y: m
+            }, p3 ];
+            p = p.map(projection);
+            return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
+        }
+        diagonal.source = function(x) {
+            if (!arguments.length) return source;
+            source = d3_functor(x);
+            return diagonal;
+        };
+        diagonal.target = function(x) {
+            if (!arguments.length) return target;
+            target = d3_functor(x);
+            return diagonal;
+        };
+        diagonal.projection = function(x) {
+            if (!arguments.length) return projection;
+            projection = x;
+            return diagonal;
+        };
+        return diagonal;
+    };
+    function d3_svg_diagonalProjection(d) {
+        return [ d.x, d.y ];
+    }
+    d3.svg.diagonal.radial = function() {
+        var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
+        diagonal.projection = function(x) {
+            return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
+        };
+        return diagonal;
+    };
+    function d3_svg_diagonalRadialProjection(projection) {
+        return function() {
+            var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ;
+            return [ r * Math.cos(a), r * Math.sin(a) ];
+        };
+    }
+    d3.svg.symbol = function() {
+        var type = d3_svg_symbolType, size = d3_svg_symbolSize;
+        function symbol(d, i) {
+            return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
+        }
+        symbol.type = function(x) {
+            if (!arguments.length) return type;
+            type = d3_functor(x);
+            return symbol;
+        };
+        symbol.size = function(x) {
+            if (!arguments.length) return size;
+            size = d3_functor(x);
+            return symbol;
+        };
+        return symbol;
+    };
+    function d3_svg_symbolSize() {
+        return 64;
+    }
+    function d3_svg_symbolType() {
+        return "circle";
+    }
+    function d3_svg_symbolCircle(size) {
+        var r = Math.sqrt(size / π);
+        return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
+    }
+    var d3_svg_symbols = d3.map({
+        circle: d3_svg_symbolCircle,
+        cross: function(size) {
+            var r = Math.sqrt(size / 5) / 2;
+            return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z";
+        },
+        diamond: function(size) {
+            var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
+            return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
+        },
+        square: function(size) {
+            var r = Math.sqrt(size) / 2;
+            return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
+        },
+        "triangle-down": function(size) {
+            var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
+            return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
+        },
+        "triangle-up": function(size) {
+            var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
+            return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
+        }
+    });
+    d3.svg.symbolTypes = d3_svg_symbols.keys();
+    var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
+    d3_selectionPrototype.transition = function(name) {
+        var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || {
+                time: Date.now(),
+                ease: d3_ease_cubicInOut,
+                delay: 0,
+                duration: 250
+            };
+        for (var j = -1, m = this.length; ++j < m; ) {
+            subgroups.push(subgroup = []);
+            for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+                if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
+                subgroup.push(node);
+            }
+        }
+        return d3_transition(subgroups, ns, id);
+    };
+    d3_selectionPrototype.interrupt = function(name) {
+        return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name)));
+    };
+    var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
+    function d3_selection_interruptNS(ns) {
+        return function() {
+            var lock, active;
+            if ((lock = this[ns]) && (active = lock[lock.active])) {
+                if (--lock.count) {
+                    delete lock[lock.active];
+                    lock.active += .5;
+                } else {
+                    delete this[ns];
+                }
+                active.event && active.event.interrupt.call(this, this.__data__, active.index);
+            }
+        };
+    }
+    function d3_transition(groups, ns, id) {
+        d3_subclass(groups, d3_transitionPrototype);
+        groups.namespace = ns;
+        groups.id = id;
+        return groups;
+    }
+    var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
+    d3_transitionPrototype.call = d3_selectionPrototype.call;
+    d3_transitionPrototype.empty = d3_selectionPrototype.empty;
+    d3_transitionPrototype.node = d3_selectionPrototype.node;
+    d3_transitionPrototype.size = d3_selectionPrototype.size;
+    d3.transition = function(selection, name) {
+        return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3_selectionRoot.transition(selection);
+    };
+    d3.transition.prototype = d3_transitionPrototype;
+    d3_transitionPrototype.select = function(selector) {
+        var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node;
+        selector = d3_selection_selector(selector);
+        for (var j = -1, m = this.length; ++j < m; ) {
+            subgroups.push(subgroup = []);
+            for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+                if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
+                    if ("__data__" in node) subnode.__data__ = node.__data__;
+                    d3_transitionNode(subnode, i, ns, id, node[ns][id]);
+                    subgroup.push(subnode);
+                } else {
+                    subgroup.push(null);
+                }
+            }
+        }
+        return d3_transition(subgroups, ns, id);
+    };
+    d3_transitionPrototype.selectAll = function(selector) {
+        var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition;
+        selector = d3_selection_selectorAll(selector);
+        for (var j = -1, m = this.length; ++j < m; ) {
+            for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+                if (node = group[i]) {
+                    transition = node[ns][id];
+                    subnodes = selector.call(node, node.__data__, i, j);
+                    subgroups.push(subgroup = []);
+                    for (var k = -1, o = subnodes.length; ++k < o; ) {
+                        if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
+                        subgroup.push(subnode);
+                    }
+                }
+            }
+        }
+        return d3_transition(subgroups, ns, id);
+    };
+    d3_transitionPrototype.filter = function(filter) {
+        var subgroups = [], subgroup, group, node;
+        if (typeof filter !== "function") filter = d3_selection_filter(filter);
+        for (var j = 0, m = this.length; j < m; j++) {
+            subgroups.push(subgroup = []);
+            for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+                if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
+                    subgroup.push(node);
+                }
+            }
+        }
+        return d3_transition(subgroups, this.namespace, this.id);
+    };
+    d3_transitionPrototype.tween = function(name, tween) {
+        var id = this.id, ns = this.namespace;
+        if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
+        return d3_selection_each(this, tween == null ? function(node) {
+            node[ns][id].tween.remove(name);
+        } : function(node) {
+            node[ns][id].tween.set(name, tween);
+        });
+    };
+    function d3_transition_tween(groups, name, value, tween) {
+        var id = groups.id, ns = groups.namespace;
+        return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
+            node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
+        } : (value = tween(value), function(node) {
+            node[ns][id].tween.set(name, value);
+        }));
+    }
+    d3_transitionPrototype.attr = function(nameNS, value) {
+        if (arguments.length < 2) {
+            for (value in nameNS) this.attr(value, nameNS[value]);
+            return this;
+        }
+        var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
+        function attrNull() {
+            this.removeAttribute(name);
+        }
+        function attrNullNS() {
+            this.removeAttributeNS(name.space, name.local);
+        }
+        function attrTween(b) {
+            return b == null ? attrNull : (b += "", function() {
+                var a = this.getAttribute(name), i;
+                return a !== b && (i = interpolate(a, b), function(t) {
+                        this.setAttribute(name, i(t));
+                    });
+            });
+        }
+        function attrTweenNS(b) {
+            return b == null ? attrNullNS : (b += "", function() {
+                var a = this.getAttributeNS(name.space, name.local), i;
+                return a !== b && (i = interpolate(a, b), function(t) {
+                        this.setAttributeNS(name.space, name.local, i(t));
+                    });
+            });
+        }
+        return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
+    };
+    d3_transitionPrototype.attrTween = function(nameNS, tween) {
+        var name = d3.ns.qualify(nameNS);
+        function attrTween(d, i) {
+            var f = tween.call(this, d, i, this.getAttribute(name));
+            return f && function(t) {
+                    this.setAttribute(name, f(t));
+                };
+        }
+        function attrTweenNS(d, i) {
+            var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
+            return f && function(t) {
+                    this.setAttributeNS(name.space, name.local, f(t));
+                };
+        }
+        return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
+    };
+    d3_transitionPrototype.style = function(name, value, priority) {
+        var n = arguments.length;
+        if (n < 3) {
+            if (typeof name !== "string") {
+                if (n < 2) value = "";
+                for (priority in name) this.style(priority, name[priority], value);
+                return this;
+            }
+            priority = "";
+        }
+        function styleNull() {
+            this.style.removeProperty(name);
+        }
+        function styleString(b) {
+            return b == null ? styleNull : (b += "", function() {
+                var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i;
+                return a !== b && (i = d3_interpolate(a, b), function(t) {
+                        this.style.setProperty(name, i(t), priority);
+                    });
+            });
+        }
+        return d3_transition_tween(this, "style." + name, value, styleString);
+    };
+    d3_transitionPrototype.styleTween = function(name, tween, priority) {
+        if (arguments.length < 3) priority = "";
+        function styleTween(d, i) {
+            var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name));
+            return f && function(t) {
+                    this.style.setProperty(name, f(t), priority);
+                };
+        }
+        return this.tween("style." + name, styleTween);
+    };
+    d3_transitionPrototype.text = function(value) {
+        return d3_transition_tween(this, "text", value, d3_transition_text);
+    };
+    function d3_transition_text(b) {
+        if (b == null) b = "";
+        return function() {
+            this.textContent = b;
+        };
+    }
+    d3_transitionPrototype.remove = function() {
+        var ns = this.namespace;
+        return this.each("end.transition", function() {
+            var p;
+            if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
+        });
+    };
+    d3_transitionPrototype.ease = function(value) {
+        var id = this.id, ns = this.namespace;
+        if (arguments.length < 1) return this.node()[ns][id].ease;
+        if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
+        return d3_selection_each(this, function(node) {
+            node[ns][id].ease = value;
+        });
+    };
+    d3_transitionPrototype.delay = function(value) {
+        var id = this.id, ns = this.namespace;
+        if (arguments.length < 1) return this.node()[ns][id].delay;
+        return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
+            node[ns][id].delay = +value.call(node, node.__data__, i, j);
+        } : (value = +value, function(node) {
+            node[ns][id].delay = value;
+        }));
+    };
+    d3_transitionPrototype.duration = function(value) {
+        var id = this.id, ns = this.namespace;
+        if (arguments.length < 1) return this.node()[ns][id].duration;
+        return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
+            node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j));
+        } : (value = Math.max(1, value), function(node) {
+            node[ns][id].duration = value;
+        }));
+    };
+    d3_transitionPrototype.each = function(type, listener) {
+        var id = this.id, ns = this.namespace;
+        if (arguments.length < 2) {
+            var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
+            try {
+                d3_transitionInheritId = id;
+                d3_selection_each(this, function(node, i, j) {
+                    d3_transitionInherit = node[ns][id];
+                    type.call(node, node.__data__, i, j);
+                });
+            } finally {
+                d3_transitionInherit = inherit;
+                d3_transitionInheritId = inheritId;
+            }
+        } else {
+            d3_selection_each(this, function(node) {
+                var transition = node[ns][id];
+                (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
+            });
+        }
+        return this;
+    };
+    d3_transitionPrototype.transition = function() {
+        var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition;
+        for (var j = 0, m = this.length; j < m; j++) {
+            subgroups.push(subgroup = []);
+            for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+                if (node = group[i]) {
+                    transition = node[ns][id0];
+                    d3_transitionNode(node, i, ns, id1, {
+                        time: transition.time,
+                        ease: transition.ease,
+                        delay: transition.delay + transition.duration,
+                        duration: transition.duration
+                    });
+                }
+                subgroup.push(node);
+            }
+        }
+        return d3_transition(subgroups, ns, id1);
+    };
+    function d3_transitionNamespace(name) {
+        return name == null ? "__transition__" : "__transition_" + name + "__";
+    }
+    function d3_transitionNode(node, i, ns, id, inherit) {
+        var lock = node[ns] || (node[ns] = {
+                active: 0,
+                count: 0
+            }), transition = lock[id];
+        if (!transition) {
+            var time = inherit.time;
+            transition = lock[id] = {
+                tween: new d3_Map(),
+                time: time,
+                delay: inherit.delay,
+                duration: inherit.duration,
+                ease: inherit.ease,
+                index: i
+            };
+            inherit = null;
+            ++lock.count;
+            d3.timer(function(elapsed) {
+                var delay = transition.delay, duration, ease, timer = d3_timer_active, tweened = [];
+                timer.t = delay + time;
+                if (delay <= elapsed) return start(elapsed - delay);
+                timer.c = start;
+                function start(elapsed) {
+                    if (lock.active > id) return stop();
+                    var active = lock[lock.active];
+                    if (active) {
+                        --lock.count;
+                        delete lock[lock.active];
+                        active.event && active.event.interrupt.call(node, node.__data__, active.index);
+                    }
+                    lock.active = id;
+                    transition.event && transition.event.start.call(node, node.__data__, i);
+                    transition.tween.forEach(function(key, value) {
+                        if (value = value.call(node, node.__data__, i)) {
+                            tweened.push(value);
+                        }
+                    });
+                    ease = transition.ease;
+                    duration = transition.duration;
+                    d3.timer(function() {
+                        timer.c = tick(elapsed || 1) ? d3_true : tick;
+                        return 1;
+                    }, 0, time);
+                }
+                function tick(elapsed) {
+                    if (lock.active !== id) return 1;
+                    var t = elapsed / duration, e = ease(t), n = tweened.length;
+                    while (n > 0) {
+                        tweened[--n].call(node, e);
+                    }
+                    if (t >= 1) {
+                        transition.event && transition.event.end.call(node, node.__data__, i);
+                        return stop();
+                    }
+                }
+                function stop() {
+                    if (--lock.count) delete lock[id]; else delete node[ns];
+                    return 1;
+                }
+            }, 0, time);
+        }
+    }
+    d3.svg.axis = function() {
+        var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
+        function axis(g) {
+            g.each(function() {
+                var g = d3.select(this);
+                var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
+                var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform;
+                var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"),
+                    d3.transition(path));
+                tickEnter.append("line");
+                tickEnter.append("text");
+                var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2;
+                if (orient === "bottom" || orient === "top") {
+                    tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2";
+                    text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle");
+                    pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize);
+                } else {
+                    tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2";
+                    text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start");
+                    pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize);
+                }
+                lineEnter.attr(y2, sign * innerTickSize);
+                textEnter.attr(y1, sign * tickSpacing);
+                lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize);
+                textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing);
+                if (scale1.rangeBand) {
+                    var x = scale1, dx = x.rangeBand() / 2;
+                    scale0 = scale1 = function(d) {
+                        return x(d) + dx;
+                    };
+                } else if (scale0.rangeBand) {
+                    scale0 = scale1;
+                } else {
+                    tickExit.call(tickTransform, scale1, scale0);
+                }
+                tickEnter.call(tickTransform, scale0, scale1);
+                tickUpdate.call(tickTransform, scale1, scale1);
+            });
+        }
+        axis.scale = function(x) {
+            if (!arguments.length) return scale;
+            scale = x;
+            return axis;
+        };
+        axis.orient = function(x) {
+            if (!arguments.length) return orient;
+            orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
+            return axis;
+        };
+        axis.ticks = function() {
+            if (!arguments.length) return tickArguments_;
+            tickArguments_ = arguments;
+            return axis;
+        };
+        axis.tickValues = function(x) {
+            if (!arguments.length) return tickValues;
+            tickValues = x;
+            return axis;
+        };
+        axis.tickFormat = function(x) {
+            if (!arguments.length) return tickFormat_;
+            tickFormat_ = x;
+            return axis;
+        };
+        axis.tickSize = function(x) {
+            var n = arguments.length;
+            if (!n) return innerTickSize;
+            innerTickSize = +x;
+            outerTickSize = +arguments[n - 1];
+            return axis;
+        };
+        axis.innerTickSize = function(x) {
+            if (!arguments.length) return innerTickSize;
+            innerTickSize = +x;
+            return axis;
+        };
+        axis.outerTickSize = function(x) {
+            if (!arguments.length) return outerTickSize;
+            outerTickSize = +x;
+            return axis;
+        };
+        axis.tickPadding = function(x) {
+            if (!arguments.length) return tickPadding;
+            tickPadding = +x;
+            return axis;
+        };
+        axis.tickSubdivide = function() {
+            return arguments.length && axis;
+        };
+        return axis;
+    };
+    var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
+        top: 1,
+        right: 1,
+        bottom: 1,
+        left: 1
+    };
+    function d3_svg_axisX(selection, x0, x1) {
+        selection.attr("transform", function(d) {
+            var v0 = x0(d);
+            return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)";
+        });
+    }
+    function d3_svg_axisY(selection, y0, y1) {
+        selection.attr("transform", function(d) {
+            var v0 = y0(d);
+            return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")";
+        });
+    }
+    d3.svg.brush = function() {
+        var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0];
+        function brush(g) {
+            g.each(function() {
+                var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart);
+                var background = g.selectAll(".background").data([ 0 ]);
+                background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
+                g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
+                var resize = g.selectAll(".resize").data(resizes, d3_identity);
+                resize.exit().remove();
+                resize.enter().append("g").attr("class", function(d) {
+                    return "resize " + d;
+                }).style("cursor", function(d) {
+                    return d3_svg_brushCursor[d];
+                }).append("rect").attr("x", function(d) {
+                    return /[ew]$/.test(d) ? -3 : null;
+                }).attr("y", function(d) {
+                    return /^[ns]/.test(d) ? -3 : null;
+                }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
+                resize.style("display", brush.empty() ? "none" : null);
+                var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
+                if (x) {
+                    range = d3_scaleRange(x);
+                    backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
+                    redrawX(gUpdate);
+                }
+                if (y) {
+                    range = d3_scaleRange(y);
+                    backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
+                    redrawY(gUpdate);
+                }
+                redraw(gUpdate);
+            });
+        }
+        brush.event = function(g) {
+            g.each(function() {
+                var event_ = event.of(this, arguments), extent1 = {
+                    x: xExtent,
+                    y: yExtent,
+                    i: xExtentDomain,
+                    j: yExtentDomain
+                }, extent0 = this.__chart__ || extent1;
+                this.__chart__ = extent1;
+                if (d3_transitionInheritId) {
+                    d3.select(this).transition().each("start.brush", function() {
+                        xExtentDomain = extent0.i;
+                        yExtentDomain = extent0.j;
+                        xExtent = extent0.x;
+                        yExtent = extent0.y;
+                        event_({
+                            type: "brushstart"
+                        });
+                    }).tween("brush:brush", function() {
+                        var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
+                        xExtentDomain = yExtentDomain = null;
+                        return function(t) {
+                            xExtent = extent1.x = xi(t);
+                            yExtent = extent1.y = yi(t);
+                            event_({
+                                type: "brush",
+                                mode: "resize"
+                            });
+                        };
+                    }).each("end.brush", function() {
+                        xExtentDomain = extent1.i;
+                        yExtentDomain = extent1.j;
+                        event_({
+                            type: "brush",
+                            mode: "resize"
+                        });
+                        event_({
+                            type: "brushend"
+                        });
+                    });
+                } else {
+                    event_({
+                        type: "brushstart"
+                    });
+                    event_({
+                        type: "brush",
+                        mode: "resize"
+                    });
+                    event_({
+                        type: "brushend"
+                    });
+                }
+            });
+        };
+        function redraw(g) {
+            g.selectAll(".resize").attr("transform", function(d) {
+                return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
+            });
+        }
+        function redrawX(g) {
+            g.select(".extent").attr("x", xExtent[0]);
+            g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
+        }
+        function redrawY(g) {
+            g.select(".extent").attr("y", yExtent[0]);
+            g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
+        }
+        function brushstart() {
+            var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(), center, origin = d3.mouse(target), offset;
+            var w = d3.select(d3_window).on("keydown.brush", keydown).on("keyup.brush", keyup);
+            if (d3.event.changedTouches) {
+                w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
+            } else {
+                w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
+            }
+            g.interrupt().selectAll("*").interrupt();
+            if (dragging) {
+                origin[0] = xExtent[0] - origin[0];
+                origin[1] = yExtent[0] - origin[1];
+            } else if (resizing) {
+                var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
+                offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
+                origin[0] = xExtent[ex];
+                origin[1] = yExtent[ey];
+            } else if (d3.event.altKey) center = origin.slice();
+            g.style("pointer-events", "none").selectAll(".resize").style("display", null);
+            d3.select("body").style("cursor", eventTarget.style("cursor"));
+            event_({
+                type: "brushstart"
+            });
+            brushmove();
+            function keydown() {
+                if (d3.event.keyCode == 32) {
+                    if (!dragging) {
+                        center = null;
+                        origin[0] -= xExtent[1];
+                        origin[1] -= yExtent[1];
+                        dragging = 2;
+                    }
+                    d3_eventPreventDefault();
+                }
+            }
+            function keyup() {
+                if (d3.event.keyCode == 32 && dragging == 2) {
+                    origin[0] += xExtent[1];
+                    origin[1] += yExtent[1];
+                    dragging = 0;
+                    d3_eventPreventDefault();
+                }
+            }
+            function brushmove() {
+                var point = d3.mouse(target), moved = false;
+                if (offset) {
+                    point[0] += offset[0];
+                    point[1] += offset[1];
+                }
+                if (!dragging) {
+                    if (d3.event.altKey) {
+                        if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
+                        origin[0] = xExtent[+(point[0] < center[0])];
+                        origin[1] = yExtent[+(point[1] < center[1])];
+                    } else center = null;
+                }
+                if (resizingX && move1(point, x, 0)) {
+                    redrawX(g);
+                    moved = true;
+                }
+                if (resizingY && move1(point, y, 1)) {
+                    redrawY(g);
+                    moved = true;
+                }
+                if (moved) {
+                    redraw(g);
+                    event_({
+                        type: "brush",
+                        mode: dragging ? "move" : "resize"
+                    });
+                }
+            }
+            function move1(point, scale, i) {
+                var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max;
+                if (dragging) {
+                    r0 -= position;
+                    r1 -= size + position;
+                }
+                min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
+                if (dragging) {
+                    max = (min += position) + size;
+                } else {
+                    if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
+                    if (position < min) {
+                        max = min;
+                        min = position;
+                    } else {
+                        max = position;
+                    }
+                }
+                if (extent[0] != min || extent[1] != max) {
+                    if (i) yExtentDomain = null; else xExtentDomain = null;
+                    extent[0] = min;
+                    extent[1] = max;
+                    return true;
+                }
+            }
+            function brushend() {
+                brushmove();
+                g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
+                d3.select("body").style("cursor", null);
+                w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null);
+                dragRestore();
+                event_({
+                    type: "brushend"
+                });
+            }
+        }
+        brush.x = function(z) {
+            if (!arguments.length) return x;
+            x = z;
+            resizes = d3_svg_brushResizes[!x << 1 | !y];
+            return brush;
+        };
+        brush.y = function(z) {
+            if (!arguments.length) return y;
+            y = z;
+            resizes = d3_svg_brushResizes[!x << 1 | !y];
+            return brush;
+        };
+        brush.clamp = function(z) {
+            if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
+            if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
+            return brush;
+        };
+        brush.extent = function(z) {
+            var x0, x1, y0, y1, t;
+            if (!arguments.length) {
+                if (x) {
+                    if (xExtentDomain) {
+                        x0 = xExtentDomain[0], x1 = xExtentDomain[1];
+                    } else {
+                        x0 = xExtent[0], x1 = xExtent[1];
+                        if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
+                        if (x1 < x0) t = x0, x0 = x1, x1 = t;
+                    }
+                }
+                if (y) {
+                    if (yExtentDomain) {
+                        y0 = yExtentDomain[0], y1 = yExtentDomain[1];
+                    } else {
+                        y0 = yExtent[0], y1 = yExtent[1];
+                        if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
+                        if (y1 < y0) t = y0, y0 = y1, y1 = t;
+                    }
+                }
+                return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
+            }
+            if (x) {
+                x0 = z[0], x1 = z[1];
+                if (y) x0 = x0[0], x1 = x1[0];
+                xExtentDomain = [ x0, x1 ];
+                if (x.invert) x0 = x(x0), x1 = x(x1);
+                if (x1 < x0) t = x0, x0 = x1, x1 = t;
+                if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
+            }
+            if (y) {
+                y0 = z[0], y1 = z[1];
+                if (x) y0 = y0[1], y1 = y1[1];
+                yExtentDomain = [ y0, y1 ];
+                if (y.invert) y0 = y(y0), y1 = y(y1);
+                if (y1 < y0) t = y0, y0 = y1, y1 = t;
+                if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
+            }
+            return brush;
+        };
+        brush.clear = function() {
+            if (!brush.empty()) {
+                xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
+                xExtentDomain = yExtentDomain = null;
+            }
+            return brush;
+        };
+        brush.empty = function() {
+            return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
+        };
+        return d3.rebind(brush, event, "on");
+    };
+    var d3_svg_brushCursor = {
+        n: "ns-resize",
+        e: "ew-resize",
+        s: "ns-resize",
+        w: "ew-resize",
+        nw: "nwse-resize",
+        ne: "nesw-resize",
+        se: "nwse-resize",
+        sw: "nesw-resize"
+    };
+    var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
+    var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat;
+    var d3_time_formatUtc = d3_time_format.utc;
+    var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ");
+    d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
+    function d3_time_formatIsoNative(date) {
+        return date.toISOString();
+    }
+    d3_time_formatIsoNative.parse = function(string) {
+        var date = new Date(string);
+        return isNaN(date) ? null : date;
+    };
+    d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
+    d3_time.second = d3_time_interval(function(date) {
+        return new d3_date(Math.floor(date / 1e3) * 1e3);
+    }, function(date, offset) {
+        date.setTime(date.getTime() + Math.floor(offset) * 1e3);
+    }, function(date) {
+        return date.getSeconds();
+    });
+    d3_time.seconds = d3_time.second.range;
+    d3_time.seconds.utc = d3_time.second.utc.range;
+    d3_time.minute = d3_time_interval(function(date) {
+        return new d3_date(Math.floor(date / 6e4) * 6e4);
+    }, function(date, offset) {
+        date.setTime(date.getTime() + Math.floor(offset) * 6e4);
+    }, function(date) {
+        return date.getMinutes();
+    });
+    d3_time.minutes = d3_time.minute.range;
+    d3_time.minutes.utc = d3_time.minute.utc.range;
+    d3_time.hour = d3_time_interval(function(date) {
+        var timezone = date.getTimezoneOffset() / 60;
+        return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
+    }, function(date, offset) {
+        date.setTime(date.getTime() + Math.floor(offset) * 36e5);
+    }, function(date) {
+        return date.getHours();
+    });
+    d3_time.hours = d3_time.hour.range;
+    d3_time.hours.utc = d3_time.hour.utc.range;
+    d3_time.month = d3_time_interval(function(date) {
+        date = d3_time.day(date);
+        date.setDate(1);
+        return date;
+    }, function(date, offset) {
+        date.setMonth(date.getMonth() + offset);
+    }, function(date) {
+        return date.getMonth();
+    });
+    d3_time.months = d3_time.month.range;
+    d3_time.months.utc = d3_time.month.utc.range;
+    function d3_time_scale(linear, methods, format) {
+        function scale(x) {
+            return linear(x);
+        }
+        scale.invert = function(x) {
+            return d3_time_scaleDate(linear.invert(x));
+        };
+        scale.domain = function(x) {
+            if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
+            linear.domain(x);
+            return scale;
+        };
+        function tickMethod(extent, count) {
+            var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target);
+            return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) {
+                return d / 31536e6;
+            }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
+        }
+        scale.nice = function(interval, skip) {
+            var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
+            if (method) interval = method[0], skip = method[1];
+            function skipped(date) {
+                return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
+            }
+            return scale.domain(d3_scale_nice(domain, skip > 1 ? {
+                floor: function(date) {
+                    while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
+                    return date;
+                },
+                ceil: function(date) {
+                    while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
+                    return date;
+                }
+            } : interval));
+        };
+        scale.ticks = function(interval, skip) {
+            var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
+                range: interval
+            }, skip ];
+            if (method) interval = method[0], skip = method[1];
+            return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip);
+        };
+        scale.tickFormat = function() {
+            return format;
+        };
+        scale.copy = function() {
+            return d3_time_scale(linear.copy(), methods, format);
+        };
+        return d3_scale_linearRebind(scale, linear);
+    }
+    function d3_time_scaleDate(t) {
+        return new Date(t);
+    }
+    var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
+    var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ];
+    var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) {
+        return d.getMilliseconds();
+    } ], [ ":%S", function(d) {
+        return d.getSeconds();
+    } ], [ "%I:%M", function(d) {
+        return d.getMinutes();
+    } ], [ "%I %p", function(d) {
+        return d.getHours();
+    } ], [ "%a %d", function(d) {
+        return d.getDay() && d.getDate() != 1;
+    } ], [ "%b %d", function(d) {
+        return d.getDate() != 1;
+    } ], [ "%B", function(d) {
+        return d.getMonth();
+    } ], [ "%Y", d3_true ] ]);
+    var d3_time_scaleMilliseconds = {
+        range: function(start, stop, step) {
+            return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate);
+        },
+        floor: d3_identity,
+        ceil: d3_identity
+    };
+    d3_time_scaleLocalMethods.year = d3_time.year;
+    d3_time.scale = function() {
+        return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
+    };
+    var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) {
+        return [ m[0].utc, m[1] ];
+    });
+    var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) {
+        return d.getUTCMilliseconds();
+    } ], [ ":%S", function(d) {
+        return d.getUTCSeconds();
+    } ], [ "%I:%M", function(d) {
+        return d.getUTCMinutes();
+    } ], [ "%I %p", function(d) {
+        return d.getUTCHours();
+    } ], [ "%a %d", function(d) {
+        return d.getUTCDay() && d.getUTCDate() != 1;
+    } ], [ "%b %d", function(d) {
+        return d.getUTCDate() != 1;
+    } ], [ "%B", function(d) {
+        return d.getUTCMonth();
+    } ], [ "%Y", d3_true ] ]);
+    d3_time_scaleUtcMethods.year = d3_time.year.utc;
+    d3_time.scale.utc = function() {
+        return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat);
+    };
+    d3.text = d3_xhrType(function(request) {
+        return request.responseText;
+    });
+    d3.json = function(url, callback) {
+        return d3_xhr(url, "application/json", d3_json, callback);
+    };
+    function d3_json(request) {
+        return JSON.parse(request.responseText);
+    }
+    d3.html = function(url, callback) {
+        return d3_xhr(url, "text/html", d3_html, callback);
+    };
+    function d3_html(request) {
+        var range = d3_document.createRange();
+        range.selectNode(d3_document.body);
+        return range.createContextualFragment(request.responseText);
+    }
+    d3.xml = d3_xhrType(function(request) {
+        return request.responseXML;
+    });
+    if (typeof define === "function" && define.amd) define(d3); else if (typeof module === "object" && module.exports) module.exports = d3;
+    this.d3 = d3;
+}();
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/js/rickshaw.min.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/js/rickshaw.min.js
new file mode 100755
index 0000000000..da7ad95c8c
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/public/js/rickshaw.min.js
@@ -0,0 +1,3 @@
+(function(root,factory){if(typeof define==="function"&&define.amd){define(["d3.min"],function(d3){return root.Rickshaw=factory(d3)})}else if(typeof exports==="object"){module.exports=factory(require("d3"))}else{root.Rickshaw=factory(d3)}})(this,function(d3){var Rickshaw={namespace:function(namespace,obj){var parts=namespace.split(".");var parent=Rickshaw;for(var i=1,length=parts.length;i<length;i++){var currentPart=parts[i];parent[currentPart]=parent[currentPart]||{};parent=parent[currentPart]}return parent},keys:function(obj){var keys=[];for(var key in obj)keys.push(key);return keys},extend:function(destination,source){for(var property in source){destination[property]=source[property]}return destination},clone:function(obj){return JSON.parse(JSON.stringify(obj))}};(function(globalContext){var _toString=Object.prototype.toString,NULL_TYPE="Null",UNDEFINED_TYPE="Undefined",BOOLEAN_TYPE="Boolean",NUMBER_TYPE="Number",STRING_TYPE="String",OBJECT_TYPE="Object",FUNCTION_CLASS="[object Function]";function isFunction(object){return _toString.call(object)===FUNCTION_CLASS}function extend(destination,source){for(var property in source)if(source.hasOwnProperty(property))destination[property]=source[property];return destination}function keys(object){if(Type(object)!==OBJECT_TYPE){throw new TypeError}var results=[];for(var property in object){if(object.hasOwnProperty(property)){results.push(property)}}return results}function Type(o){switch(o){case null:return NULL_TYPE;case void 0:return UNDEFINED_TYPE}var type=typeof o;switch(type){case"boolean":return BOOLEAN_TYPE;case"number":return NUMBER_TYPE;case"string":return STRING_TYPE}return OBJECT_TYPE}function isUndefined(object){return typeof object==="undefined"}var slice=Array.prototype.slice;function argumentNames(fn){var names=fn.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1].replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g,"").replace(/\s+/g,"").split(",");return names.length==1&&!names[0]?[]:names}function wrap(fn,wrapper){var __method=fn;return function(){var a=update([bind(__method,this)],arguments);return wrapper.apply(this,a)}}function update(array,args){var arrayLength=array.length,length=args.length;while(length--)array[arrayLength+length]=args[length];return array}function merge(array,args){array=slice.call(array,0);return update(array,args)}function bind(fn,context){if(arguments.length<2&&isUndefined(arguments[0]))return this;var __method=fn,args=slice.call(arguments,2);return function(){var a=merge(args,arguments);return __method.apply(context,a)}}var emptyFunction=function(){};var Class=function(){var IS_DONTENUM_BUGGY=function(){for(var p in{toString:1}){if(p==="toString")return false}return true}();function subclass(){}function create(){var parent=null,properties=[].slice.apply(arguments);if(isFunction(properties[0]))parent=properties.shift();function klass(){this.initialize.apply(this,arguments)}extend(klass,Class.Methods);klass.superclass=parent;klass.subclasses=[];if(parent){subclass.prototype=parent.prototype;klass.prototype=new subclass;try{parent.subclasses.push(klass)}catch(e){}}for(var i=0,length=properties.length;i<length;i++)klass.addMethods(properties[i]);if(!klass.prototype.initialize)klass.prototype.initialize=emptyFunction;klass.prototype.constructor=klass;return klass}function addMethods(source){var ancestor=this.superclass&&this.superclass.prototype,properties=keys(source);if(IS_DONTENUM_BUGGY){if(source.toString!=Object.prototype.toString)properties.push("toString");if(source.valueOf!=Object.prototype.valueOf)properties.push("valueOf")}for(var i=0,length=properties.length;i<length;i++){var property=properties[i],value=source[property];if(ancestor&&isFunction(value)&&argumentNames(value)[0]=="$super"){var method=value;value=wrap(function(m){return function(){return ancestor[m].apply(this,arguments)}}(property),method);value.valueOf=bind(method.valueOf,method);value.toString=bind(method.toString,method)}this.prototype[property]=value}return this}return{create:create,Methods:{addMethods:addMethods}}}();if(globalContext.exports){globalContext.exports.Class=Class}else{globalContext.Class=Class}})(Rickshaw);Rickshaw.namespace("Rickshaw.Compat.ClassList");Rickshaw.Compat.ClassList=function(){if(typeof document!=="undefined"&&!("classList"in document.createElement("a"))){(function(view){"use strict";var classListProp="classList",protoProp="prototype",elemCtrProto=(view.HTMLElement||view.Element)[protoProp],objCtr=Object,strTrim=String[protoProp].trim||function(){return this.replace(/^\s+|\s+$/g,"")},arrIndexOf=Array[protoProp].indexOf||function(item){var i=0,len=this.length;for(;i<len;i++){if(i in this&&this[i]===item){return i}}return-1},DOMEx=function(type,message){this.name=type;this.code=DOMException[type];this.message=message},checkTokenAndGetIndex=function(classList,token){if(token===""){throw new DOMEx("SYNTAX_ERR","An invalid or illegal string was specified")}if(/\s/.test(token)){throw new DOMEx("INVALID_CHARACTER_ERR","String contains an invalid character")}return arrIndexOf.call(classList,token)},ClassList=function(elem){var trimmedClasses=strTrim.call(elem.className),classes=trimmedClasses?trimmedClasses.split(/\s+/):[],i=0,len=classes.length;for(;i<len;i++){this.push(classes[i])}this._updateClassName=function(){elem.className=this.toString()}},classListProto=ClassList[protoProp]=[],classListGetter=function(){return new ClassList(this)};DOMEx[protoProp]=Error[protoProp];classListProto.item=function(i){return this[i]||null};classListProto.contains=function(token){token+="";return checkTokenAndGetIndex(this,token)!==-1};classListProto.add=function(token){token+="";if(checkTokenAndGetIndex(this,token)===-1){this.push(token);this._updateClassName()}};classListProto.remove=function(token){token+="";var index=checkTokenAndGetIndex(this,token);if(index!==-1){this.splice(index,1);this._updateClassName()}};classListProto.toggle=function(token){token+="";if(checkTokenAndGetIndex(this,token)===-1){this.add(token)}else{this.remove(token)}};classListProto.toString=function(){return this.join(" ")};if(objCtr.defineProperty){var classListPropDesc={get:classListGetter,enumerable:true,configurable:true};try{objCtr.defineProperty(elemCtrProto,classListProp,classListPropDesc)}catch(ex){if(ex.number===-2146823252){classListPropDesc.enumerable=false;objCtr.defineProperty(elemCtrProto,classListProp,classListPropDesc)}}}else if(objCtr[protoProp].__defineGetter__){elemCtrProto.__defineGetter__(classListProp,classListGetter)}})(window)}};if(typeof RICKSHAW_NO_COMPAT!=="undefined"&&!RICKSHAW_NO_COMPAT||typeof RICKSHAW_NO_COMPAT==="undefined"){new Rickshaw.Compat.ClassList}Rickshaw.namespace("Rickshaw.Graph");Rickshaw.Graph=function(args){var self=this;this.initialize=function(args){if(!args.element)throw"Rickshaw.Graph needs a reference to an element";if(args.element.nodeType!==1)throw"Rickshaw.Graph element was defined but not an HTML element";this.element=args.element;this.series=args.series;this.window={};this.updateCallbacks=[];this.configureCallbacks=[];this.defaults={interpolation:"cardinal",offset:"zero",min:undefined,max:undefined,preserve:false,xScale:undefined,yScale:undefined,stack:true};this._loadRenderers();this.configure(args);this.validateSeries(args.series);this.series.active=function(){return self.series.filter(function(s){return!s.disabled})};this.setSize({width:args.width,height:args.height});this.element.classList.add("rickshaw_graph");this.vis=d3.select(this.element).append("svg:svg").attr("width",this.width).attr("height",this.height);this.discoverRange()};this._loadRenderers=function(){for(var name in Rickshaw.Graph.Renderer){if(!name||!Rickshaw.Graph.Renderer.hasOwnProperty(name))continue;var r=Rickshaw.Graph.Renderer[name];if(!r||!r.prototype||!r.prototype.render)continue;self.registerRenderer(new r({graph:self}))}};this.validateSeries=function(series){if(!Array.isArray(series)&&!(series instanceof Rickshaw.Series)){var seriesSignature=Object.prototype.toString.apply(series);throw"series is not an array: "+seriesSignature}var pointsCount;series.forEach(function(s){if(!(s instanceof Object)){throw"series element is not an object: "+s}if(!s.data){throw"series has no data: "+JSON.stringify(s)}if(!Array.isArray(s.data)){throw"series data is not an array: "+JSON.stringify(s.data)}if(s.data.length>0){var x=s.data[0].x;var y=s.data[0].y;if(typeof x!="number"||typeof y!="number"&&y!==null){throw"x and y properties of points should be numbers instead of "+typeof x+" and "+typeof y}}if(s.data.length>=3){if(s.data[2].x<s.data[1].x||s.data[1].x<s.data[0].x||s.data[s.data.length-1].x<s.data[0].x){throw"series data needs to be sorted on x values for series name: "+s.name}}},this)};this.dataDomain=function(){var data=this.series.map(function(s){return s.data});var min=d3.min(data.map(function(d){return d[0].x}));var max=d3.max(data.map(function(d){return d[d.length-1].x}));return[min,max]};this.discoverRange=function(){var domain=this.renderer.domain();this.x=(this.xScale||d3.scale.linear()).copy().domain(domain.x).range([0,this.width]);this.y=(this.yScale||d3.scale.linear()).copy().domain(domain.y).range([this.height,0]);this.x.magnitude=d3.scale.linear().domain([domain.x[0]-domain.x[0],domain.x[1]-domain.x[0]]).range([0,this.width]);this.y.magnitude=d3.scale.linear().domain([domain.y[0]-domain.y[0],domain.y[1]-domain.y[0]]).range([0,this.height])};this.render=function(){var stackedData=this.stackData();this.discoverRange();this.renderer.render();this.updateCallbacks.forEach(function(callback){callback()})};this.update=this.render;this.stackData=function(){var data=this.series.active().map(function(d){return d.data}).map(function(d){return d.filter(function(d){return this._slice(d)},this)},this);var preserve=this.preserve;if(!preserve){this.series.forEach(function(series){if(series.scale){preserve=true}})}data=preserve?Rickshaw.clone(data):data;this.series.active().forEach(function(series,index){if(series.scale){var seriesData=data[index];if(seriesData){seriesData.forEach(function(d){d.y=series.scale(d.y)})}}});this.stackData.hooks.data.forEach(function(entry){data=entry.f.apply(self,[data])});var stackedData;if(!this.renderer.unstack){this._validateStackable();var layout=d3.layout.stack();layout.offset(self.offset);stackedData=layout(data)}stackedData=stackedData||data;if(this.renderer.unstack){stackedData.forEach(function(seriesData){seriesData.forEach(function(d){d.y0=d.y0===undefined?0:d.y0})})}this.stackData.hooks.after.forEach(function(entry){stackedData=entry.f.apply(self,[data])});var i=0;this.series.forEach(function(series){if(series.disabled)return;series.stack=stackedData[i++]});this.stackedData=stackedData;return stackedData};this._validateStackable=function(){var series=this.series;var pointsCount;series.forEach(function(s){pointsCount=pointsCount||s.data.length;if(pointsCount&&s.data.length!=pointsCount){throw"stacked series cannot have differing numbers of points: "+pointsCount+" vs "+s.data.length+"; see Rickshaw.Series.fill()"}},this)};this.stackData.hooks={data:[],after:[]};this._slice=function(d){if(this.window.xMin||this.window.xMax){var isInRange=true;if(this.window.xMin&&d.x<this.window.xMin)isInRange=false;if(this.window.xMax&&d.x>this.window.xMax)isInRange=false;return isInRange}return true};this.onUpdate=function(callback){this.updateCallbacks.push(callback)};this.onConfigure=function(callback){this.configureCallbacks.push(callback)};this.registerRenderer=function(renderer){this._renderers=this._renderers||{};this._renderers[renderer.name]=renderer};this.configure=function(args){this.config=this.config||{};if(args.width||args.height){this.setSize(args)}Rickshaw.keys(this.defaults).forEach(function(k){this.config[k]=k in args?args[k]:k in this?this[k]:this.defaults[k]},this);Rickshaw.keys(this.config).forEach(function(k){this[k]=this.config[k]},this);if("stack"in args)args.unstack=!args.stack;var renderer=args.renderer||this.renderer&&this.renderer.name||"stack";this.setRenderer(renderer,args);this.configureCallbacks.forEach(function(callback){callback(args)})};this.setRenderer=function(r,args){if(typeof r=="function"){this.renderer=new r({graph:self});this.registerRenderer(this.renderer)}else{if(!this._renderers[r]){throw"couldn't find renderer "+r}this.renderer=this._renderers[r]}if(typeof args=="object"){this.renderer.configure(args)}};this.setSize=function(args){args=args||{};if(typeof window!==undefined){var style=window.getComputedStyle(this.element,null);var elementWidth=parseInt(style.getPropertyValue("width"),10);var elementHeight=parseInt(style.getPropertyValue("height"),10)}this.width=args.width||elementWidth||400;this.height=args.height||elementHeight||250;this.vis&&this.vis.attr("width",this.width).attr("height",this.height)};this.initialize(args)};Rickshaw.namespace("Rickshaw.Fixtures.Color");Rickshaw.Fixtures.Color=function(){this.schemes={};this.schemes.spectrum14=["#ecb796","#dc8f70","#b2a470","#92875a","#716c49","#d2ed82","#bbe468","#a1d05d","#e7cbe6","#d8aad6","#a888c2","#9dc2d3","#649eb9","#387aa3"].reverse();this.schemes.spectrum2000=["#57306f","#514c76","#646583","#738394","#6b9c7d","#84b665","#a7ca50","#bfe746","#e2f528","#fff726","#ecdd00","#d4b11d","#de8800","#de4800","#c91515","#9a0000","#7b0429","#580839","#31082b"];this.schemes.spectrum2001=["#2f243f","#3c2c55","#4a3768","#565270","#6b6b7c","#72957f","#86ad6e","#a1bc5e","#b8d954","#d3e04e","#ccad2a","#cc8412","#c1521d","#ad3821","#8a1010","#681717","#531e1e","#3d1818","#320a1b"];this.schemes.classic9=["#423d4f","#4a6860","#848f39","#a2b73c","#ddcb53","#c5a32f","#7d5836","#963b20","#7c2626","#491d37","#2f254a"].reverse();this.schemes.httpStatus={503:"#ea5029",502:"#d23f14",500:"#bf3613",410:"#efacea",409:"#e291dc",403:"#f457e8",408:"#e121d2",401:"#b92dae",405:"#f47ceb",404:"#a82a9f",400:"#b263c6",301:"#6fa024",302:"#87c32b",307:"#a0d84c",304:"#28b55c",200:"#1a4f74",206:"#27839f",201:"#52adc9",202:"#7c979f",203:"#a5b8bd",204:"#c1cdd1"};this.schemes.colorwheel=["#b5b6a9","#858772","#785f43","#96557e","#4682b4","#65b9ac","#73c03a","#cb513a"].reverse();this.schemes.cool=["#5e9d2f","#73c03a","#4682b4","#7bc3b8","#a9884e","#c1b266","#a47493","#c09fb5"];this.schemes.munin=["#00cc00","#0066b3","#ff8000","#ffcc00","#330099","#990099","#ccff00","#ff0000","#808080","#008f00","#00487d","#b35a00","#b38f00","#6b006b","#8fb300","#b30000","#bebebe","#80ff80","#80c9ff","#ffc080","#ffe680","#aa80ff","#ee00cc","#ff8080","#666600","#ffbfff","#00ffcc","#cc6699","#999900"]};Rickshaw.namespace("Rickshaw.Fixtures.RandomData");Rickshaw.Fixtures.RandomData=function(timeInterval){var addData;timeInterval=timeInterval||1;var lastRandomValue=200;var timeBase=Math.floor((new Date).getTime()/1e3);this.addData=function(data){var randomValue=Math.random()*100+15+lastRandomValue;var index=data[0].length;var counter=1;data.forEach(function(series){var randomVariance=Math.random()*20;var v=randomValue/25+counter++ +(Math.cos(index*counter*11/960)+2)*15+(Math.cos(index/7)+2)*7+(Math.cos(index/17)+2)*1;series.push({x:index*timeInterval+timeBase,y:v+randomVariance})});lastRandomValue=randomValue*.85};this.removeData=function(data){data.forEach(function(series){series.shift()});timeBase+=timeInterval}};Rickshaw.namespace("Rickshaw.Fixtures.Time");Rickshaw.Fixtures.Time=function(){var self=this;this.months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];this.units=[{name:"decade",seconds:86400*365.25*10,formatter:function(d){return parseInt(d.getUTCFullYear()/10,10)*10}},{name:"year",seconds:86400*365.25,formatter:function(d){return d.getUTCFullYear()}},{name:"month",seconds:86400*30.5,formatter:function(d){return self.months[d.getUTCMonth()]}},{name:"week",seconds:86400*7,formatter:function(d){return self.formatDate(d)}},{name:"day",seconds:86400,formatter:function(d){return d.getUTCDate()}},{name:"6 hour",seconds:3600*6,formatter:function(d){return self.formatTime(d)}},{name:"hour",seconds:3600,formatter:function(d){return self.formatTime(d)}},{name:"15 minute",seconds:60*15,formatter:function(d){return self.formatTime(d)}},{name:"minute",seconds:60,formatter:function(d){return d.getUTCMinutes()}},{name:"15 second",seconds:15,formatter:function(d){return d.getUTCSeconds()+"s"}},{name:"second",seconds:1,formatter:function(d){return d.getUTCSeconds()+"s"}},{name:"decisecond",seconds:1/10,formatter:function(d){return d.getUTCMilliseconds()+"ms"}},{name:"centisecond",seconds:1/100,formatter:function(d){return d.getUTCMilliseconds()+"ms"}}];this.unit=function(unitName){return this.units.filter(function(unit){return unitName==unit.name}).shift()};this.formatDate=function(d){return d3.time.format("%b %e")(d)};this.formatTime=function(d){return d.toUTCString().match(/(\d+:\d+):/)[1]};this.ceil=function(time,unit){var date,floor,year;if(unit.name=="month"){date=new Date(time*1e3);floor=Date.UTC(date.getUTCFullYear(),date.getUTCMonth())/1e3;if(floor==time)return time;year=date.getUTCFullYear();var month=date.getUTCMonth();if(month==11){month=0;year=year+1}else{month+=1}return Date.UTC(year,month)/1e3}if(unit.name=="year"){date=new Date(time*1e3);floor=Date.UTC(date.getUTCFullYear(),0)/1e3;if(floor==time)return time;year=date.getUTCFullYear()+1;return Date.UTC(year,0)/1e3}return Math.ceil(time/unit.seconds)*unit.seconds}};Rickshaw.namespace("Rickshaw.Fixtures.Time.Local");Rickshaw.Fixtures.Time.Local=function(){var self=this;this.months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];this.units=[{name:"decade",seconds:86400*365.25*10,formatter:function(d){return parseInt(d.getFullYear()/10,10)*10}},{name:"year",seconds:86400*365.25,formatter:function(d){return d.getFullYear()}},{name:"month",seconds:86400*30.5,formatter:function(d){return self.months[d.getMonth()]}},{name:"week",seconds:86400*7,formatter:function(d){return self.formatDate(d)}},{name:"day",seconds:86400,formatter:function(d){return d.getDate()}},{name:"6 hour",seconds:3600*6,formatter:function(d){return self.formatTime(d)}},{name:"hour",seconds:3600,formatter:function(d){return self.formatTime(d)}},{name:"15 minute",seconds:60*15,formatter:function(d){return self.formatTime(d)}},{name:"minute",seconds:60,formatter:function(d){return d.getMinutes()}},{name:"15 second",seconds:15,formatter:function(d){return d.getSeconds()+"s"}},{name:"second",seconds:1,formatter:function(d){return d.getSeconds()+"s"}},{name:"decisecond",seconds:1/10,formatter:function(d){return d.getMilliseconds()+"ms"}},{name:"centisecond",seconds:1/100,formatter:function(d){return d.getMilliseconds()+"ms"}}];this.unit=function(unitName){return this.units.filter(function(unit){return unitName==unit.name}).shift()};this.formatDate=function(d){return d3.time.format("%b %e")(d)};this.formatTime=function(d){return d.toString().match(/(\d+:\d+):/)[1]};this.ceil=function(time,unit){var date,floor,year;if(unit.name=="day"){var nearFuture=new Date((time+unit.seconds-1)*1e3);var rounded=new Date(0);rounded.setMilliseconds(0);rounded.setSeconds(0);rounded.setMinutes(0);rounded.setHours(0);rounded.setDate(nearFuture.getDate());rounded.setMonth(nearFuture.getMonth());rounded.setFullYear(nearFuture.getFullYear());return rounded.getTime()/1e3}if(unit.name=="month"){date=new Date(time*1e3);floor=new Date(date.getFullYear(),date.getMonth()).getTime()/1e3;if(floor==time)return time;year=date.getFullYear();var month=date.getMonth();if(month==11){month=0;year=year+1}else{month+=1}return new Date(year,month).getTime()/1e3}if(unit.name=="year"){date=new Date(time*1e3);floor=new Date(date.getUTCFullYear(),0).getTime()/1e3;if(floor==time)return time;year=date.getFullYear()+1;return new Date(year,0).getTime()/1e3}return Math.ceil(time/unit.seconds)*unit.seconds}};Rickshaw.namespace("Rickshaw.Fixtures.Number");Rickshaw.Fixtures.Number.formatKMBT=function(y){var abs_y=Math.abs(y);if(abs_y>=1e12){return y/1e12+"T"}else if(abs_y>=1e9){return y/1e9+"B"}else if(abs_y>=1e6){return y/1e6+"M"}else if(abs_y>=1e3){return y/1e3+"K"}else if(abs_y<1&&y>0){return y.toFixed(2)}else if(abs_y===0){return""}else{return y}};Rickshaw.Fixtures.Number.formatBase1024KMGTP=function(y){var abs_y=Math.abs(y);if(abs_y>=0x4000000000000){return y/0x4000000000000+"P"}else if(abs_y>=1099511627776){return y/1099511627776+"T"}else if(abs_y>=1073741824){return y/1073741824+"G"}else if(abs_y>=1048576){return y/1048576+"M"}else if(abs_y>=1024){return y/1024+"K"}else if(abs_y<1&&y>0){return y.toFixed(2)}else if(abs_y===0){return""}else{return y}};Rickshaw.namespace("Rickshaw.Color.Palette");Rickshaw.Color.Palette=function(args){var color=new Rickshaw.Fixtures.Color;args=args||{};this.schemes={};this.scheme=color.schemes[args.scheme]||args.scheme||color.schemes.colorwheel;this.runningIndex=0;this.generatorIndex=0;if(args.interpolatedStopCount){var schemeCount=this.scheme.length-1;var i,j,scheme=[];for(i=0;i<schemeCount;i++){scheme.push(this.scheme[i]);var generator=d3.interpolateHsl(this.scheme[i],this.scheme[i+1]);for(j=1;j<args.interpolatedStopCount;j++){scheme.push(generator(1/args.interpolatedStopCount*j))}}scheme.push(this.scheme[this.scheme.length-1]);this.scheme=scheme}this.rotateCount=this.scheme.length;this.color=function(key){return this.scheme[key]||this.scheme[this.runningIndex++]||this.interpolateColor()||"#808080"};this.interpolateColor=function(){if(!Array.isArray(this.scheme))return;var color;if(this.generatorIndex==this.rotateCount*2-1){color=d3.interpolateHsl(this.scheme[this.generatorIndex],this.scheme[0])(.5);this.generatorIndex=0;this.rotateCount*=2}else{color=d3.interpolateHsl(this.scheme[this.generatorIndex],this.scheme[this.generatorIndex+1])(.5);this.generatorIndex++}this.scheme.push(color);return color}};Rickshaw.namespace("Rickshaw.Graph.Ajax");Rickshaw.Graph.Ajax=Rickshaw.Class.create({initialize:function(args){this.dataURL=args.dataURL;this.onData=args.onData||function(d){return d};this.onComplete=args.onComplete||function(){};this.onError=args.onError||function(){};this.args=args;this.request()},request:function(){jQuery.ajax({url:this.dataURL,dataType:"json",success:this.success.bind(this),error:this.error.bind(this)})},error:function(){console.log("error loading dataURL: "+this.dataURL);this.onError(this)},success:function(data,status){data=this.onData(data);this.args.series=this._splice({data:data,series:this.args.series});this.graph=this.graph||new Rickshaw.Graph(this.args);this.graph.render();this.onComplete(this)},_splice:function(args){var data=args.data;var series=args.series;if(!args.series)return data;series.forEach(function(s){var seriesKey=s.key||s.name;if(!seriesKey)throw"series needs a key or a name";data.forEach(function(d){var dataKey=d.key||d.name;if(!dataKey)throw"data needs a key or a name";if(seriesKey==dataKey){var properties=["color","name","data"];properties.forEach(function(p){if(d[p])s[p]=d[p]})}})});return series}});Rickshaw.namespace("Rickshaw.Graph.Annotate");Rickshaw.Graph.Annotate=function(args){var graph=this.graph=args.graph;this.elements={timeline:args.element};var self=this;this.data={};this.elements.timeline.classList.add("rickshaw_annotation_timeline");this.add=function(time,content,end_time){self.data[time]=self.data[time]||{boxes:[]};self.data[time].boxes.push({content:content,end:end_time})};this.update=function(){Rickshaw.keys(self.data).forEach(function(time){var annotation=self.data[time];var left=self.graph.x(time);if(left<0||left>self.graph.x.range()[1]){if(annotation.element){annotation.line.classList.add("offscreen");annotation.element.style.display="none"}annotation.boxes.forEach(function(box){if(box.rangeElement)box.rangeElement.classList.add("offscreen")});return}if(!annotation.element){var element=annotation.element=document.createElement("div");element.classList.add("annotation");this.elements.timeline.appendChild(element);element.addEventListener("click",function(e){element.classList.toggle("active");annotation.line.classList.toggle("active");annotation.boxes.forEach(function(box){if(box.rangeElement)box.rangeElement.classList.toggle("active")})},false)}annotation.element.style.left=left+"px";annotation.element.style.display="block";annotation.boxes.forEach(function(box){var element=box.element;if(!element){element=box.element=document.createElement("div");element.classList.add("content");element.innerHTML=box.content;annotation.element.appendChild(element);annotation.line=document.createElement("div");annotation.line.classList.add("annotation_line");self.graph.element.appendChild(annotation.line);if(box.end){box.rangeElement=document.createElement("div");box.rangeElement.classList.add("annotation_range");self.graph.element.appendChild(box.rangeElement)}}if(box.end){var annotationRangeStart=left;var annotationRangeEnd=Math.min(self.graph.x(box.end),self.graph.x.range()[1]);if(annotationRangeStart>annotationRangeEnd){annotationRangeEnd=left;annotationRangeStart=Math.max(self.graph.x(box.end),self.graph.x.range()[0])}var annotationRangeWidth=annotationRangeEnd-annotationRangeStart;box.rangeElement.style.left=annotationRangeStart+"px";box.rangeElement.style.width=annotationRangeWidth+"px";box.rangeElement.classList.remove("offscreen")}annotation.line.classList.remove("offscreen");annotation.line.style.left=left+"px"})},this)};this.graph.onUpdate(function(){self.update()})};Rickshaw.namespace("Rickshaw.Graph.Axis.Time");Rickshaw.Graph.Axis.Time=function(args){var self=this;this.graph=args.graph;this.elements=[];this.ticksTreatment=args.ticksTreatment||"plain";this.fixedTimeUnit=args.timeUnit;var time=args.timeFixture||new Rickshaw.Fixtures.Time;this.appropriateTimeUnit=function(){var unit;var units=time.units;var domain=this.graph.x.domain();var rangeSeconds=domain[1]-domain[0];units.forEach(function(u){if(Math.floor(rangeSeconds/u.seconds)>=2){unit=unit||u}});return unit||time.units[time.units.length-1]};this.tickOffsets=function(){var domain=this.graph.x.domain();var unit=this.fixedTimeUnit||this.appropriateTimeUnit();var count=Math.ceil((domain[1]-domain[0])/unit.seconds);var runningTick=domain[0];var offsets=[];for(var i=0;i<count;i++){var tickValue=time.ceil(runningTick,unit);runningTick=tickValue+unit.seconds/2;offsets.push({value:tickValue,unit:unit})}return offsets};this.render=function(){this.elements.forEach(function(e){e.parentNode.removeChild(e)});this.elements=[];var offsets=this.tickOffsets();offsets.forEach(function(o){if(self.graph.x(o.value)>self.graph.x.range()[1])return;var element=document.createElement("div");element.style.left=self.graph.x(o.value)+"px";element.classList.add("x_tick");element.classList.add(self.ticksTreatment);var title=document.createElement("div");title.classList.add("title");title.innerHTML=o.unit.formatter(new Date(o.value*1e3));element.appendChild(title);self.graph.element.appendChild(element);self.elements.push(element)})};this.graph.onUpdate(function(){self.render()})};Rickshaw.namespace("Rickshaw.Graph.Axis.X");Rickshaw.Graph.Axis.X=function(args){var self=this;var berthRate=.1;this.initialize=function(args){this.graph=args.graph;this.orientation=args.orientation||"top";this.pixelsPerTick=args.pixelsPerTick||75;if(args.ticks)this.staticTicks=args.ticks;if(args.tickValues)this.tickValues=args.tickValues;this.tickSize=args.tickSize||4;this.ticksTreatment=args.ticksTreatment||"plain";if(args.element){this.element=args.element;this._discoverSize(args.element,args);this.vis=d3.select(args.element).append("svg:svg").attr("height",this.height).attr("width",this.width).attr("class","rickshaw_graph x_axis_d3");this.element=this.vis[0][0];this.element.style.position="relative";this.setSize({width:args.width,height:args.height})}else{this.vis=this.graph.vis}this.graph.onUpdate(function(){self.render()})};this.setSize=function(args){args=args||{};if(!this.element)return;this._discoverSize(this.element.parentNode,args);this.vis.attr("height",this.height).attr("width",this.width*(1+berthRate));var berth=Math.floor(this.width*berthRate/2);this.element.style.left=-1*berth+"px"};this.render=function(){if(this._renderWidth!==undefined&&this.graph.width!==this._renderWidth)this.setSize({auto:true});var axis=d3.svg.axis().scale(this.graph.x).orient(this.orientation);axis.tickFormat(args.tickFormat||function(x){return x});if(this.tickValues)axis.tickValues(this.tickValues);this.ticks=this.staticTicks||Math.floor(this.graph.width/this.pixelsPerTick);var berth=Math.floor(this.width*berthRate/2)||0;var transform;if(this.orientation=="top"){var yOffset=this.height||this.graph.height;transform="translate("+berth+","+yOffset+")"}else{transform="translate("+berth+", 0)"}if(this.element){this.vis.selectAll("*").remove()}this.vis.append("svg:g").attr("class",["x_ticks_d3",this.ticksTreatment].join(" ")).attr("transform",transform).call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));var gridSize=(this.orientation=="bottom"?1:-1)*this.graph.height;this.graph.vis.append("svg:g").attr("class","x_grid_d3").call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize)).selectAll("text").each(function(){this.parentNode.setAttribute("data-x-value",this.textContent)});this._renderHeight=this.graph.height};this._discoverSize=function(element,args){if(typeof window!=="undefined"){var style=window.getComputedStyle(element,null);var elementHeight=parseInt(style.getPropertyValue("height"),10);if(!args.auto){var elementWidth=parseInt(style.getPropertyValue("width"),10)}}this.width=(args.width||elementWidth||this.graph.width)*(1+berthRate);this.height=args.height||elementHeight||40};this.initialize(args)};Rickshaw.namespace("Rickshaw.Graph.Axis.Y");Rickshaw.Graph.Axis.Y=Rickshaw.Class.create({initialize:function(args){this.graph=args.graph;this.orientation=args.orientation||"right";this.pixelsPerTick=args.pixelsPerTick||75;if(args.ticks)this.staticTicks=args.ticks;if(args.tickValues)this.tickValues=args.tickValues;this.tickSize=args.tickSize||4;this.ticksTreatment=args.ticksTreatment||"plain";this.tickFormat=args.tickFormat||function(y){return y};this.berthRate=.1;if(args.element){this.element=args.element;this.vis=d3.select(args.element).append("svg:svg").attr("class","rickshaw_graph y_axis");this.element=this.vis[0][0];this.element.style.position="relative";this.setSize({width:args.width,height:args.height})}else{this.vis=this.graph.vis}var self=this;this.graph.onUpdate(function(){self.render()})},setSize:function(args){args=args||{};if(!this.element)return;if(typeof window!=="undefined"){var style=window.getComputedStyle(this.element.parentNode,null);var elementWidth=parseInt(style.getPropertyValue("width"),10);if(!args.auto){var elementHeight=parseInt(style.getPropertyValue("height"),10)}}this.width=args.width||elementWidth||this.graph.width*this.berthRate;this.height=args.height||elementHeight||this.graph.height;this.vis.attr("width",this.width).attr("height",this.height*(1+this.berthRate));var berth=this.height*this.berthRate;if(this.orientation=="left"){this.element.style.top=-1*berth+"px"}},render:function(){if(this._renderHeight!==undefined&&this.graph.height!==this._renderHeight)this.setSize({auto:true});this.ticks=this.staticTicks||Math.floor(this.graph.height/this.pixelsPerTick);var axis=this._drawAxis(this.graph.y);this._drawGrid(axis);this._renderHeight=this.graph.height},_drawAxis:function(scale){var axis=d3.svg.axis().scale(scale).orient(this.orientation);axis.tickFormat(this.tickFormat);if(this.tickValues)axis.tickValues(this.tickValues);if(this.orientation=="left"){var berth=this.height*this.berthRate;var transform="translate("+this.width+", "+berth+")"}if(this.element){this.vis.selectAll("*").remove()}this.vis.append("svg:g").attr("class",["y_ticks",this.ticksTreatment].join(" ")).attr("transform",transform).call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));return axis},_drawGrid:function(axis){var gridSize=(this.orientation=="right"?1:-1)*this.graph.width;this.graph.vis.append("svg:g").attr("class","y_grid").call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize)).selectAll("text").each(function(){this.parentNode.setAttribute("data-y-value",this.textContent)
+})}});Rickshaw.namespace("Rickshaw.Graph.Axis.Y.Scaled");Rickshaw.Graph.Axis.Y.Scaled=Rickshaw.Class.create(Rickshaw.Graph.Axis.Y,{initialize:function($super,args){if(typeof args.scale==="undefined"){throw new Error("Scaled requires scale")}this.scale=args.scale;if(typeof args.grid==="undefined"){this.grid=true}else{this.grid=args.grid}$super(args)},_drawAxis:function($super,scale){var domain=this.scale.domain();var renderDomain=this.graph.renderer.domain().y;var extents=[Math.min.apply(Math,domain),Math.max.apply(Math,domain)];var extentMap=d3.scale.linear().domain([0,1]).range(extents);var adjExtents=[extentMap(renderDomain[0]),extentMap(renderDomain[1])];var adjustment=d3.scale.linear().domain(extents).range(adjExtents);var adjustedScale=this.scale.copy().domain(domain.map(adjustment)).range(scale.range());return $super(adjustedScale)},_drawGrid:function($super,axis){if(this.grid){$super(axis)}}});Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Highlight");Rickshaw.Graph.Behavior.Series.Highlight=function(args){this.graph=args.graph;this.legend=args.legend;var self=this;var colorSafe={};var activeLine=null;var disabledColor=args.disabledColor||function(seriesColor){return d3.interpolateRgb(seriesColor,d3.rgb("#d8d8d8"))(.8).toString()};this.addHighlightEvents=function(l){l.element.addEventListener("mouseover",function(e){if(activeLine)return;else activeLine=l;self.legend.lines.forEach(function(line){if(l===line){if(self.graph.renderer.unstack&&(line.series.renderer?line.series.renderer.unstack:true)){var seriesIndex=self.graph.series.indexOf(line.series);line.originalIndex=seriesIndex;var series=self.graph.series.splice(seriesIndex,1)[0];self.graph.series.push(series)}return}colorSafe[line.series.name]=colorSafe[line.series.name]||line.series.color;line.series.color=disabledColor(line.series.color)});self.graph.update()},false);l.element.addEventListener("mouseout",function(e){if(!activeLine)return;else activeLine=null;self.legend.lines.forEach(function(line){if(l===line&&line.hasOwnProperty("originalIndex")){var series=self.graph.series.pop();self.graph.series.splice(line.originalIndex,0,series);delete line.originalIndex}if(colorSafe[line.series.name]){line.series.color=colorSafe[line.series.name]}});self.graph.update()},false)};if(this.legend){this.legend.lines.forEach(function(l){self.addHighlightEvents(l)})}};Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Order");Rickshaw.Graph.Behavior.Series.Order=function(args){this.graph=args.graph;this.legend=args.legend;var self=this;if(typeof window.jQuery=="undefined"){throw"couldn't find jQuery at window.jQuery"}if(typeof window.jQuery.ui=="undefined"){throw"couldn't find jQuery UI at window.jQuery.ui"}jQuery(function(){jQuery(self.legend.list).sortable({containment:"parent",tolerance:"pointer",update:function(event,ui){var series=[];jQuery(self.legend.list).find("li").each(function(index,item){if(!item.series)return;series.push(item.series)});for(var i=self.graph.series.length-1;i>=0;i--){self.graph.series[i]=series.shift()}self.graph.update()}});jQuery(self.legend.list).disableSelection()});this.graph.onUpdate(function(){var h=window.getComputedStyle(self.legend.element).height;self.legend.element.style.height=h})};Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Toggle");Rickshaw.Graph.Behavior.Series.Toggle=function(args){this.graph=args.graph;this.legend=args.legend;var self=this;this.addAnchor=function(line){var anchor=document.createElement("a");anchor.innerHTML="&#10004;";anchor.classList.add("action");line.element.insertBefore(anchor,line.element.firstChild);anchor.onclick=function(e){if(line.series.disabled){line.series.enable();line.element.classList.remove("disabled")}else{if(this.graph.series.filter(function(s){return!s.disabled}).length<=1)return;line.series.disable();line.element.classList.add("disabled")}self.graph.update()}.bind(this);var label=line.element.getElementsByTagName("span")[0];label.onclick=function(e){var disableAllOtherLines=line.series.disabled;if(!disableAllOtherLines){for(var i=0;i<self.legend.lines.length;i++){var l=self.legend.lines[i];if(line.series===l.series){}else if(l.series.disabled){}else{disableAllOtherLines=true;break}}}if(disableAllOtherLines){line.series.enable();line.element.classList.remove("disabled");self.legend.lines.forEach(function(l){if(line.series===l.series){}else{l.series.disable();l.element.classList.add("disabled")}})}else{self.legend.lines.forEach(function(l){l.series.enable();l.element.classList.remove("disabled")})}self.graph.update()}};if(this.legend){var $=jQuery;if(typeof $!="undefined"&&$(this.legend.list).sortable){$(this.legend.list).sortable({start:function(event,ui){ui.item.bind("no.onclick",function(event){event.preventDefault()})},stop:function(event,ui){setTimeout(function(){ui.item.unbind("no.onclick")},250)}})}this.legend.lines.forEach(function(l){self.addAnchor(l)})}this._addBehavior=function(){this.graph.series.forEach(function(s){s.disable=function(){if(self.graph.series.length<=1){throw"only one series left"}s.disabled=true};s.enable=function(){s.disabled=false}})};this._addBehavior();this.updateBehaviour=function(){this._addBehavior()}};Rickshaw.namespace("Rickshaw.Graph.HoverDetail");Rickshaw.Graph.HoverDetail=Rickshaw.Class.create({initialize:function(args){var graph=this.graph=args.graph;this.xFormatter=args.xFormatter||function(x){return new Date(x*1e3).toUTCString()};this.yFormatter=args.yFormatter||function(y){return y===null?y:y.toFixed(2)};var element=this.element=document.createElement("div");element.className="detail";this.visible=true;graph.element.appendChild(element);this.lastEvent=null;this._addListeners();this.onShow=args.onShow;this.onHide=args.onHide;this.onRender=args.onRender;this.formatter=args.formatter||this.formatter},formatter:function(series,x,y,formattedX,formattedY,d){return series.name+":&nbsp;"+formattedY},update:function(e){e=e||this.lastEvent;if(!e)return;this.lastEvent=e;if(!e.target.nodeName.match(/^(path|svg|rect|circle)$/))return;var graph=this.graph;var eventX=e.offsetX||e.layerX;var eventY=e.offsetY||e.layerY;var j=0;var points=[];var nearestPoint;this.graph.series.active().forEach(function(series){var data=this.graph.stackedData[j++];if(!data.length)return;var domainX=graph.x.invert(eventX);var domainIndexScale=d3.scale.linear().domain([data[0].x,data.slice(-1)[0].x]).range([0,data.length-1]);var approximateIndex=Math.round(domainIndexScale(domainX));if(approximateIndex==data.length-1)approximateIndex--;var dataIndex=Math.min(approximateIndex||0,data.length-1);for(var i=approximateIndex;i<data.length-1;){if(!data[i]||!data[i+1])break;if(data[i].x<=domainX&&data[i+1].x>domainX){dataIndex=Math.abs(domainX-data[i].x)<Math.abs(domainX-data[i+1].x)?i:i+1;break}if(data[i+1].x<=domainX){i++}else{i--}}if(dataIndex<0)dataIndex=0;var value=data[dataIndex];var distance=Math.sqrt(Math.pow(Math.abs(graph.x(value.x)-eventX),2)+Math.pow(Math.abs(graph.y(value.y+value.y0)-eventY),2));var xFormatter=series.xFormatter||this.xFormatter;var yFormatter=series.yFormatter||this.yFormatter;var point={formattedXValue:xFormatter(value.x),formattedYValue:yFormatter(series.scale?series.scale.invert(value.y):value.y),series:series,value:value,distance:distance,order:j,name:series.name};if(!nearestPoint||distance<nearestPoint.distance){nearestPoint=point}points.push(point)},this);if(!nearestPoint)return;nearestPoint.active=true;var domainX=nearestPoint.value.x;var formattedXValue=nearestPoint.formattedXValue;this.element.innerHTML="";this.element.style.left=graph.x(domainX)+"px";this.visible&&this.render({points:points,detail:points,mouseX:eventX,mouseY:eventY,formattedXValue:formattedXValue,domainX:domainX})},hide:function(){this.visible=false;this.element.classList.add("inactive");if(typeof this.onHide=="function"){this.onHide()}},show:function(){this.visible=true;this.element.classList.remove("inactive");if(typeof this.onShow=="function"){this.onShow()}},render:function(args){var graph=this.graph;var points=args.points;var point=points.filter(function(p){return p.active}).shift();if(point.value.y===null)return;var formattedXValue=point.formattedXValue;var formattedYValue=point.formattedYValue;this.element.innerHTML="";this.element.style.left=graph.x(point.value.x)+"px";var xLabel=document.createElement("div");xLabel.className="x_label";xLabel.innerHTML=formattedXValue;this.element.appendChild(xLabel);var item=document.createElement("div");item.className="item";var series=point.series;var actualY=series.scale?series.scale.invert(point.value.y):point.value.y;item.innerHTML=this.formatter(series,point.value.x,actualY,formattedXValue,formattedYValue,point);item.style.top=this.graph.y(point.value.y0+point.value.y)+"px";this.element.appendChild(item);var dot=document.createElement("div");dot.className="dot";dot.style.top=item.style.top;dot.style.borderColor=series.color;this.element.appendChild(dot);if(point.active){item.classList.add("active");dot.classList.add("active")}var alignables=[xLabel,item];alignables.forEach(function(el){el.classList.add("left")});this.show();var leftAlignError=this._calcLayoutError(alignables);if(leftAlignError>0){alignables.forEach(function(el){el.classList.remove("left");el.classList.add("right")});var rightAlignError=this._calcLayoutError(alignables);if(rightAlignError>leftAlignError){alignables.forEach(function(el){el.classList.remove("right");el.classList.add("left")})}}if(typeof this.onRender=="function"){this.onRender(args)}},_calcLayoutError:function(alignables){var parentRect=this.element.parentNode.getBoundingClientRect();var error=0;var alignRight=alignables.forEach(function(el){var rect=el.getBoundingClientRect();if(!rect.width){return}if(rect.right>parentRect.right){error+=rect.right-parentRect.right}if(rect.left<parentRect.left){error+=parentRect.left-rect.left}});return error},_addListeners:function(){this.graph.element.addEventListener("mousemove",function(e){this.visible=true;this.update(e)}.bind(this),false);this.graph.onUpdate(function(){this.update()}.bind(this));this.graph.element.addEventListener("mouseout",function(e){if(e.relatedTarget&&!(e.relatedTarget.compareDocumentPosition(this.graph.element)&Node.DOCUMENT_POSITION_CONTAINS)){this.hide()}}.bind(this),false)}});Rickshaw.namespace("Rickshaw.Graph.JSONP");Rickshaw.Graph.JSONP=Rickshaw.Class.create(Rickshaw.Graph.Ajax,{request:function(){jQuery.ajax({url:this.dataURL,dataType:"jsonp",success:this.success.bind(this),error:this.error.bind(this)})}});Rickshaw.namespace("Rickshaw.Graph.Legend");Rickshaw.Graph.Legend=Rickshaw.Class.create({className:"rickshaw_legend",initialize:function(args){this.element=args.element;this.graph=args.graph;this.naturalOrder=args.naturalOrder;this.element.classList.add(this.className);this.list=document.createElement("ul");this.element.appendChild(this.list);this.render();this.graph.onUpdate(function(){})},render:function(){var self=this;while(this.list.firstChild){this.list.removeChild(this.list.firstChild)}this.lines=[];var series=this.graph.series.map(function(s){return s});if(!this.naturalOrder){series=series.reverse()}series.forEach(function(s){self.addLine(s)})},addLine:function(series){var line=document.createElement("li");line.className="line";if(series.disabled){line.className+=" disabled"}if(series.className){d3.select(line).classed(series.className,true)}var swatch=document.createElement("div");swatch.className="swatch";swatch.style.backgroundColor=series.color;line.appendChild(swatch);var label=document.createElement("span");label.className="label";label.innerHTML=series.name;line.appendChild(label);this.list.appendChild(line);line.series=series;if(series.noLegend){line.style.display="none"}var _line={element:line,series:series};if(this.shelving){this.shelving.addAnchor(_line);this.shelving.updateBehaviour()}if(this.highlighter){this.highlighter.addHighlightEvents(_line)}this.lines.push(_line);return line}});Rickshaw.namespace("Rickshaw.Graph.RangeSlider");Rickshaw.Graph.RangeSlider=Rickshaw.Class.create({initialize:function(args){var element=this.element=args.element;var graph=this.graph=args.graph;this.slideCallbacks=[];this.build();graph.onUpdate(function(){this.update()}.bind(this))},build:function(){var element=this.element;var graph=this.graph;var $=jQuery;var domain=graph.dataDomain();var self=this;$(function(){$(element).slider({range:true,min:domain[0],max:domain[1],values:[domain[0],domain[1]],slide:function(event,ui){if(ui.values[1]<=ui.values[0])return;graph.window.xMin=ui.values[0];graph.window.xMax=ui.values[1];graph.update();var domain=graph.dataDomain();if(domain[0]==ui.values[0]){graph.window.xMin=undefined}if(domain[1]==ui.values[1]){graph.window.xMax=undefined}self.slideCallbacks.forEach(function(callback){callback(graph,graph.window.xMin,graph.window.xMax)})}})});$(element)[0].style.width=graph.width+"px"},update:function(){var element=this.element;var graph=this.graph;var $=jQuery;var values=$(element).slider("option","values");var domain=graph.dataDomain();$(element).slider("option","min",domain[0]);$(element).slider("option","max",domain[1]);if(graph.window.xMin==null){values[0]=domain[0]}if(graph.window.xMax==null){values[1]=domain[1]}$(element).slider("option","values",values)},onSlide:function(callback){this.slideCallbacks.push(callback)}});Rickshaw.namespace("Rickshaw.Graph.RangeSlider.Preview");Rickshaw.Graph.RangeSlider.Preview=Rickshaw.Class.create({initialize:function(args){if(!args.element)throw"Rickshaw.Graph.RangeSlider.Preview needs a reference to an element";if(!args.graph&&!args.graphs)throw"Rickshaw.Graph.RangeSlider.Preview needs a reference to an graph or an array of graphs";this.element=args.element;this.element.style.position="relative";this.graphs=args.graph?[args.graph]:args.graphs;this.defaults={height:75,width:400,gripperColor:undefined,frameTopThickness:3,frameHandleThickness:10,frameColor:"#d4d4d4",frameOpacity:1,minimumFrameWidth:0,heightRatio:.2};this.heightRatio=args.heightRatio||this.defaults.heightRatio;this.defaults.gripperColor=d3.rgb(this.defaults.frameColor).darker().toString();this.configureCallbacks=[];this.slideCallbacks=[];this.previews=[];if(!args.width)this.widthFromGraph=true;if(!args.height)this.heightFromGraph=true;if(this.widthFromGraph||this.heightFromGraph){this.graphs[0].onConfigure(function(){this.configure(args);this.render()}.bind(this))}args.width=args.width||this.graphs[0].width||this.defaults.width;args.height=args.height||this.graphs[0].height*this.heightRatio||this.defaults.height;this.configure(args);this.render()},onSlide:function(callback){this.slideCallbacks.push(callback)},onConfigure:function(callback){this.configureCallbacks.push(callback)},configure:function(args){this.config=this.config||{};this.configureCallbacks.forEach(function(callback){callback(args)});Rickshaw.keys(this.defaults).forEach(function(k){this.config[k]=k in args?args[k]:k in this.config?this.config[k]:this.defaults[k]},this);if("width"in args||"height"in args){if(this.widthFromGraph){this.config.width=this.graphs[0].width}if(this.heightFromGraph){this.config.height=this.graphs[0].height*this.heightRatio;this.previewHeight=this.config.height}this.previews.forEach(function(preview){var height=this.previewHeight/this.graphs.length-this.config.frameTopThickness*2;var width=this.config.width-this.config.frameHandleThickness*2;preview.setSize({width:width,height:height});if(this.svg){var svgHeight=height+this.config.frameHandleThickness*2;var svgWidth=width+this.config.frameHandleThickness*2;this.svg.style("width",svgWidth+"px");this.svg.style("height",svgHeight+"px")}},this)}},render:function(){var self=this;this.svg=d3.select(this.element).selectAll("svg.rickshaw_range_slider_preview").data([null]);this.previewHeight=this.config.height-this.config.frameTopThickness*2;this.previewWidth=this.config.width-this.config.frameHandleThickness*2;this.currentFrame=[0,this.previewWidth];var buildGraph=function(parent,index){var graphArgs=Rickshaw.extend({},parent.config);var height=self.previewHeight/self.graphs.length;var renderer=parent.renderer.name;Rickshaw.extend(graphArgs,{element:this.appendChild(document.createElement("div")),height:height,width:self.previewWidth,series:parent.series,renderer:renderer});var graph=new Rickshaw.Graph(graphArgs);self.previews.push(graph);parent.onUpdate(function(){graph.render();self.render()});parent.onConfigure(function(args){delete args.height;args.width=args.width-self.config.frameHandleThickness*2;graph.configure(args);graph.render()});graph.render()};var graphContainer=d3.select(this.element).selectAll("div.rickshaw_range_slider_preview_container").data(this.graphs);var translateCommand="translate("+this.config.frameHandleThickness+"px, "+this.config.frameTopThickness+"px)";graphContainer.enter().append("div").classed("rickshaw_range_slider_preview_container",true).style("-webkit-transform",translateCommand).style("-moz-transform",translateCommand).style("-ms-transform",translateCommand).style("transform",translateCommand).each(buildGraph);graphContainer.exit().remove();var masterGraph=this.graphs[0];var domainScale=d3.scale.linear().domain([0,this.previewWidth]).range(masterGraph.dataDomain());var currentWindow=[masterGraph.window.xMin,masterGraph.window.xMax];this.currentFrame[0]=currentWindow[0]===undefined?0:Math.round(domainScale.invert(currentWindow[0]));if(this.currentFrame[0]<0)this.currentFrame[0]=0;this.currentFrame[1]=currentWindow[1]===undefined?this.previewWidth:domainScale.invert(currentWindow[1]);if(this.currentFrame[1]-this.currentFrame[0]<self.config.minimumFrameWidth){this.currentFrame[1]=(this.currentFrame[0]||0)+self.config.minimumFrameWidth}this.svg.enter().append("svg").classed("rickshaw_range_slider_preview",true).style("height",this.config.height+"px").style("width",this.config.width+"px").style("position","absolute").style("top",0);this._renderDimming();this._renderFrame();this._renderGrippers();this._renderHandles();this._renderMiddle();this._registerMouseEvents()},_renderDimming:function(){var element=this.svg.selectAll("path.dimming").data([null]);element.enter().append("path").attr("fill","white").attr("fill-opacity","0.7").attr("fill-rule","evenodd").classed("dimming",true);var path="";path+=" M "+this.config.frameHandleThickness+" "+this.config.frameTopThickness;path+=" h "+this.previewWidth;path+=" v "+this.previewHeight;path+=" h "+-this.previewWidth;path+=" z ";path+=" M "+Math.max(this.currentFrame[0],this.config.frameHandleThickness)+" "+this.config.frameTopThickness;path+=" H "+Math.min(this.currentFrame[1]+this.config.frameHandleThickness*2,this.previewWidth+this.config.frameHandleThickness);path+=" v "+this.previewHeight;path+=" H "+Math.max(this.currentFrame[0],this.config.frameHandleThickness);path+=" z";element.attr("d",path)},_renderFrame:function(){var element=this.svg.selectAll("path.frame").data([null]);element.enter().append("path").attr("stroke","white").attr("stroke-width","1px").attr("stroke-linejoin","round").attr("fill",this.config.frameColor).attr("fill-opacity",this.config.frameOpacity).attr("fill-rule","evenodd").classed("frame",true);var path="";path+=" M "+this.currentFrame[0]+" 0";path+=" H "+(this.currentFrame[1]+this.config.frameHandleThickness*2);path+=" V "+this.config.height;path+=" H "+this.currentFrame[0];path+=" z";path+=" M "+(this.currentFrame[0]+this.config.frameHandleThickness)+" "+this.config.frameTopThickness;path+=" H "+(this.currentFrame[1]+this.config.frameHandleThickness);path+=" v "+this.previewHeight;path+=" H "+(this.currentFrame[0]+this.config.frameHandleThickness);path+=" z";element.attr("d",path)},_renderGrippers:function(){var gripper=this.svg.selectAll("path.gripper").data([null]);gripper.enter().append("path").attr("stroke",this.config.gripperColor).classed("gripper",true);var path="";[.4,.6].forEach(function(spacing){path+=" M "+Math.round(this.currentFrame[0]+this.config.frameHandleThickness*spacing)+" "+Math.round(this.config.height*.3);path+=" V "+Math.round(this.config.height*.7);path+=" M "+Math.round(this.currentFrame[1]+this.config.frameHandleThickness*(1+spacing))+" "+Math.round(this.config.height*.3);path+=" V "+Math.round(this.config.height*.7)}.bind(this));gripper.attr("d",path)},_renderHandles:function(){var leftHandle=this.svg.selectAll("rect.left_handle").data([null]);leftHandle.enter().append("rect").attr("width",this.config.frameHandleThickness).style("cursor","ew-resize").style("fill-opacity","0").classed("left_handle",true);leftHandle.attr("x",this.currentFrame[0]).attr("height",this.config.height);var rightHandle=this.svg.selectAll("rect.right_handle").data([null]);rightHandle.enter().append("rect").attr("width",this.config.frameHandleThickness).style("cursor","ew-resize").style("fill-opacity","0").classed("right_handle",true);rightHandle.attr("x",this.currentFrame[1]+this.config.frameHandleThickness).attr("height",this.config.height)},_renderMiddle:function(){var middleHandle=this.svg.selectAll("rect.middle_handle").data([null]);middleHandle.enter().append("rect").style("cursor","move").style("fill-opacity","0").classed("middle_handle",true);middleHandle.attr("width",Math.max(0,this.currentFrame[1]-this.currentFrame[0])).attr("x",this.currentFrame[0]+this.config.frameHandleThickness).attr("height",this.config.height)},_registerMouseEvents:function(){var element=d3.select(this.element);var drag={target:null,start:null,stop:null,left:false,right:false,rigid:false};var self=this;function onMousemove(datum,index){drag.stop=self._getClientXFromEvent(d3.event,drag);var distanceTraveled=drag.stop-drag.start;var frameAfterDrag=self.frameBeforeDrag.slice(0);var minimumFrameWidth=self.config.minimumFrameWidth;if(drag.rigid){minimumFrameWidth=self.frameBeforeDrag[1]-self.frameBeforeDrag[0]}if(drag.left){frameAfterDrag[0]=Math.max(frameAfterDrag[0]+distanceTraveled,0)}if(drag.right){frameAfterDrag[1]=Math.min(frameAfterDrag[1]+distanceTraveled,self.previewWidth)}var currentFrameWidth=frameAfterDrag[1]-frameAfterDrag[0];if(currentFrameWidth<=minimumFrameWidth){if(drag.left){frameAfterDrag[0]=frameAfterDrag[1]-minimumFrameWidth}if(drag.right){frameAfterDrag[1]=frameAfterDrag[0]+minimumFrameWidth}if(frameAfterDrag[0]<=0){frameAfterDrag[1]-=frameAfterDrag[0];frameAfterDrag[0]=0}if(frameAfterDrag[1]>=self.previewWidth){frameAfterDrag[0]-=frameAfterDrag[1]-self.previewWidth;frameAfterDrag[1]=self.previewWidth}}self.graphs.forEach(function(graph){var domainScale=d3.scale.linear().interpolate(d3.interpolateNumber).domain([0,self.previewWidth]).range(graph.dataDomain());var windowAfterDrag=[domainScale(frameAfterDrag[0]),domainScale(frameAfterDrag[1])];self.slideCallbacks.forEach(function(callback){callback(graph,windowAfterDrag[0],windowAfterDrag[1])});if(frameAfterDrag[0]===0){windowAfterDrag[0]=undefined}if(frameAfterDrag[1]===self.previewWidth){windowAfterDrag[1]=undefined}graph.window.xMin=windowAfterDrag[0];graph.window.xMax=windowAfterDrag[1];graph.update()})}function onMousedown(){drag.target=d3.event.target;drag.start=self._getClientXFromEvent(d3.event,drag);self.frameBeforeDrag=self.currentFrame.slice();d3.event.preventDefault?d3.event.preventDefault():d3.event.returnValue=false;d3.select(document).on("mousemove.rickshaw_range_slider_preview",onMousemove);d3.select(document).on("mouseup.rickshaw_range_slider_preview",onMouseup);d3.select(document).on("touchmove.rickshaw_range_slider_preview",onMousemove);d3.select(document).on("touchend.rickshaw_range_slider_preview",onMouseup);d3.select(document).on("touchcancel.rickshaw_range_slider_preview",onMouseup)}function onMousedownLeftHandle(datum,index){drag.left=true;onMousedown()}function onMousedownRightHandle(datum,index){drag.right=true;onMousedown()}function onMousedownMiddleHandle(datum,index){drag.left=true;drag.right=true;drag.rigid=true;onMousedown()}function onMouseup(datum,index){d3.select(document).on("mousemove.rickshaw_range_slider_preview",null);d3.select(document).on("mouseup.rickshaw_range_slider_preview",null);d3.select(document).on("touchmove.rickshaw_range_slider_preview",null);d3.select(document).on("touchend.rickshaw_range_slider_preview",null);d3.select(document).on("touchcancel.rickshaw_range_slider_preview",null);delete self.frameBeforeDrag;drag.left=false;drag.right=false;drag.rigid=false}element.select("rect.left_handle").on("mousedown",onMousedownLeftHandle);element.select("rect.right_handle").on("mousedown",onMousedownRightHandle);element.select("rect.middle_handle").on("mousedown",onMousedownMiddleHandle);element.select("rect.left_handle").on("touchstart",onMousedownLeftHandle);element.select("rect.right_handle").on("touchstart",onMousedownRightHandle);element.select("rect.middle_handle").on("touchstart",onMousedownMiddleHandle)},_getClientXFromEvent:function(event,drag){switch(event.type){case"touchstart":case"touchmove":var touchList=event.changedTouches;var touch=null;for(var touchIndex=0;touchIndex<touchList.length;touchIndex++){if(touchList[touchIndex].target===drag.target){touch=touchList[touchIndex];break}}return touch!==null?touch.clientX:undefined;default:return event.clientX}}});Rickshaw.namespace("Rickshaw.Graph.Renderer");Rickshaw.Graph.Renderer=Rickshaw.Class.create({initialize:function(args){this.graph=args.graph;this.tension=args.tension||this.tension;this.configure(args)},seriesPathFactory:function(){},seriesStrokeFactory:function(){},defaults:function(){return{tension:.8,strokeWidth:2,unstack:true,padding:{top:.01,right:0,bottom:.01,left:0},stroke:false,fill:false}},domain:function(data){var stackedData=data||this.graph.stackedData||this.graph.stackData();var xMin=+Infinity;var xMax=-Infinity;var yMin=+Infinity;var yMax=-Infinity;stackedData.forEach(function(series){series.forEach(function(d){if(d.y==null)return;var y=d.y+d.y0;if(y<yMin)yMin=y;if(y>yMax)yMax=y});if(!series.length)return;if(series[0].x<xMin)xMin=series[0].x;if(series[series.length-1].x>xMax)xMax=series[series.length-1].x});xMin-=(xMax-xMin)*this.padding.left;xMax+=(xMax-xMin)*this.padding.right;yMin=this.graph.min==="auto"?yMin:this.graph.min||0;yMax=this.graph.max===undefined?yMax:this.graph.max;if(this.graph.min==="auto"||yMin<0){yMin-=(yMax-yMin)*this.padding.bottom}if(this.graph.max===undefined){yMax+=(yMax-yMin)*this.padding.top}return{x:[xMin,xMax],y:[yMin,yMax]}},render:function(args){args=args||{};var graph=this.graph;var series=args.series||graph.series;var vis=args.vis||graph.vis;vis.selectAll("*").remove();var data=series.filter(function(s){return!s.disabled}).map(function(s){return s.stack});var pathNodes=vis.selectAll("path.path").data(data).enter().append("svg:path").classed("path",true).attr("d",this.seriesPathFactory());if(this.stroke){var strokeNodes=vis.selectAll("path.stroke").data(data).enter().append("svg:path").classed("stroke",true).attr("d",this.seriesStrokeFactory())}var i=0;series.forEach(function(series){if(series.disabled)return;series.path=pathNodes[0][i];if(this.stroke)series.stroke=strokeNodes[0][i];this._styleSeries(series);i++},this)},_styleSeries:function(series){var fill=this.fill?series.color:"none";var stroke=this.stroke?series.color:"none";series.path.setAttribute("fill",fill);series.path.setAttribute("stroke",stroke);series.path.setAttribute("stroke-width",this.strokeWidth);if(series.className){d3.select(series.path).classed(series.className,true)}if(series.className&&this.stroke){d3.select(series.stroke).classed(series.className,true)}},configure:function(args){args=args||{};Rickshaw.keys(this.defaults()).forEach(function(key){if(!args.hasOwnProperty(key)){this[key]=this[key]||this.graph[key]||this.defaults()[key];return}if(typeof this.defaults()[key]=="object"){Rickshaw.keys(this.defaults()[key]).forEach(function(k){this[key][k]=args[key][k]!==undefined?args[key][k]:this[key][k]!==undefined?this[key][k]:this.defaults()[key][k]},this)}else{this[key]=args[key]!==undefined?args[key]:this[key]!==undefined?this[key]:this.graph[key]!==undefined?this.graph[key]:this.defaults()[key]}},this)},setStrokeWidth:function(strokeWidth){if(strokeWidth!==undefined){this.strokeWidth=strokeWidth}},setTension:function(tension){if(tension!==undefined){this.tension=tension}}});Rickshaw.namespace("Rickshaw.Graph.Renderer.Line");Rickshaw.Graph.Renderer.Line=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"line",defaults:function($super){return Rickshaw.extend($super(),{unstack:true,fill:false,stroke:true})},seriesPathFactory:function(){var graph=this.graph;var factory=d3.svg.line().x(function(d){return graph.x(d.x)}).y(function(d){return graph.y(d.y)}).interpolate(this.graph.interpolation).tension(this.tension);factory.defined&&factory.defined(function(d){return d.y!==null});return factory}});Rickshaw.namespace("Rickshaw.Graph.Renderer.Stack");Rickshaw.Graph.Renderer.Stack=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"stack",defaults:function($super){return Rickshaw.extend($super(),{fill:true,stroke:false,unstack:false})},seriesPathFactory:function(){var graph=this.graph;var factory=d3.svg.area().x(function(d){return graph.x(d.x)}).y0(function(d){return graph.y(d.y0)}).y1(function(d){return graph.y(d.y+d.y0)}).interpolate(this.graph.interpolation).tension(this.tension);factory.defined&&factory.defined(function(d){return d.y!==null});return factory}});Rickshaw.namespace("Rickshaw.Graph.Renderer.Bar");Rickshaw.Graph.Renderer.Bar=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"bar",defaults:function($super){var defaults=Rickshaw.extend($super(),{gapSize:.05,unstack:false});delete defaults.tension;return defaults},initialize:function($super,args){args=args||{};this.gapSize=args.gapSize||this.gapSize;$super(args)},domain:function($super){var domain=$super();var frequentInterval=this._frequentInterval(this.graph.stackedData.slice(-1).shift());domain.x[1]+=Number(frequentInterval.magnitude);return domain},barWidth:function(series){var frequentInterval=this._frequentInterval(series.stack);var barWidth=this.graph.x.magnitude(frequentInterval.magnitude)*(1-this.gapSize);return barWidth},render:function(args){args=args||{};var graph=this.graph;var series=args.series||graph.series;var vis=args.vis||graph.vis;vis.selectAll("*").remove();var barWidth=this.barWidth(series.active()[0]);var barXOffset=0;var activeSeriesCount=series.filter(function(s){return!s.disabled}).length;var seriesBarWidth=this.unstack?barWidth/activeSeriesCount:barWidth;var transform=function(d){var matrix=[1,0,0,d.y<0?-1:1,0,d.y<0?graph.y.magnitude(Math.abs(d.y))*2:0];return"matrix("+matrix.join(",")+")"};series.forEach(function(series){if(series.disabled)return;var barWidth=this.barWidth(series);var nodes=vis.selectAll("path").data(series.stack.filter(function(d){return d.y!==null})).enter().append("svg:rect").attr("x",function(d){return graph.x(d.x)+barXOffset}).attr("y",function(d){return graph.y(d.y0+Math.abs(d.y))*(d.y<0?-1:1)}).attr("width",seriesBarWidth).attr("height",function(d){return graph.y.magnitude(Math.abs(d.y))}).attr("transform",transform);Array.prototype.forEach.call(nodes[0],function(n){n.setAttribute("fill",series.color)});if(this.unstack)barXOffset+=seriesBarWidth},this)},_frequentInterval:function(data){var intervalCounts={};for(var i=0;i<data.length-1;i++){var interval=data[i+1].x-data[i].x;intervalCounts[interval]=intervalCounts[interval]||0;intervalCounts[interval]++}var frequentInterval={count:0,magnitude:1};Rickshaw.keys(intervalCounts).forEach(function(i){if(frequentInterval.count<intervalCounts[i]){frequentInterval={count:intervalCounts[i],magnitude:i}}});return frequentInterval}});Rickshaw.namespace("Rickshaw.Graph.Renderer.Area");Rickshaw.Graph.Renderer.Area=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"area",defaults:function($super){return Rickshaw.extend($super(),{unstack:false,fill:false,stroke:false})},seriesPathFactory:function(){var graph=this.graph;var factory=d3.svg.area().x(function(d){return graph.x(d.x)}).y0(function(d){return graph.y(d.y0)}).y1(function(d){return graph.y(d.y+d.y0)}).interpolate(graph.interpolation).tension(this.tension);
+factory.defined&&factory.defined(function(d){return d.y!==null});return factory},seriesStrokeFactory:function(){var graph=this.graph;var factory=d3.svg.line().x(function(d){return graph.x(d.x)}).y(function(d){return graph.y(d.y+d.y0)}).interpolate(graph.interpolation).tension(this.tension);factory.defined&&factory.defined(function(d){return d.y!==null});return factory},render:function(args){args=args||{};var graph=this.graph;var series=args.series||graph.series;var vis=args.vis||graph.vis;vis.selectAll("*").remove();var method=this.unstack?"append":"insert";var data=series.filter(function(s){return!s.disabled}).map(function(s){return s.stack});var nodes=vis.selectAll("path").data(data).enter()[method]("svg:g","g");nodes.append("svg:path").attr("d",this.seriesPathFactory()).attr("class","area");if(this.stroke){nodes.append("svg:path").attr("d",this.seriesStrokeFactory()).attr("class","line")}var i=0;series.forEach(function(series){if(series.disabled)return;series.path=nodes[0][i++];this._styleSeries(series)},this)},_styleSeries:function(series){if(!series.path)return;d3.select(series.path).select(".area").attr("fill",series.color);if(this.stroke){d3.select(series.path).select(".line").attr("fill","none").attr("stroke",series.stroke||d3.interpolateRgb(series.color,"black")(.125)).attr("stroke-width",this.strokeWidth)}if(series.className){series.path.setAttribute("class",series.className)}}});Rickshaw.namespace("Rickshaw.Graph.Renderer.ScatterPlot");Rickshaw.Graph.Renderer.ScatterPlot=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"scatterplot",defaults:function($super){return Rickshaw.extend($super(),{unstack:true,fill:true,stroke:false,padding:{top:.01,right:.01,bottom:.01,left:.01},dotSize:4})},initialize:function($super,args){$super(args)},render:function(args){args=args||{};var graph=this.graph;var series=args.series||graph.series;var vis=args.vis||graph.vis;var dotSize=this.dotSize;vis.selectAll("*").remove();series.forEach(function(series){if(series.disabled)return;var nodes=vis.selectAll("path").data(series.stack.filter(function(d){return d.y!==null})).enter().append("svg:circle").attr("cx",function(d){return graph.x(d.x)}).attr("cy",function(d){return graph.y(d.y)}).attr("r",function(d){return"r"in d?d.r:dotSize});if(series.className){nodes.classed(series.className,true)}Array.prototype.forEach.call(nodes[0],function(n){n.setAttribute("fill",series.color)})},this)}});Rickshaw.namespace("Rickshaw.Graph.Renderer.Multi");Rickshaw.Graph.Renderer.Multi=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"multi",initialize:function($super,args){$super(args)},defaults:function($super){return Rickshaw.extend($super(),{unstack:true,fill:false,stroke:true})},configure:function($super,args){args=args||{};this.config=args;$super(args)},domain:function($super){this.graph.stackData();var domains=[];var groups=this._groups();this._stack(groups);groups.forEach(function(group){var data=group.series.filter(function(s){return!s.disabled}).map(function(s){return s.stack});if(!data.length)return;var domain=null;if(group.renderer&&group.renderer.domain){domain=group.renderer.domain(data)}else{domain=$super(data)}domains.push(domain)});var xMin=d3.min(domains.map(function(d){return d.x[0]}));var xMax=d3.max(domains.map(function(d){return d.x[1]}));var yMin=d3.min(domains.map(function(d){return d.y[0]}));var yMax=d3.max(domains.map(function(d){return d.y[1]}));return{x:[xMin,xMax],y:[yMin,yMax]}},_groups:function(){var graph=this.graph;var renderGroups={};graph.series.forEach(function(series){if(series.disabled)return;if(!renderGroups[series.renderer]){var ns="http://www.w3.org/2000/svg";var vis=document.createElementNS(ns,"g");graph.vis[0][0].appendChild(vis);var renderer=graph._renderers[series.renderer];var config={};var defaults=[this.defaults(),renderer.defaults(),this.config,this.graph];defaults.forEach(function(d){Rickshaw.extend(config,d)});renderer.configure(config);renderGroups[series.renderer]={renderer:renderer,series:[],vis:d3.select(vis)}}renderGroups[series.renderer].series.push(series)},this);var groups=[];Object.keys(renderGroups).forEach(function(key){var group=renderGroups[key];groups.push(group)});return groups},_stack:function(groups){groups.forEach(function(group){var series=group.series.filter(function(series){return!series.disabled});var data=series.map(function(series){return series.stack});if(!group.renderer.unstack){var layout=d3.layout.stack();var stackedData=Rickshaw.clone(layout(data));series.forEach(function(series,index){series._stack=Rickshaw.clone(stackedData[index])})}},this);return groups},render:function(){this.graph.series.forEach(function(series){if(!series.renderer){throw new Error("Each series needs a renderer for graph 'multi' renderer")}});this.graph.vis.selectAll("*").remove();var groups=this._groups();groups=this._stack(groups);groups.forEach(function(group){var series=group.series.filter(function(series){return!series.disabled});series.active=function(){return series};group.renderer.render({series:series,vis:group.vis});series.forEach(function(s){s.stack=s._stack||s.stack||s.data})})}});Rickshaw.namespace("Rickshaw.Graph.Renderer.LinePlot");Rickshaw.Graph.Renderer.LinePlot=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"lineplot",defaults:function($super){return Rickshaw.extend($super(),{unstack:true,fill:false,stroke:true,padding:{top:.01,right:.01,bottom:.01,left:.01},dotSize:3,strokeWidth:2})},seriesPathFactory:function(){var graph=this.graph;var factory=d3.svg.line().x(function(d){return graph.x(d.x)}).y(function(d){return graph.y(d.y)}).interpolate(this.graph.interpolation).tension(this.tension);factory.defined&&factory.defined(function(d){return d.y!==null});return factory},render:function(args){args=args||{};var graph=this.graph;var series=args.series||graph.series;var vis=args.vis||graph.vis;var dotSize=this.dotSize;vis.selectAll("*").remove();var data=series.filter(function(s){return!s.disabled}).map(function(s){return s.stack});var nodes=vis.selectAll("path").data(data).enter().append("svg:path").attr("d",this.seriesPathFactory());var i=0;series.forEach(function(series){if(series.disabled)return;series.path=nodes[0][i++];this._styleSeries(series)},this);series.forEach(function(series){if(series.disabled)return;var nodes=vis.selectAll("x").data(series.stack.filter(function(d){return d.y!==null})).enter().append("svg:circle").attr("cx",function(d){return graph.x(d.x)}).attr("cy",function(d){return graph.y(d.y)}).attr("r",function(d){return"r"in d?d.r:dotSize});Array.prototype.forEach.call(nodes[0],function(n){if(!n)return;n.setAttribute("data-color",series.color);n.setAttribute("fill","white");n.setAttribute("stroke",series.color);n.setAttribute("stroke-width",this.strokeWidth)}.bind(this))},this)}});Rickshaw.namespace("Rickshaw.Graph.Smoother");Rickshaw.Graph.Smoother=Rickshaw.Class.create({initialize:function(args){this.graph=args.graph;this.element=args.element;this.aggregationScale=1;this.build();this.graph.stackData.hooks.data.push({name:"smoother",orderPosition:50,f:this.transformer.bind(this)})},build:function(){var self=this;var $=jQuery;if(this.element){$(function(){$(self.element).slider({min:1,max:100,slide:function(event,ui){self.setScale(ui.value)}})})}},setScale:function(scale){if(scale<1){throw"scale out of range: "+scale}this.aggregationScale=scale;this.graph.update()},transformer:function(data){if(this.aggregationScale==1)return data;var aggregatedData=[];data.forEach(function(seriesData){var aggregatedSeriesData=[];while(seriesData.length){var avgX=0,avgY=0;var slice=seriesData.splice(0,this.aggregationScale);slice.forEach(function(d){avgX+=d.x/slice.length;avgY+=d.y/slice.length});aggregatedSeriesData.push({x:avgX,y:avgY})}aggregatedData.push(aggregatedSeriesData)}.bind(this));return aggregatedData}});Rickshaw.namespace("Rickshaw.Graph.Socketio");Rickshaw.Graph.Socketio=Rickshaw.Class.create(Rickshaw.Graph.Ajax,{request:function(){var socket=io.connect(this.dataURL);var self=this;socket.on("rickshaw",function(data){self.success(data)})}});Rickshaw.namespace("Rickshaw.Series");Rickshaw.Series=Rickshaw.Class.create(Array,{initialize:function(data,palette,options){options=options||{};this.palette=new Rickshaw.Color.Palette(palette);this.timeBase=typeof options.timeBase==="undefined"?Math.floor((new Date).getTime()/1e3):options.timeBase;var timeInterval=typeof options.timeInterval=="undefined"?1e3:options.timeInterval;this.setTimeInterval(timeInterval);if(data&&typeof data=="object"&&Array.isArray(data)){data.forEach(function(item){this.addItem(item)},this)}},addItem:function(item){if(typeof item.name==="undefined"){throw"addItem() needs a name"}item.color=item.color||this.palette.color(item.name);item.data=item.data||[];if(item.data.length===0&&this.length&&this.getIndex()>0){this[0].data.forEach(function(plot){item.data.push({x:plot.x,y:0})})}else if(item.data.length===0){item.data.push({x:this.timeBase-(this.timeInterval||0),y:0})}this.push(item);if(this.legend){this.legend.addLine(this.itemByName(item.name))}},addData:function(data,x){var index=this.getIndex();Rickshaw.keys(data).forEach(function(name){if(!this.itemByName(name)){this.addItem({name:name})}},this);this.forEach(function(item){item.data.push({x:x||(index*this.timeInterval||1)+this.timeBase,y:data[item.name]||0})},this)},getIndex:function(){return this[0]&&this[0].data&&this[0].data.length?this[0].data.length:0},itemByName:function(name){for(var i=0;i<this.length;i++){if(this[i].name==name)return this[i]}},setTimeInterval:function(iv){this.timeInterval=iv/1e3},setTimeBase:function(t){this.timeBase=t},dump:function(){var data={timeBase:this.timeBase,timeInterval:this.timeInterval,items:[]};this.forEach(function(item){var newItem={color:item.color,name:item.name,data:[]};item.data.forEach(function(plot){newItem.data.push({x:plot.x,y:plot.y})});data.items.push(newItem)});return data},load:function(data){if(data.timeInterval){this.timeInterval=data.timeInterval}if(data.timeBase){this.timeBase=data.timeBase}if(data.items){data.items.forEach(function(item){this.push(item);if(this.legend){this.legend.addLine(this.itemByName(item.name))}},this)}}});Rickshaw.Series.zeroFill=function(series){Rickshaw.Series.fill(series,0)};Rickshaw.Series.fill=function(series,fill){var x;var i=0;var data=series.map(function(s){return s.data});while(i<Math.max.apply(null,data.map(function(d){return d.length}))){x=Math.min.apply(null,data.filter(function(d){return d[i]}).map(function(d){return d[i].x}));data.forEach(function(d){if(!d[i]||d[i].x!=x){d.splice(i,0,{x:x,y:fill})}});i++}};Rickshaw.namespace("Rickshaw.Series.FixedDuration");Rickshaw.Series.FixedDuration=Rickshaw.Class.create(Rickshaw.Series,{initialize:function(data,palette,options){options=options||{};if(typeof options.timeInterval==="undefined"){throw new Error("FixedDuration series requires timeInterval")}if(typeof options.maxDataPoints==="undefined"){throw new Error("FixedDuration series requires maxDataPoints")}this.palette=new Rickshaw.Color.Palette(palette);this.timeBase=typeof options.timeBase==="undefined"?Math.floor((new Date).getTime()/1e3):options.timeBase;this.setTimeInterval(options.timeInterval);if(this[0]&&this[0].data&&this[0].data.length){this.currentSize=this[0].data.length;this.currentIndex=this[0].data.length}else{this.currentSize=0;this.currentIndex=0}this.maxDataPoints=options.maxDataPoints;if(data&&typeof data=="object"&&Array.isArray(data)){data.forEach(function(item){this.addItem(item)},this);this.currentSize+=1;this.currentIndex+=1}this.timeBase-=(this.maxDataPoints-this.currentSize)*this.timeInterval;if(typeof this.maxDataPoints!=="undefined"&&this.currentSize<this.maxDataPoints){for(var i=this.maxDataPoints-this.currentSize-1;i>1;i--){this.currentSize+=1;this.currentIndex+=1;this.forEach(function(item){item.data.unshift({x:((i-1)*this.timeInterval||1)+this.timeBase,y:0,i:i})},this)}}},addData:function($super,data,x){$super(data,x);this.currentSize+=1;this.currentIndex+=1;if(this.maxDataPoints!==undefined){while(this.currentSize>this.maxDataPoints){this.dropData()}}},dropData:function(){this.forEach(function(item){item.data.splice(0,1)});this.currentSize-=1},getIndex:function(){return this.currentIndex}});return Rickshaw});
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/rickshaw-graph.hbs b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/rickshaw-graph.hbs
new file mode 100755
index 0000000000..cc23912e50
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/rickshaw-graph.hbs
@@ -0,0 +1,8 @@
+{{#zone "topCss"}}
+    {{css "css/graph.css"}}
+{{/zone}}
+
+{{#zone "bottomJs"}}
+    {{js "js/d3.min.js"}}
+    {{js "js/rickshaw.min.js"}}
+{{/zone}}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/rickshaw-graph.json b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/rickshaw-graph.json
new file mode 100755
index 0000000000..36e949136e
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.lib.rickshaw-graph/rickshaw-graph.json
@@ -0,0 +1,4 @@
+{
+  "version": "1.0.0",
+  "index": 30
+}
\ No newline at end of file
diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql
index deabce4756..34b385655c 100644
--- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql
+++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql
@@ -502,14 +502,16 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_GROUP_POLICY (
     ON DELETE NO ACTION
     ON UPDATE NO ACTION,
   CONSTRAINT FK_DM_DEVICE_GROUP_DM_POLICY
-    FOREIGN KEY (POLICY_ID , DEVICE_GROUP_ID)
-    REFERENCES DM_POLICY (ID , ID)
+    FOREIGN KEY (POLICY_ID)
+    REFERENCES DM_POLICY (ID)
     ON DELETE NO ACTION
     ON UPDATE NO ACTION
 );
 
 -- END OF POLICY AND DEVICE GROUP MAPPING --
 
+-- DASHBOARD RELATED VIEWS --
+
 CREATE VIEW DEVICES_VIEW_1 AS
 SELECT
 DEVICE_INFO.DEVICE_ID,
@@ -560,3 +562,5 @@ DM_POLICY_COMPLIANCE_STATUS.DEVICE_ID = DM_DEVICE.ID AND
 DM_DEVICE.DEVICE_TYPE_ID = DM_DEVICE_TYPE.ID AND
 DM_DEVICE.ID = DM_DEVICE_DETAIL.DEVICE_ID
 ORDER BY TENANT_ID, DEVICE_ID;
+
+-- END OF DASHBOARD RELATED VIEWS --
diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mssql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mssql.sql
index 6101c9e5a8..c4bd77fa10 100644
--- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mssql.sql
+++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mssql.sql
@@ -2,10 +2,21 @@ CREATE TABLE DM_DEVICE_TYPE (
     ID INTEGER IDENTITY NOT NULL,
     NAME VARCHAR(300) DEFAULT NULL,
     PROVIDER_TENANT_ID INTEGER DEFAULT 0,
-    SHARED_WITH_ALL_TENANTS BOOLEAN NOT NULL DEFAULT FALSE,
+    SHARED_WITH_ALL_TENANTS INTEGER NOT NULL DEFAULT 0,
     PRIMARY KEY (ID)
 );
 
+CREATE TABLE DM_GROUP (
+  ID                  INTEGER IDENTITY NOT NULL,
+  GROUP_NAME          VARCHAR(100) DEFAULT NULL,
+  DESCRIPTION         VARCHAR(max)         DEFAULT NULL,
+  DATE_OF_CREATE      BIGINT       DEFAULT NULL,
+  DATE_OF_LAST_UPDATE BIGINT       DEFAULT NULL,
+  OWNER               VARCHAR(45)  DEFAULT NULL,
+  TENANT_ID           INTEGER      DEFAULT 0,
+  PRIMARY KEY (ID)
+);
+
 CREATE TABLE DM_DEVICE_CERTIFICATE (
       ID                    INTEGER IDENTITY NOT NULL,
       SERIAL_NUMBER            VARCHAR(500) DEFAULT NULL,
@@ -18,7 +29,7 @@ CREATE TABLE DM_DEVICE (
      ID                    INTEGER identity NOT NULL,
      DESCRIPTION           VARCHAR(max) DEFAULT NULL,
      NAME                  VARCHAR(100) DEFAULT NULL,
-     DEVICE_TYPE_ID        INT DEFAULT NULL,
+     DEVICE_TYPE_ID        INTEGER DEFAULT NULL,
      DEVICE_IDENTIFICATION VARCHAR(300) DEFAULT NULL,
      TENANT_ID INTEGER DEFAULT 0,
      PRIMARY KEY (ID),
@@ -26,6 +37,18 @@ CREATE TABLE DM_DEVICE (
      REFERENCES DM_DEVICE_TYPE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION
 );
 
+CREATE TABLE DM_DEVICE_GROUP_MAP (
+  ID        INTEGER IDENTITY NOT NULL,
+  DEVICE_ID INTEGER DEFAULT NULL,
+  GROUP_ID  INTEGER DEFAULT NULL,
+  TENANT_ID INTEGER DEFAULT 0,
+  PRIMARY KEY (ID),
+  CONSTRAINT fk_DM_DEVICE_GROUP_MAP_DM_DEVICE2 FOREIGN KEY (DEVICE_ID)
+  REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION,
+  CONSTRAINT fk_DM_DEVICE_GROUP_MAP_DM_GROUP2 FOREIGN KEY (GROUP_ID)
+  REFERENCES DM_GROUP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION
+);
+
 CREATE TABLE DM_OPERATION (
     ID INTEGER IDENTITY NOT NULL,
     TYPE VARCHAR(50) NOT NULL,
@@ -77,7 +100,7 @@ CREATE TABLE DM_ENROLMENT (
     STATUS VARCHAR(50) NULL,
     DATE_OF_ENROLMENT DATETIME2(0) DEFAULT NULL,
     DATE_OF_LAST_UPDATE DATETIME2(0) DEFAULT NULL,
-    TENANT_ID INT NOT NULL,
+    TENANT_ID INTEGER NOT NULL,
     PRIMARY KEY (ID),
     CONSTRAINT fk_dm_device_enrolment FOREIGN KEY (DEVICE_ID) REFERENCES
     DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION
@@ -113,10 +136,10 @@ CREATE TABLE DM_DEVICE_OPERATION_RESPONSE (
 
 
 CREATE  TABLE DM_PROFILE (
-  ID INT NOT NULL IDENTITY ,
+  ID INTEGER NOT NULL IDENTITY ,
   PROFILE_NAME VARCHAR(45) NOT NULL ,
-  TENANT_ID INT NOT NULL ,
-  DEVICE_TYPE_ID INT NOT NULL ,
+  TENANT_ID INTEGER NOT NULL ,
+  DEVICE_TYPE_ID INTEGER NOT NULL ,
   CREATED_TIME DATETIME2(0) NOT NULL ,
   UPDATED_TIME DATETIME2(0) NOT NULL ,
   PRIMARY KEY (ID) ,
@@ -132,16 +155,16 @@ CREATE  TABLE DM_PROFILE (
 
 
 CREATE  TABLE DM_POLICY (
-  ID INT NOT NULL IDENTITY ,
+  ID INTEGER NOT NULL IDENTITY ,
   NAME VARCHAR(45) DEFAULT NULL ,
   DESCRIPTION VARCHAR(1000) NULL,
-  TENANT_ID INT NOT NULL ,
-  PROFILE_ID INT NOT NULL ,
+  TENANT_ID INTEGER NOT NULL ,
+  PROFILE_ID INTEGER NOT NULL ,
   OWNERSHIP_TYPE VARCHAR(45) NULL,
   COMPLIANCE VARCHAR(100) NULL,
-  PRIORITY INT NOT NULL,
-  ACTIVE INT NOT NULL,
-  UPDATED INT NULL,
+  PRIORITY INTEGER NOT NULL,
+  ACTIVE INTEGER NOT NULL,
+  UPDATED INTEGER NULL,
   PRIMARY KEY (ID) ,
   CONSTRAINT FK_DM_PROFILE_DM_POLICY
     FOREIGN KEY (PROFILE_ID )
@@ -154,11 +177,11 @@ CREATE  TABLE DM_POLICY (
 
 
 CREATE  TABLE DM_DEVICE_POLICY (
-  ID INT NOT NULL IDENTITY ,
-  DEVICE_ID INT NOT NULL ,
-  ENROLMENT_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY ,
+  DEVICE_ID INTEGER NOT NULL ,
+  ENROLMENT_ID INTEGER NOT NULL,
   DEVICE VARBINARY(max) NOT NULL,
-  POLICY_ID INT NOT NULL ,
+  POLICY_ID INTEGER NOT NULL ,
   PRIMARY KEY (ID) ,
   CONSTRAINT FK_POLICY_DEVICE_POLICY
     FOREIGN KEY (POLICY_ID )
@@ -176,9 +199,9 @@ CREATE  TABLE DM_DEVICE_POLICY (
 
 
 CREATE  TABLE DM_DEVICE_TYPE_POLICY (
-  ID INT NOT NULL ,
-  DEVICE_TYPE_ID INT NOT NULL ,
-  POLICY_ID INT NOT NULL ,
+  ID INTEGER NOT NULL ,
+  DEVICE_TYPE_ID INTEGER NOT NULL ,
+  POLICY_ID INTEGER NOT NULL ,
   PRIMARY KEY (ID) ,
   CONSTRAINT FK_DEVICE_TYPE_POLICY
     FOREIGN KEY (POLICY_ID )
@@ -197,11 +220,11 @@ CREATE  TABLE DM_DEVICE_TYPE_POLICY (
 
 
 CREATE  TABLE DM_PROFILE_FEATURES (
-  ID INT NOT NULL IDENTITY,
-  PROFILE_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  PROFILE_ID INTEGER NOT NULL,
   FEATURE_CODE VARCHAR(100) NOT NULL,
-  DEVICE_TYPE_ID INT NOT NULL,
-  TENANT_ID INT NOT NULL ,
+  DEVICE_TYPE_ID INTEGER NOT NULL,
+  TENANT_ID INTEGER NOT NULL ,
   CONTENT VARBINARY(max) NULL DEFAULT NULL,
   PRIMARY KEY (ID),
   CONSTRAINT FK_DM_PROFILE_DM_POLICY_FEATURES
@@ -215,9 +238,9 @@ CREATE  TABLE DM_PROFILE_FEATURES (
 
 
 CREATE  TABLE DM_ROLE_POLICY (
-  ID INT NOT NULL IDENTITY ,
+  ID INTEGER NOT NULL IDENTITY ,
   ROLE_NAME VARCHAR(45) NOT NULL ,
-  POLICY_ID INT NOT NULL ,
+  POLICY_ID INTEGER NOT NULL ,
   PRIMARY KEY (ID) ,
   CONSTRAINT FK_ROLE_POLICY_POLICY
     FOREIGN KEY (POLICY_ID )
@@ -230,8 +253,8 @@ CREATE  TABLE DM_ROLE_POLICY (
 
 
 CREATE  TABLE DM_USER_POLICY (
-  ID INT NOT NULL IDENTITY ,
-  POLICY_ID INT NOT NULL ,
+  ID INTEGER NOT NULL IDENTITY ,
+  POLICY_ID INTEGER NOT NULL ,
   USERNAME VARCHAR(45) NOT NULL ,
   PRIMARY KEY (ID) ,
   CONSTRAINT DM_POLICY_USER_POLICY
@@ -243,12 +266,12 @@ CREATE  TABLE DM_USER_POLICY (
 
 
  CREATE  TABLE DM_DEVICE_POLICY_APPLIED (
-  ID INT NOT NULL IDENTITY ,
-  DEVICE_ID INT NOT NULL ,
-  ENROLMENT_ID INT NOT NULL,
-  POLICY_ID INT NOT NULL ,
+  ID INTEGER NOT NULL IDENTITY ,
+  DEVICE_ID INTEGER NOT NULL ,
+  ENROLMENT_ID INTEGER NOT NULL,
+  POLICY_ID INTEGER NOT NULL ,
   POLICY_CONTENT VARBINARY(max) NULL ,
-  TENANT_ID INT NOT NULL,
+  TENANT_ID INTEGER NOT NULL,
   APPLIED SMALLINT NULL ,
   CREATED_TIME DATETIME2(0) NULL ,
   UPDATED_TIME DATETIME2(0) NULL ,
@@ -264,8 +287,8 @@ CREATE  TABLE DM_USER_POLICY (
 
 
 CREATE TABLE DM_CRITERIA (
-  ID INT NOT NULL IDENTITY,
-  TENANT_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  TENANT_ID INTEGER NOT NULL,
   NAME VARCHAR(50) NULL,
   PRIMARY KEY (ID)
 );
@@ -273,9 +296,9 @@ CREATE TABLE DM_CRITERIA (
 
 
 CREATE TABLE DM_POLICY_CRITERIA (
-  ID INT NOT NULL IDENTITY,
-  CRITERIA_ID INT NOT NULL,
-  POLICY_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  CRITERIA_ID INTEGER NOT NULL,
+  POLICY_ID INTEGER NOT NULL,
   PRIMARY KEY (ID),
   CONSTRAINT FK_CRITERIA_POLICY_CRITERIA
     FOREIGN KEY (CRITERIA_ID)
@@ -290,8 +313,8 @@ CREATE TABLE DM_POLICY_CRITERIA (
 );
 
 CREATE TABLE DM_POLICY_CRITERIA_PROPERTIES (
-  ID INT NOT NULL IDENTITY,
-  POLICY_CRITERION_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  POLICY_CRITERION_ID INTEGER NOT NULL,
   PROP_KEY VARCHAR(45) NULL,
   PROP_VALUE VARCHAR(100) NULL,
   CONTENT VARBINARY(max) NULL ,
@@ -304,35 +327,35 @@ CREATE TABLE DM_POLICY_CRITERIA_PROPERTIES (
 );
 
 CREATE TABLE DM_POLICY_COMPLIANCE_STATUS (
-  ID INT NOT NULL IDENTITY,
-  DEVICE_ID INT NOT NULL,
-  ENROLMENT_ID INT NOT NULL,
-  POLICY_ID INT NOT NULL,
-  TENANT_ID INT NOT NULL,
-  STATUS INT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  DEVICE_ID INTEGER NOT NULL,
+  ENROLMENT_ID INTEGER NOT NULL,
+  POLICY_ID INTEGER NOT NULL,
+  TENANT_ID INTEGER NOT NULL,
+  STATUS INTEGER NULL,
   LAST_SUCCESS_TIME DATETIME2(0) NULL,
   LAST_REQUESTED_TIME DATETIME2(0) NULL,
   LAST_FAILED_TIME DATETIME2(0) NULL,
-  ATTEMPTS INT NULL,
+  ATTEMPTS INTEGER NULL,
   PRIMARY KEY (ID)
 );
 
 
 CREATE TABLE DM_POLICY_CHANGE_MGT (
-  ID INT NOT NULL IDENTITY,
-  POLICY_ID INT NOT NULL,
-  DEVICE_TYPE_ID INT NOT NULL,
-  TENANT_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  POLICY_ID INTEGER NOT NULL,
+  DEVICE_TYPE_ID INTEGER NOT NULL,
+  TENANT_ID INTEGER NOT NULL,
   PRIMARY KEY (ID)
 );
 
 
 CREATE TABLE DM_POLICY_COMPLIANCE_FEATURES (
-  ID INT NOT NULL IDENTITY,
-  COMPLIANCE_STATUS_ID INT NOT NULL,
-  TENANT_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  COMPLIANCE_STATUS_ID INTEGER NOT NULL,
+  TENANT_ID INTEGER NOT NULL,
   FEATURE_CODE VARCHAR(100) NOT NULL,
-  STATUS INT NULL,
+  STATUS INTEGER NULL,
   PRIMARY KEY (ID),
   CONSTRAINT FK_COMPLIANCE_FEATURES_STATUS
     FOREIGN KEY (COMPLIANCE_STATUS_ID)
@@ -342,10 +365,10 @@ CREATE TABLE DM_POLICY_COMPLIANCE_FEATURES (
 );
 
 CREATE TABLE DM_DEVICE_GROUP_POLICY (
-  ID INT NOT NULL IDENTITY,
-  DEVICE_GROUP_ID INT NOT NULL,
-  POLICY_ID INT NOT NULL,
-  TENANT_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  DEVICE_GROUP_ID INTEGER NOT NULL,
+  POLICY_ID INTEGER NOT NULL,
+  TENANT_ID INTEGER NOT NULL,
   PRIMARY KEY (ID),
   CONSTRAINT FK_DM_DEVICE_GROUP_POLICY
     FOREIGN KEY (DEVICE_GROUP_ID)
@@ -353,8 +376,8 @@ CREATE TABLE DM_DEVICE_GROUP_POLICY (
     ON DELETE NO ACTION
     ON UPDATE NO ACTION,
   CONSTRAINT FK_DM_DEVICE_GROUP_DM_POLICY
-    FOREIGN KEY (POLICY_ID , DEVICE_GROUP_ID)
-    REFERENCES DM_POLICY (ID , ID)
+    FOREIGN KEY (POLICY_ID)
+    REFERENCES DM_POLICY (ID)
     ON DELETE NO ACTION
     ON UPDATE NO ACTION
 );
@@ -370,7 +393,7 @@ CREATE TABLE DM_APPLICATION (
     LOCATION_URL VARCHAR(100) DEFAULT NULL,
     IMAGE_URL VARCHAR(100) DEFAULT NULL,
     APP_PROPERTIES VARBINARY(max) NULL,
-    MEMORY_USAGE INTEGER(10) NULL,
+    MEMORY_USAGE INTEGER NULL,
     TENANT_ID INTEGER NOT NULL,
     PRIMARY KEY (ID)
 );
@@ -405,12 +428,12 @@ CREATE TABLE DM_NOTIFICATION (
 );
 -- NOTIFICATION TABLE END --
 
-
-DROP TABLE IF EXISTS DM_DEVICE_INFO;
+IF EXISTS (SELECT * FROM sys.objects WHERE name = 'DM_DEVICE_INFO' and type = 'u')
+    DROP TABLE DM_DEVICE_INFO;
 
 CREATE TABLE DM_DEVICE_INFO (
   ID INTEGER IDENTITY NOT NULL,
-  DEVICE_ID INT NULL,
+  DEVICE_ID INTEGER NULL,
   KEY_FIELD VARCHAR(45) NULL,
   VALUE_FIELD VARCHAR(100) NULL,
   PRIMARY KEY (ID),
@@ -423,11 +446,12 @@ CREATE TABLE DM_DEVICE_INFO (
 
 CREATE INDEX DM_DEVICE_INFO_DEVICE_idx ON DM_DEVICE_INFO (DEVICE_ID ASC);
 
-DROP TABLE IF EXISTS DM_DEVICE_LOCATION;
+IF EXISTS (SELECT * FROM sys.objects WHERE name = 'DM_DEVICE_LOCATION' and type = 'u')
+  DROP TABLE DM_DEVICE_LOCATION;
 
 CREATE TABLE DM_DEVICE_LOCATION (
   ID INTEGER IDENTITY NOT NULL,
-  DEVICE_ID INT NULL,
+  DEVICE_ID INTEGER NULL,
   LATITUDE FLOAT NULL,
   LONGITUDE FLOAT NULL,
   STREET1 VARCHAR(45) NULL,
@@ -447,11 +471,12 @@ CREATE TABLE DM_DEVICE_LOCATION (
 
 CREATE INDEX DM_DEVICE_LOCATION_DEVICE_idx ON DM_DEVICE_LOCATION (DEVICE_ID ASC);
 
-DROP TABLE IF EXISTS DM_DEVICE_DETAIL;
+IF EXISTS (SELECT * FROM sys.objects WHERE name = 'DM_DEVICE_DETAIL' and type = 'u')
+  DROP TABLE DM_DEVICE_DETAIL;
 
 CREATE TABLE DM_DEVICE_DETAIL (
-  ID INT NOT NULL IDENTITY,
-  DEVICE_ID INT NOT NULL,
+  ID INTEGER NOT NULL IDENTITY,
+  DEVICE_ID INTEGER NOT NULL,
   DEVICE_MODEL VARCHAR(45) NULL,
   VENDOR VARCHAR(45) NULL,
   OS_VERSION VARCHAR(45) NULL,
@@ -465,7 +490,7 @@ CREATE TABLE DM_DEVICE_DETAIL (
   CPU_USAGE DECIMAL(5) NULL,
   TOTAL_RAM_MEMORY DECIMAL(30,3) NULL,
   AVAILABLE_RAM_MEMORY DECIMAL(30,3) NULL,
-  PLUGGED_IN INT NULL,
+  PLUGGED_IN INTEGER NULL,
   UPDATE_TIMESTAMP BIGINT NOT NULL,
   PRIMARY KEY (ID),
   CONSTRAINT FK_DM_DEVICE_DETAILS_DEVICE
diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mysql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mysql.sql
index 7d3eada22e..e031ea2fe2 100644
--- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mysql.sql
+++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mysql.sql
@@ -423,8 +423,8 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_GROUP_POLICY (
     ON DELETE NO ACTION
     ON UPDATE NO ACTION,
   CONSTRAINT FK_DM_DEVICE_GROUP_DM_POLICY
-    FOREIGN KEY (POLICY_ID , DEVICE_GROUP_ID)
-    REFERENCES DM_POLICY (ID , ID)
+    FOREIGN KEY (POLICY_ID)
+    REFERENCES DM_POLICY (ID)
     ON DELETE NO ACTION
     ON UPDATE NO ACTION
 )ENGINE = InnoDB;
@@ -493,8 +493,6 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
 )
 ENGINE = InnoDB;
 
-
-
 DROP TABLE IF EXISTS DM_DEVICE_DETAIL ;
 
 CREATE TABLE IF NOT EXISTS DM_DEVICE_DETAIL (
@@ -524,3 +522,64 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_DETAIL (
     ON UPDATE NO ACTION)
 ENGINE = InnoDB;
 
+-- DASHBOARD RELATED VIEWS --
+
+CREATE VIEW DEVICE_INFO_VIEW AS
+SELECT
+DM_DEVICE.ID AS DEVICE_ID,
+DM_DEVICE_TYPE.NAME AS PLATFORM,
+DM_ENROLMENT.OWNERSHIP,
+DM_ENROLMENT.STATUS AS CONNECTIVITY_STATUS,
+DM_DEVICE.TENANT_ID
+FROM DM_DEVICE, DM_DEVICE_TYPE, DM_ENROLMENT
+WHERE DM_DEVICE.DEVICE_TYPE_ID = DM_DEVICE_TYPE.ID AND DM_DEVICE.ID = DM_ENROLMENT.DEVICE_ID;
+
+CREATE VIEW DEVICE_WITH_POLICY_INFO_VIEW AS
+SELECT
+DEVICE_ID,
+POLICY_ID,
+STATUS AS IS_COMPLIANT
+FROM DM_POLICY_COMPLIANCE_STATUS;
+
+CREATE VIEW DEVICES_VIEW_1 AS
+SELECT
+DEVICE_INFO_VIEW.DEVICE_ID,
+DEVICE_INFO_VIEW.PLATFORM,
+DEVICE_INFO_VIEW.OWNERSHIP,
+DEVICE_INFO_VIEW.CONNECTIVITY_STATUS,
+IFNULL(DEVICE_WITH_POLICY_INFO_VIEW.POLICY_ID, -1) AS POLICY_ID,
+IFNULL(DEVICE_WITH_POLICY_INFO_VIEW.IS_COMPLIANT, -1) AS IS_COMPLIANT,
+DEVICE_INFO_VIEW.TENANT_ID
+FROM
+DEVICE_INFO_VIEW
+LEFT JOIN
+DEVICE_WITH_POLICY_INFO_VIEW
+ON DEVICE_INFO_VIEW.DEVICE_ID = DEVICE_WITH_POLICY_INFO_VIEW.DEVICE_ID
+ORDER BY DEVICE_INFO_VIEW.DEVICE_ID;
+
+CREATE VIEW DEVICES_VIEW_2 AS
+SELECT
+DM_DEVICE.ID AS DEVICE_ID,
+DM_DEVICE_DETAIL.DEVICE_MODEL,
+DM_DEVICE_DETAIL.VENDOR,
+DM_DEVICE_DETAIL.OS_VERSION,
+DM_ENROLMENT.OWNERSHIP,
+DM_ENROLMENT.OWNER,
+DM_ENROLMENT.STATUS AS CONNECTIVITY_STATUS,
+DM_POLICY_COMPLIANCE_STATUS.POLICY_ID,
+DM_DEVICE_TYPE.NAME AS PLATFORM,
+DM_POLICY_COMPLIANCE_FEATURES.FEATURE_CODE,
+DM_POLICY_COMPLIANCE_FEATURES.STATUS AS IS_COMPLAINT,
+DM_DEVICE.TENANT_ID
+FROM
+DM_POLICY_COMPLIANCE_FEATURES, DM_POLICY_COMPLIANCE_STATUS, DM_ENROLMENT, DM_DEVICE, DM_DEVICE_TYPE, DM_DEVICE_DETAIL
+WHERE
+DM_POLICY_COMPLIANCE_FEATURES.COMPLIANCE_STATUS_ID = DM_POLICY_COMPLIANCE_STATUS.ID AND
+DM_POLICY_COMPLIANCE_STATUS.ENROLMENT_ID = DM_ENROLMENT.ID AND
+DM_POLICY_COMPLIANCE_STATUS.DEVICE_ID = DM_DEVICE.ID AND
+DM_DEVICE.DEVICE_TYPE_ID = DM_DEVICE_TYPE.ID AND
+DM_DEVICE.ID = DM_DEVICE_DETAIL.DEVICE_ID
+ORDER BY TENANT_ID, DEVICE_ID;
+
+-- END OF DASHBOARD RELATED VIEWS --
+
diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/oracle.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/oracle.sql
index fa9ac2e783..c4a2fd0267 100644
--- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/oracle.sql
+++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/oracle.sql
@@ -2,7 +2,7 @@ CREATE TABLE DM_DEVICE_TYPE (
   ID NUMBER(10) NOT NULL,
   NAME VARCHAR2(300) DEFAULT NULL,
   PROVIDER_TENANT_ID INTEGER DEFAULT 0,
-  SHARED_WITH_ALL_TENANTS BOOLEAN NOT NULL DEFAULT FALSE,
+  SHARED_WITH_ALL_TENANTS NUMBER(1) DEFAULT 0 NOT NULL,
   CONSTRAINT PK_DM_DEVICE_TYPE PRIMARY KEY (ID)
 )
 /
@@ -20,6 +20,31 @@ WHEN (NEW.ID IS NULL)
   END;
 /
 
+CREATE TABLE DM_GROUP (
+  ID NUMBER(10) NOT NULL,
+  DESCRIPTION CLOB DEFAULT NULL,
+  GROUP_NAME VARCHAR2(100) DEFAULT NULL,
+  DATE_OF_ENROLLMENT TIMESTAMP(0) DEFAULT NULL,
+  DATE_OF_LAST_UPDATE TIMESTAMP(0) DEFAULT NULL,
+  OWNER VARCHAR2(45) DEFAULT NULL,
+  TENANT_ID NUMBER(10) DEFAULT 0,
+  CONSTRAINT PK_DM_GROUP PRIMARY KEY (ID)
+)
+/
+-- Generate ID using sequence and trigger
+CREATE SEQUENCE DM_GROUP_seq START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE OR REPLACE TRIGGER DM_GROUP_seq_tr
+BEFORE INSERT
+ON DM_GROUP
+REFERENCING NEW AS NEW
+FOR EACH ROW
+WHEN (NEW.ID IS NULL)
+  BEGIN
+    SELECT DM_GROUP_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
+  END;
+/
+
 CREATE TABLE DM_DEVICE_CERTIFICATE (
   ID                    NUMBER(10) NOT NULL,
   SERIAL_NUMBER         VARCHAR2(500) DEFAULT NULL,
@@ -65,6 +90,29 @@ WHEN (NEW.ID IS NULL)
   END;
 /
 
+CREATE TABLE DM_DEVICE_GROUP_MAP (
+  ID        NUMBER(10) NOT NULL,
+  DEVICE_ID NUMBER(10) DEFAULT NULL,
+  GROUP_ID  NUMBER(10) DEFAULT NULL,
+  TENANT_ID NUMBER(10) DEFAULT 0,
+  PRIMARY KEY (ID),
+  CONSTRAINT fk_DM_DEV_GROUP_MAP_DM_DEV2 FOREIGN KEY (DEVICE_ID)
+  REFERENCES DM_DEVICE (ID),
+  CONSTRAINT fk_DM_DEV_GROUP_MAP_DM_GROUP2 FOREIGN KEY (GROUP_ID)
+  REFERENCES DM_GROUP (ID)
+);
+
+-- Generate ID using sequence and trigger
+CREATE SEQUENCE DM_DEVICE_GROUP_MAP_seq START WITH 1 INCREMENT BY 1;
+
+CREATE OR REPLACE TRIGGER DM_DEVICE_GROUP_MAP_seq_tr
+BEFORE INSERT ON DM_DEVICE_GROUP_MAP FOR EACH ROW
+WHEN (NEW.ID IS NULL)
+  BEGIN
+    SELECT DM_DEVICE_GROUP_MAP_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
+  END;
+/
+
 CREATE TABLE DM_OPERATION (
   ID NUMBER(10) NOT NULL,
   TYPE VARCHAR2(50) NOT NULL,
@@ -576,23 +624,23 @@ CREATE TABLE DM_DEVICE_GROUP_POLICY (
   TENANT_ID NUMBER(10) NOT NULL,
   PRIMARY KEY (ID),
   CONSTRAINT FK_DM_DEVICE_GROUP_POLICY
-    FOREIGN KEY (DEVICE_GROUP_ID)
-    REFERENCES DM_GROUP (ID)
-   ,
+  FOREIGN KEY (DEVICE_GROUP_ID)
+  REFERENCES DM_GROUP (ID)
+  ,
   CONSTRAINT FK_DM_DEVICE_GROUP_DM_POLICY
-    FOREIGN KEY (POLICY_ID , DEVICE_GROUP_ID)
-    REFERENCES DM_POLICY (ID , ID)
+  FOREIGN KEY (POLICY_ID)
+  REFERENCES DM_POLICY (ID)
 ) ;
 
 -- Generate ID using sequence and trigger
 CREATE SEQUENCE DM_DEVICE_GROUP_POLICY_seq START WITH 1 INCREMENT BY 1;
 
 CREATE OR REPLACE TRIGGER DM_DEVICE_GROUP_POLICY_seq_tr
- BEFORE INSERT ON DM_DEVICE_GROUP_POLICY FOR EACH ROW
- WHEN (NEW.ID IS NULL)
-BEGIN
- SELECT DM_DEVICE_GROUP_POLICY_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
-END;
+BEFORE INSERT ON DM_DEVICE_GROUP_POLICY FOR EACH ROW
+WHEN (NEW.ID IS NULL)
+  BEGIN
+    SELECT DM_DEVICE_GROUP_POLICY_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
+  END;
 /
 
 
@@ -690,9 +738,9 @@ WHEN (NEW.NOTIFICATION_ID IS NULL)
 -- Device Info and Search Table --
 
 BEGIN
-   EXECUTE IMMEDIATE 'DROP TABLE DM_DEVICE_INFO';
-EXCEPTION
-   WHEN OTHERS THEN NULL;
+  EXECUTE IMMEDIATE 'DROP TABLE DM_DEVICE_INFO';
+  EXCEPTION
+  WHEN OTHERS THEN NULL;
 END;
 /
 
@@ -702,10 +750,10 @@ CREATE TABLE DM_DEVICE_INFO (
   KEY_FIELD VARCHAR2(45) NULL,
   VALUE_FIELD VARCHAR2(100) NULL,
   PRIMARY KEY (ID)
- ,
+  ,
   CONSTRAINT DM_DEVICE_INFO_DEVICE
-    FOREIGN KEY (DEVICE_ID)
-    REFERENCES DM_DEVICE (ID)
+  FOREIGN KEY (DEVICE_ID)
+  REFERENCES DM_DEVICE (ID)
 )
 ;
 
@@ -713,21 +761,17 @@ CREATE TABLE DM_DEVICE_INFO (
 CREATE SEQUENCE DM_DEVICE_INFO_seq START WITH 1 INCREMENT BY 1;
 
 CREATE OR REPLACE TRIGGER DM_DEVICE_INFO_seq_tr
- BEFORE INSERT ON DM_DEVICE_INFO FOR EACH ROW
- WHEN (NEW.ID IS NULL)
-BEGIN
- SELECT DM_DEVICE_INFO_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
-END;
+BEFORE INSERT ON DM_DEVICE_INFO FOR EACH ROW
+WHEN (NEW.ID IS NULL)
+  BEGIN
+    SELECT DM_DEVICE_INFO_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
+  END;
 /
 
-CREATE INDEX DM_DEVICE_INFO_DEVICE_idx ON DM_DEVICE_INFO (DEVICE_ID ASC);
-
-
-
 BEGIN
-   EXECUTE IMMEDIATE 'DROP TABLE DM_DEVICE_LOCATION';
-EXCEPTION
-   WHEN OTHERS THEN NULL;
+  EXECUTE IMMEDIATE 'DROP TABLE DM_DEVICE_LOCATION';
+  EXCEPTION
+  WHEN OTHERS THEN NULL;
 END;
 /
 
@@ -744,10 +788,10 @@ CREATE TABLE DM_DEVICE_LOCATION (
   COUNTRY VARCHAR2(45) NULL,
   UPDATE_TIMESTAMP NUMBER(19) NOT NULL,
   PRIMARY KEY (ID)
- ,
+  ,
   CONSTRAINT DM_DEVICE_LOCATION_DEVICE
-    FOREIGN KEY (DEVICE_ID)
-    REFERENCES DM_DEVICE (ID)
+  FOREIGN KEY (DEVICE_ID)
+  REFERENCES DM_DEVICE (ID)
 )
 ;
 
@@ -755,21 +799,17 @@ CREATE TABLE DM_DEVICE_LOCATION (
 CREATE SEQUENCE DM_DEVICE_LOCATION_seq START WITH 1 INCREMENT BY 1;
 
 CREATE OR REPLACE TRIGGER DM_DEVICE_LOCATION_seq_tr
- BEFORE INSERT ON DM_DEVICE_LOCATION FOR EACH ROW
- WHEN (NEW.ID IS NULL)
-BEGIN
- SELECT DM_DEVICE_LOCATION_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
-END;
+BEFORE INSERT ON DM_DEVICE_LOCATION FOR EACH ROW
+WHEN (NEW.ID IS NULL)
+  BEGIN
+    SELECT DM_DEVICE_LOCATION_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
+  END;
 /
 
-CREATE INDEX DM_DEVICE_LOCATION_DEVICE_idx ON DM_DEVICE_LOCATION (DEVICE_ID ASC);
-
-
-
 BEGIN
-   EXECUTE IMMEDIATE 'DROP TABLE DM_DEVICE_DETAIL';
-EXCEPTION
-   WHEN OTHERS THEN NULL;
+  EXECUTE IMMEDIATE 'DROP TABLE DM_DEVICE_DETAIL';
+  EXCEPTION
+  WHEN OTHERS THEN NULL;
 END;
 /
 
@@ -793,20 +833,73 @@ CREATE TABLE DM_DEVICE_DETAIL (
   UPDATE_TIMESTAMP NUMBER(19) NOT NULL,
   PRIMARY KEY (ID),
   CONSTRAINT FK_DM_DEVICE_DETAILS_DEVICE
-    FOREIGN KEY (DEVICE_ID)
-    REFERENCES DM_DEVICE (ID)
+  FOREIGN KEY (DEVICE_ID)
+  REFERENCES DM_DEVICE (ID)
 );
 
 -- Generate ID using sequence and trigger
 CREATE SEQUENCE DM_DEVICE_DETAIL_seq START WITH 1 INCREMENT BY 1;
 
 CREATE OR REPLACE TRIGGER DM_DEVICE_DETAIL_seq_tr
- BEFORE INSERT ON DM_DEVICE_DETAIL FOR EACH ROW
- WHEN (NEW.ID IS NULL)
-BEGIN
- SELECT DM_DEVICE_DETAIL_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
-END;
+BEFORE INSERT ON DM_DEVICE_DETAIL FOR EACH ROW
+WHEN (NEW.ID IS NULL)
+  BEGIN
+    SELECT DM_DEVICE_DETAIL_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
+  END;
 /
 
-CREATE INDEX FK_DM_DEVICE_DETAILS_DEVICE_idx ON DM_DEVICE_DETAIL (DEVICE_ID ASC);
+-- DASHBOARD RELATED VIEWS --
+
+CREATE VIEW DEVICES_VIEW_1 AS
+SELECT
+DEVICE_INFO.DEVICE_ID,
+DEVICE_INFO.PLATFORM,
+DEVICE_INFO.OWNERSHIP,
+DEVICE_INFO.CONNECTIVITY_STATUS,
+NVL(DEVICE_WITH_POLICY_INFO.POLICY_ID, -1) AS POLICY_ID,
+NVL(DEVICE_WITH_POLICY_INFO.IS_COMPLIANT, -1) AS IS_COMPLIANT,
+DEVICE_INFO.TENANT_ID
+FROM
+(SELECT
+DM_DEVICE.ID AS DEVICE_ID,
+DM_DEVICE_TYPE.NAME AS PLATFORM,
+DM_ENROLMENT.OWNERSHIP,
+DM_ENROLMENT.STATUS AS CONNECTIVITY_STATUS,
+DM_DEVICE.TENANT_ID
+FROM DM_DEVICE, DM_DEVICE_TYPE, DM_ENROLMENT
+WHERE DM_DEVICE.DEVICE_TYPE_ID = DM_DEVICE_TYPE.ID AND DM_DEVICE.ID = DM_ENROLMENT.DEVICE_ID) DEVICE_INFO
+LEFT JOIN
+(SELECT
+DEVICE_ID,
+POLICY_ID,
+STATUS AS IS_COMPLIANT
+FROM DM_POLICY_COMPLIANCE_STATUS) DEVICE_WITH_POLICY_INFO
+ON DEVICE_INFO.DEVICE_ID = DEVICE_WITH_POLICY_INFO.DEVICE_ID
+ORDER BY DEVICE_INFO.DEVICE_ID;
+
+CREATE VIEW DEVICES_VIEW_2 AS
+SELECT
+DM_DEVICE.ID AS DEVICE_ID,
+DM_DEVICE_DETAIL.DEVICE_MODEL,
+DM_DEVICE_DETAIL.VENDOR,
+DM_DEVICE_DETAIL.OS_VERSION,
+DM_ENROLMENT.OWNERSHIP,
+DM_ENROLMENT.OWNER,
+DM_ENROLMENT.STATUS AS CONNECTIVITY_STATUS,
+DM_POLICY_COMPLIANCE_STATUS.POLICY_ID,
+DM_DEVICE_TYPE.NAME AS PLATFORM,
+DM_POLICY_COMPLIANCE_FEATURES.FEATURE_CODE,
+DM_POLICY_COMPLIANCE_FEATURES.STATUS AS IS_COMPLAINT,
+DM_DEVICE.TENANT_ID
+FROM
+DM_POLICY_COMPLIANCE_FEATURES, DM_POLICY_COMPLIANCE_STATUS, DM_ENROLMENT, DM_DEVICE, DM_DEVICE_TYPE, DM_DEVICE_DETAIL
+WHERE
+DM_POLICY_COMPLIANCE_FEATURES.COMPLIANCE_STATUS_ID = DM_POLICY_COMPLIANCE_STATUS.ID AND
+DM_POLICY_COMPLIANCE_STATUS.ENROLMENT_ID = DM_ENROLMENT.ID AND
+DM_POLICY_COMPLIANCE_STATUS.DEVICE_ID = DM_DEVICE.ID AND
+DM_DEVICE.DEVICE_TYPE_ID = DM_DEVICE_TYPE.ID AND
+DM_DEVICE.ID = DM_DEVICE_DETAIL.DEVICE_ID
+ORDER BY TENANT_ID, DEVICE_ID;
+
+-- END OF DASHBOARD RELATED VIEWS --
 
diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/postgresql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/postgresql.sql
index 5fcf3f99ac..7e8c8a3e0c 100644
--- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/postgresql.sql
+++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/postgresql.sql
@@ -8,8 +8,8 @@ CREATE TABLE IF NOT EXISTS  DM_DEVICE_TYPE (
 CREATE TABLE IF NOT EXISTS DM_DEVICE_CERTIFICATE (
   ID                    BIGSERIAL NOT NULL PRIMARY KEY,
   SERIAL_NUMBER         VARCHAR(500) DEFAULT NULL,
-  CERTIFICATE           BYTEA DEFAULT NULL
-  TENANT_ID INTEGER DEFAULT 0,
+  CERTIFICATE           BYTEA DEFAULT NULL,
+  TENANT_ID INTEGER DEFAULT 0
 );
 
 CREATE TABLE IF NOT EXISTS  DM_DEVICE (
@@ -292,10 +292,9 @@ CREATE TABLE IF NOT EXISTS  DM_POLICY_COMPLIANCE_FEATURES (
   ON UPDATE NO ACTION
 );
 
-CREATE SEQUENCE DM_DEVICE_GROUP_POLICY_seq;
 
 CREATE TABLE IF NOT EXISTS DM_DEVICE_GROUP_POLICY (
-  ID INT NOT NULL DEFAULT NEXTVAL ('DM_DEVICE_GROUP_POLICY_seq'),
+  ID BIGSERIAL NOT NULL PRIMARY KEY,
   DEVICE_GROUP_ID INT NOT NULL,
   POLICY_ID INT NOT NULL,
   TENANT_ID INT NOT NULL,
@@ -306,8 +305,8 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_GROUP_POLICY (
     ON DELETE NO ACTION
     ON UPDATE NO ACTION,
   CONSTRAINT FK_DM_DEVICE_GROUP_DM_POLICY
-    FOREIGN KEY (POLICY_ID , DEVICE_GROUP_ID)
-    REFERENCES DM_POLICY (ID , ID)
+    FOREIGN KEY (POLICY_ID)
+    REFERENCES DM_POLICY (ID)
     ON DELETE NO ACTION
     ON UPDATE NO ACTION
 );
@@ -373,11 +372,10 @@ CREATE TABLE IF NOT EXISTS  DM_NOTIFICATION (
 
 
 CREATE TABLE IF NOT EXISTS DM_DEVICE_INFO (
-  ID INTEGER DEFAULT NEXTVAL ('DM_DEVICE_INFO_seq') NOT NULL,
+  ID BIGSERIAL NOT NULL PRIMARY KEY,
   DEVICE_ID INT NULL,
   KEY_FIELD VARCHAR(45) NULL,
   VALUE_FIELD VARCHAR(100) NULL,
-  PRIMARY KEY (ID) ,
   CONSTRAINT DM_DEVICE_INFO_DEVICE
     FOREIGN KEY (DEVICE_ID)
     REFERENCES DM_DEVICE (ID)
@@ -385,11 +383,9 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_INFO (
     ON UPDATE NO ACTION
 );
 
-CREATE INDEX DM_DEVICE_INFO_DEVICE_idx ON DM_DEVICE_INFO (DEVICE_ID ASC);
-
 
 CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
-  ID INTEGER DEFAULT NEXTVAL ('DM_DEVICE_LOCATION_seq') NOT NULL,
+  ID BIGSERIAL NOT NULL PRIMARY KEY,
   DEVICE_ID INT NULL,
   LATITUDE DOUBLE PRECISION NULL,
   LONGITUDE DOUBLE PRECISION NULL,
@@ -400,7 +396,6 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
   STATE VARCHAR(45) NULL,
   COUNTRY VARCHAR(45) NULL,
   UPDATE_TIMESTAMP BIGINT NOT NULL,
-  PRIMARY KEY (ID) ,
   CONSTRAINT DM_DEVICE_LOCATION_DEVICE
     FOREIGN KEY (DEVICE_ID)
     REFERENCES DM_DEVICE (ID)
@@ -408,13 +403,9 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
     ON UPDATE NO ACTION
 );
 
-CREATE INDEX DM_DEVICE_LOCATION_DEVICE_idx ON DM_DEVICE_LOCATION (DEVICE_ID ASC);
-
-
-CREATE SEQUENCE DM_DEVICE_DETAIL_seq;
 
 CREATE TABLE IF NOT EXISTS DM_DEVICE_DETAIL (
-  ID INT NOT NULL DEFAULT NEXTVAL ('DM_DEVICE_DETAIL_seq'),
+  ID BIGSERIAL NOT NULL PRIMARY KEY,
   DEVICE_ID INT NOT NULL,
   DEVICE_MODEL VARCHAR(45) NULL,
   VENDOR VARCHAR(45) NULL,
@@ -431,7 +422,6 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_DETAIL (
   AVAILABLE_RAM_MEMORY DECIMAL(30,3) NULL,
   PLUGGED_IN INT NULL,
   UPDATE_TIMESTAMP BIGINT NOT NULL,
-  PRIMARY KEY (ID),
   CONSTRAINT FK_DM_DEVICE_DETAILS_DEVICE
     FOREIGN KEY (DEVICE_ID)
     REFERENCES DM_DEVICE (ID)
@@ -439,5 +429,58 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_DETAIL (
     ON UPDATE NO ACTION
 );
 
-CREATE INDEX FK_DM_DEVICE_DETAILS_DEVICE_idx ON DM_DEVICE_DETAIL (DEVICE_ID ASC);
+-- DASHBOARD RELATED VIEWS --
+
+CREATE VIEW DEVICES_VIEW_1 AS
+SELECT
+DEVICE_INFO.DEVICE_ID,
+DEVICE_INFO.PLATFORM,
+DEVICE_INFO.OWNERSHIP,
+DEVICE_INFO.CONNECTIVITY_STATUS,
+COALESCE(DEVICE_WITH_POLICY_INFO.POLICY_ID, -1) AS POLICY_ID,
+COALESCE(DEVICE_WITH_POLICY_INFO.IS_COMPLIANT, -1) AS IS_COMPLIANT,
+DEVICE_INFO.TENANT_ID
+FROM
+(SELECT
+DM_DEVICE.ID AS DEVICE_ID,
+DM_DEVICE_TYPE.NAME AS PLATFORM,
+DM_ENROLMENT.OWNERSHIP,
+DM_ENROLMENT.STATUS AS CONNECTIVITY_STATUS,
+DM_DEVICE.TENANT_ID
+FROM DM_DEVICE, DM_DEVICE_TYPE, DM_ENROLMENT
+WHERE DM_DEVICE.DEVICE_TYPE_ID = DM_DEVICE_TYPE.ID AND DM_DEVICE.ID = DM_ENROLMENT.DEVICE_ID) DEVICE_INFO
+LEFT JOIN
+(SELECT
+DEVICE_ID,
+POLICY_ID,
+STATUS AS IS_COMPLIANT
+FROM DM_POLICY_COMPLIANCE_STATUS) DEVICE_WITH_POLICY_INFO
+ON DEVICE_INFO.DEVICE_ID = DEVICE_WITH_POLICY_INFO.DEVICE_ID
+ORDER BY DEVICE_INFO.DEVICE_ID;
+
+CREATE VIEW DEVICES_VIEW_2 AS
+SELECT
+DM_DEVICE.ID AS DEVICE_ID,
+DM_DEVICE_DETAIL.DEVICE_MODEL,
+DM_DEVICE_DETAIL.VENDOR,
+DM_DEVICE_DETAIL.OS_VERSION,
+DM_ENROLMENT.OWNERSHIP,
+DM_ENROLMENT.OWNER,
+DM_ENROLMENT.STATUS AS CONNECTIVITY_STATUS,
+DM_POLICY_COMPLIANCE_STATUS.POLICY_ID,
+DM_DEVICE_TYPE.NAME AS PLATFORM,
+DM_POLICY_COMPLIANCE_FEATURES.FEATURE_CODE,
+DM_POLICY_COMPLIANCE_FEATURES.STATUS AS IS_COMPLAINT,
+DM_DEVICE.TENANT_ID
+FROM
+DM_POLICY_COMPLIANCE_FEATURES, DM_POLICY_COMPLIANCE_STATUS, DM_ENROLMENT, DM_DEVICE, DM_DEVICE_TYPE, DM_DEVICE_DETAIL
+WHERE
+DM_POLICY_COMPLIANCE_FEATURES.COMPLIANCE_STATUS_ID = DM_POLICY_COMPLIANCE_STATUS.ID AND
+DM_POLICY_COMPLIANCE_STATUS.ENROLMENT_ID = DM_ENROLMENT.ID AND
+DM_POLICY_COMPLIANCE_STATUS.DEVICE_ID = DM_DEVICE.ID AND
+DM_DEVICE.DEVICE_TYPE_ID = DM_DEVICE_TYPE.ID AND
+DM_DEVICE.ID = DM_DEVICE_DETAIL.DEVICE_ID
+ORDER BY TENANT_ID, DEVICE_ID;
+
+-- END OF DASHBOARD RELATED VIEWS --