[rhq] Branch 'feature/cassandra-backend' - modules/common modules/enterprise
by John Sanda
modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java | 4 ++--
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
New commits:
commit 5b031a3d75b1e7893d455f8d2ab73f96d424306e
Author: John Sanda <jsanda(a)redhat.com>
Date: Fri May 3 23:16:57 2013 -0400
fixing credentials for tests
With changes to SchemaManager, we need to pass the default super user
diff --git a/modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java b/modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java
index f456511..1a25f88 100644
--- a/modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java
+++ b/modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java
@@ -85,8 +85,8 @@ public class CCMSuiteDeploymentExtension implements LoadableExtension {
DeploymentOptionsFactory factory = new DeploymentOptionsFactory();
DeploymentOptions options = factory.newDeploymentOptions();
options.setClusterDir(clusterDir.getAbsolutePath());
- options.setUsername("rhqadmin");
- options.setPassword("rhqadmin");
+ options.setUsername("cassandra");
+ options.setPassword("cassandra");
ccm = new CassandraClusterManager(options);
}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
index 84a1c07..3a9679e 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
@@ -51,7 +51,7 @@ public class CassandraIntegrationTest {
private static DateTimeService dateTimeService;
@BeforeSuite
- @DeployCluster(numNodes = 2)
+ @DeployCluster(numNodes = 2, username = "cassandra", password = "cassandra")
public void deployCluster() throws Exception {
dateTimeService = new DateTimeService();
11 years
[rhq] Branch 'refs/tags/RHQ_4_7_0_JONCI' - modules/enterprise
by snegrea
modules/enterprise/agentupdate/pom.xml | 2 ++
1 file changed, 2 insertions(+)
New commits:
commit 6964581886d2fb846b79138e9143a918ec9f7824
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Fri May 3 17:17:52 2013 -0500
[BZ 959591] Add back specific ant version based on local pom variables to force the download of the dependency even if not directly used in the pom. Without this, the pom would assume that the version referred here is the version from parent poms. This can cause the submodule artifact to miss ant binaries if not already in the local maven repository.
(cherry picked from commit 2bb45d3c4c9d5edc71acf4380ce7a10a93937aae)
diff --git a/modules/enterprise/agentupdate/pom.xml b/modules/enterprise/agentupdate/pom.xml
index 75cfda1..329f7fb 100644
--- a/modules/enterprise/agentupdate/pom.xml
+++ b/modules/enterprise/agentupdate/pom.xml
@@ -37,12 +37,14 @@
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
11 years
[rhq] Branch 'nightly/rhq.jon' - modules/enterprise
by snegrea
modules/enterprise/agentupdate/pom.xml | 2 ++
1 file changed, 2 insertions(+)
New commits:
commit 6964581886d2fb846b79138e9143a918ec9f7824
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Fri May 3 17:17:52 2013 -0500
[BZ 959591] Add back specific ant version based on local pom variables to force the download of the dependency even if not directly used in the pom. Without this, the pom would assume that the version referred here is the version from parent poms. This can cause the submodule artifact to miss ant binaries if not already in the local maven repository.
(cherry picked from commit 2bb45d3c4c9d5edc71acf4380ce7a10a93937aae)
diff --git a/modules/enterprise/agentupdate/pom.xml b/modules/enterprise/agentupdate/pom.xml
index 75cfda1..329f7fb 100644
--- a/modules/enterprise/agentupdate/pom.xml
+++ b/modules/enterprise/agentupdate/pom.xml
@@ -37,12 +37,14 @@
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
11 years
[rhq] Branch 'refs/tags/RHQ_4_7_0' - modules/enterprise
by snegrea
modules/enterprise/agentupdate/pom.xml | 2 ++
1 file changed, 2 insertions(+)
New commits:
commit 51e5261c7faf7d2edf6bb2c5bec8d549897c601c
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Fri May 3 17:12:12 2013 -0500
[BZ 959591] Add back specific ant version based on local pom variables to force the download of the dependency even if not directly used in the pom. Without this, the pom would assume that the version referred here is the version from parent poms. This can cause the submodule artifact to miss ant binaries if not already in the local maven repository.
(cherry picked from commit 2bb45d3c4c9d5edc71acf4380ce7a10a93937aae)
diff --git a/modules/enterprise/agentupdate/pom.xml b/modules/enterprise/agentupdate/pom.xml
index cd77bfb..899eeeb 100644
--- a/modules/enterprise/agentupdate/pom.xml
+++ b/modules/enterprise/agentupdate/pom.xml
@@ -37,12 +37,14 @@
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
11 years
[rhq] Branch 'release-4.7.0' - modules/enterprise
by snegrea
modules/enterprise/agentupdate/pom.xml | 2 ++
1 file changed, 2 insertions(+)
New commits:
commit 51e5261c7faf7d2edf6bb2c5bec8d549897c601c
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Fri May 3 17:12:12 2013 -0500
[BZ 959591] Add back specific ant version based on local pom variables to force the download of the dependency even if not directly used in the pom. Without this, the pom would assume that the version referred here is the version from parent poms. This can cause the submodule artifact to miss ant binaries if not already in the local maven repository.
(cherry picked from commit 2bb45d3c4c9d5edc71acf4380ce7a10a93937aae)
diff --git a/modules/enterprise/agentupdate/pom.xml b/modules/enterprise/agentupdate/pom.xml
index cd77bfb..899eeeb 100644
--- a/modules/enterprise/agentupdate/pom.xml
+++ b/modules/enterprise/agentupdate/pom.xml
@@ -37,12 +37,14 @@
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
11 years
[rhq] modules/enterprise
by snegrea
modules/enterprise/agentupdate/pom.xml | 2 ++
1 file changed, 2 insertions(+)
New commits:
commit 1dba477c3fc0034a5a21960db8447fa9b43f5a56
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Fri May 3 17:10:53 2013 -0500
[BZ 959591] Add back specific ant version based on local pom variables to force the download of the dependency even if not directly used in the pom. Without this, the pom would assume that the version referred here is the version from parent poms. This can cause the submodule artifact to miss ant binaries if not already in the local maven repository.
(cherry picked from commit 2bb45d3c4c9d5edc71acf4380ce7a10a93937aae)
diff --git a/modules/enterprise/agentupdate/pom.xml b/modules/enterprise/agentupdate/pom.xml
index ddec938..3b66b92 100644
--- a/modules/enterprise/agentupdate/pom.xml
+++ b/modules/enterprise/agentupdate/pom.xml
@@ -37,12 +37,14 @@
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
11 years
[rhq] Branch 'pre-release-4.7.0' - modules/enterprise
by snegrea
modules/enterprise/agentupdate/pom.xml | 2 ++
1 file changed, 2 insertions(+)
New commits:
commit 2bb45d3c4c9d5edc71acf4380ce7a10a93937aae
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Fri May 3 17:09:31 2013 -0500
[BZ 959591] Add back specific ant version based on local pom variables to force the download of the dependency even if not directly used in the pom. Without this, the pom would assume that the version referred here is the version from parent poms. This can cause the submodule artifact to miss ant binaries if not already in the local maven repository.
diff --git a/modules/enterprise/agentupdate/pom.xml b/modules/enterprise/agentupdate/pom.xml
index ddec938..3b66b92 100644
--- a/modules/enterprise/agentupdate/pom.xml
+++ b/modules/enterprise/agentupdate/pom.xml
@@ -37,12 +37,14 @@
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
+ <version>${ant.version}</version>
<scope>provided</scope>
</dependency>
11 years
[rhq] modules/enterprise
by mike thompson
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/charttype/MetricNvd3BarChartGraph.java | 8
modules/enterprise/gui/coregui/src/main/webapp/css/nv.d3.css | 128
modules/enterprise/gui/coregui/src/main/webapp/js/nv.d3.js | 2855 ++++++++--
3 files changed, 2444 insertions(+), 547 deletions(-)
New commits:
commit 17865107e38aa503c35f5a608cc1972c09bf5a73
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Fri May 3 15:01:26 2013 -0700
[BZ 958754] - d3 multi-line chart: time units should have minutes when appropriate. Added the appropriate d3 time format string and updated the nvd3 chart library neither of which fixed the issue. It appears to be an error in nvd3 chart library.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/charttype/MetricNvd3BarChartGraph.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/charttype/MetricNvd3BarChartGraph.java
index 1fec750..a2de9c6 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/charttype/MetricNvd3BarChartGraph.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/charttype/MetricNvd3BarChartGraph.java
@@ -19,7 +19,7 @@
package org.rhq.enterprise.gui.coregui.client.inventory.common.charttype;
/**
- * Contains the chart definition for a Bar Chart Graph.
+ * Contains the chart definition for a MultiLine Graph.
*
* @author Mike Thompson
*/
@@ -39,7 +39,7 @@ public final class MetricNvd3BarChartGraph extends AbstractGraph
*/
public native void drawJsniChart() /*-{
console.log("Draw NVD3 Bar jsni chart");
- console.time("multiChart")
+ console.time("multiChart");
var global = this,
chartId = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.charttype.MetricGraphData::getChartId()(),
chartHandle = "#rChart-"+chartId,
@@ -49,7 +49,7 @@ public final class MetricNvd3BarChartGraph extends AbstractGraph
yAxisUnits = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.charttype.MetricGraphData::getYAxisUnits()(),
xAxisLabel = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.charttype.MetricGraphData::getXAxisTitle()(),
displayDayOfWeek = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.charttype.MetricGraphData::shouldDisplayDayOfWeekInXAxisLabel()(),
- xAxisTimeFormat = (displayDayOfWeek) ? "%a %I %p" : "%I %p",
+ xAxisTimeFormat = (displayDayOfWeek) ? "%a %I %p" : "%I : %M %p",
// nvd3 defines their json models a standard way (same model for other graphs)
data = function() {
@@ -63,7 +63,7 @@ public final class MetricNvd3BarChartGraph extends AbstractGraph
};
$wnd.nv.addGraph(function() {
var chart = $wnd.nv.models.multiBarChart()
- .showControls(false)
+ .showControls(true)
.tooltips(true);
chart.xAxis.axisLabel(xAxisLabel)
diff --git a/modules/enterprise/gui/coregui/src/main/webapp/css/nv.d3.css b/modules/enterprise/gui/coregui/src/main/webapp/css/nv.d3.css
index cc40dfb..163b9a8 100755
--- a/modules/enterprise/gui/coregui/src/main/webapp/css/nv.d3.css
+++ b/modules/enterprise/gui/coregui/src/main/webapp/css/nv.d3.css
@@ -18,8 +18,8 @@
.nvtooltip {
position: absolute;
background-color: rgba(255,255,255,1);
- padding: 10px;
- border: 1px solid #ddd;
+ padding: 1px;
+ border: 1px solid rgba(0,0,0,.2);
z-index: 10000;
font-family: Arial;
@@ -33,12 +33,13 @@
-moz-transition-delay: 500ms;
-webkit-transition-delay: 500ms;
- -moz-box-shadow: 4px 4px 8px rgba(0,0,0,.5);
- -webkit-box-shadow: 4px 4px 8px rgba(0,0,0,.5);
- box-shadow: 4px 4px 8px rgba(0,0,0,.5);
+ -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
+ -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
+ box-shadow: 0 5px 10px rgba(0,0,0,.2);
- -moz-border-radius: 10px;
- border-radius: 10px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
pointer-events: none;
@@ -50,15 +51,29 @@
user-select: none;
}
+.nvtooltip.x-nvtooltip,
+.nvtooltip.y-nvtooltip {
+ padding: 8px;
+}
+
.nvtooltip h3 {
margin: 0;
- padding: 0;
+ padding: 4px 14px;
+ line-height: 18px;
+ font-weight: normal;
+ background-color: #f7f7f7;
text-align: center;
+
+ border-bottom: 1px solid #ebebeb;
+
+ -webkit-border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ border-radius: 5px 5px 0 0;
}
.nvtooltip p {
margin: 0;
- padding: 0;
+ padding: 5px 14px;
text-align: center;
}
@@ -93,7 +108,7 @@ svg {
svg text {
- font: normal 12px sans-serif;
+ font: normal 12px Arial;
}
svg .title {
@@ -101,7 +116,8 @@ svg .title {
}
.nvd3 .nv-background {
- fill: none;
+ fill: white;
+ fill-opacity: 0;
/*
pointer-events: none;
*/
@@ -109,7 +125,7 @@ svg .title {
.nvd3.nv-noData {
font-size: 18px;
- font-weight: bolf;
+ font-weight: bold;
}
@@ -197,12 +213,10 @@ svg .title {
.nvd3 .nv-bars .negative rect {
zfill: brown;
- cursor: pointer;
}
.nvd3 .nv-bars rect {
zfill: steelblue;
- cursor: pointer;
fill-opacity: .75;
transition: fill-opacity 250ms linear;
@@ -226,10 +240,6 @@ svg .title {
fill: rgba(0,0,0,1);
}
-.nvd3 .nv-x.nv-axis text {
- transform: rotate(90);
-}
-
/**********
* Bars
@@ -302,10 +312,10 @@ svg .title {
.nvd3 .nv-groups path.nv-line {
fill: none;
stroke-width: 2.5px;
+ /*
stroke-linecap: round;
shape-rendering: geometricPrecision;
- /*
transition: stroke-width 250ms linear;
-moz-transition: stroke-width 250ms linear;
-webkit-transition: stroke-width 250ms linear;
@@ -318,10 +328,10 @@ svg .title {
.nvd3 .nv-groups path.nv-area {
stroke: none;
+ /*
stroke-linecap: round;
shape-rendering: geometricPrecision;
- /*
stroke-width: 2.5px;
transition: stroke-width 250ms linear;
-moz-transition: stroke-width 250ms linear;
@@ -350,8 +360,8 @@ svg .title {
}
.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point {
- fill-opacity: .5;
- stroke-opacity: .5;
+ fill-opacity: .5 !important;
+ stroke-opacity: .5 !important;
}
@@ -397,9 +407,11 @@ svg .title {
* Scatter
*/
+/* **Attempting to remove this for useVoronoi(false), need to see if it's required anywhere
.nvd3 .nv-groups .nv-point {
pointer-events: none;
}
+*/
.nvd3 .nv-groups .nv-point.hover {
stroke-width: 20px;
@@ -485,21 +497,27 @@ svg .title {
* Bullet
*/
-.nvd3.nv-bullet { font: 10px sans-serif; cursor: pointer; }
-.nvd3.nv-bullet rect { fill-opacity: .6; }
-.nvd3.nv-bullet rect:hover { fill-opacity: 1; }
+.nvd3.nv-bullet { font: 10px sans-serif; }
+.nvd3.nv-bullet .nv-measure { fill-opacity: .8; }
+.nvd3.nv-bullet .nv-measure:hover { fill-opacity: 1; }
.nvd3.nv-bullet .nv-marker { stroke: #000; stroke-width: 2px; }
.nvd3.nv-bullet .nv-markerTriangle { stroke: #000; fill: #fff; stroke-width: 1.5px; }
.nvd3.nv-bullet .nv-tick line { stroke: #666; stroke-width: .5px; }
.nvd3.nv-bullet .nv-range.nv-s0 { fill: #eee; }
.nvd3.nv-bullet .nv-range.nv-s1 { fill: #ddd; }
.nvd3.nv-bullet .nv-range.nv-s2 { fill: #ccc; }
-.nvd3.nv-bullet .nv-measure.nv-s0 { fill: steelblue; }
-.nvd3.nv-bullet .nv-measure.nv-s1 { fill: darkblue; }
.nvd3.nv-bullet .nv-title { font-size: 14px; font-weight: bold; }
.nvd3.nv-bullet .nv-subtitle { fill: #999; }
+.nvd3.nv-bullet .nv-range {
+ fill: #999;
+ fill-opacity: .4;
+}
+.nvd3.nv-bullet .nv-range:hover {
+ fill-opacity: .7;
+}
+
/**********
@@ -515,7 +533,7 @@ svg .title {
}
.nvd3.nv-sparklineplus .nv-hoverValue line {
- stroke: #f44;
+ stroke: #333;
stroke-width: 1.5px;
}
@@ -531,8 +549,11 @@ svg .title {
.nvd3.nv-sparklineplus .nv-xValue,
.nvd3.nv-sparklineplus .nv-yValue {
+ /*
stroke: #666;
- font-size: .5em;
+ */
+ stroke-width: 0;
+ font-size: .9em;
font-weight: normal;
}
@@ -540,6 +561,24 @@ svg .title {
stroke: #f66;
}
+.nvd3.nv-sparklineplus .nv-maxValue {
+ stroke: #2ca02c;
+ fill: #2ca02c;
+}
+
+.nvd3.nv-sparklineplus .nv-minValue {
+ stroke: #d62728;
+ fill: #d62728;
+}
+
+.nvd3.nv-sparklineplus .nv-currentValue {
+ /*
+ stroke: #444;
+ fill: #000;
+ */
+ font-weight: bold;
+ font-size: 1.1em;
+}
/**********
* historical stock
@@ -629,4 +668,35 @@ svg .title {
*/
}
+/**********
+* Parallel Coordinates
+*/
+
+.nvd3 .background path {
+ fill: none;
+ stroke: #ccc;
+ stroke-opacity: .4;
+ shape-rendering: crispEdges;
+}
+
+.nvd3 .foreground path {
+ fill: none;
+ stroke: steelblue;
+ stroke-opacity: .7;
+}
+
+.nvd3 .brush .extent {
+ fill-opacity: .3;
+ stroke: #fff;
+ shape-rendering: crispEdges;
+}
+
+.nvd3 .axis line, .axis path {
+ fill: none;
+ stroke: #000;
+ shape-rendering: crispEdges;
+}
+.nvd3 .axis text {
+ text-shadow: 0 1px 0 #fff;
+}
\ No newline at end of file
diff --git a/modules/enterprise/gui/coregui/src/main/webapp/js/nv.d3.js b/modules/enterprise/gui/coregui/src/main/webapp/js/nv.d3.js
index 2fe2c64..e405d2e 100644
--- a/modules/enterprise/gui/coregui/src/main/webapp/js/nv.d3.js
+++ b/modules/enterprise/gui/coregui/src/main/webapp/js/nv.d3.js
@@ -36,29 +36,34 @@ if (nv.dev) {
// Logs all arguments, and returns the last so you can test things in place
nv.log = function() {
- if (nv.dev && console.log && console.log.apply) console.log.apply(console, arguments);
+ if (nv.dev && console.log && console.log.apply)
+ console.log.apply(console, arguments)
+ else if (nv.dev && console.log && Function.prototype.bind) {
+ var log = Function.prototype.bind.call(console.log, console);
+ log.apply(console, arguments);
+ }
return arguments[arguments.length - 1];
};
nv.render = function render(step) {
- step = step || 1; // number of graphs to generate in each timout loop
+ step = step || 1; // number of graphs to generate in each timeout loop
- render.active = true;
+ nv.render.active = true;
nv.dispatch.render_start();
setTimeout(function() {
- var chart;
+ var chart, graph;
- for (var i = 0; i < step && (graph = render.queue[i]); i++) {
+ for (var i = 0; i < step && (graph = nv.render.queue[i]); i++) {
chart = graph.generate();
if (typeof graph.callback == typeof(Function)) graph.callback(chart);
nv.graphs.push(chart);
}
- render.queue.splice(0, i);
+ nv.render.queue.splice(0, i);
- if (render.queue.length) setTimeout(arguments.callee, 0);
+ if (nv.render.queue.length) setTimeout(arguments.callee, 0);
else { nv.render.active = false; nv.dispatch.render_end(); }
}, 0);
};
@@ -114,7 +119,7 @@ d3.time.monthEnds = d3_time_range(d3.time.monthEnd, function(date) {
/*****
- * A no frills tooltip implementation.
+ * A no-frills tooltip implementation.
*****/
@@ -143,39 +148,67 @@ d3.time.monthEnds = d3_time_range(d3.time.monthEnd, function(date) {
width = parseInt(container.offsetWidth),
windowWidth = nv.utils.windowSize().width,
windowHeight = nv.utils.windowSize().height,
- scrollTop = body.scrollTop,
- scrollLeft = body.scrollLeft,
+ scrollTop = window.scrollY,
+ scrollLeft = window.scrollX,
left, top;
+ windowHeight = window.innerWidth >= document.body.scrollWidth ? windowHeight : windowHeight - 16;
+ windowWidth = window.innerHeight >= document.body.scrollHeight ? windowWidth : windowWidth - 16;
+
+ var tooltipTop = function ( Elem ) {
+ var offsetTop = top;
+ do {
+ if( !isNaN( Elem.offsetTop ) ) {
+ offsetTop += (Elem.offsetTop);
+ }
+ } while( Elem = Elem.offsetParent );
+ return offsetTop;
+ }
+
+ var tooltipLeft = function ( Elem ) {
+ var offsetLeft = left;
+ do {
+ if( !isNaN( Elem.offsetLeft ) ) {
+ offsetLeft += (Elem.offsetLeft);
+ }
+ } while( Elem = Elem.offsetParent );
+ return offsetLeft;
+ }
switch (gravity) {
case 'e':
left = pos[0] - width - dist;
top = pos[1] - (height / 2);
- if (left < scrollLeft) left = pos[0] + dist;
- if (top < scrollTop) top = scrollTop + 5;
- if (top + height > scrollTop + windowHeight) top = scrollTop - height - 5;
+ var tLeft = tooltipLeft(container);
+ var tTop = tooltipTop(container);
+ if (tLeft < scrollLeft) left = pos[0] + dist > scrollLeft ? pos[0] + dist : scrollLeft - tLeft + left;
+ if (tTop < scrollTop) top = scrollTop - tTop + top;
+ if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
break;
case 'w':
left = pos[0] + dist;
top = pos[1] - (height / 2);
- if (left + width > windowWidth) left = pos[0] - width - dist;
- if (top < scrollTop) top = scrollTop + 5;
- if (top + height > scrollTop + windowHeight) top = scrollTop - height - 5;
+ if (tLeft + width > windowWidth) left = pos[0] - width - dist;
+ if (tTop < scrollTop) top = scrollTop + 5;
+ if (tTop + height > scrollTop + windowHeight) top = scrollTop - height - 5;
break;
case 'n':
- left = pos[0] - (width / 2);
+ left = pos[0] - (width / 2) - 5;
top = pos[1] + dist;
- if (left < scrollLeft) left = scrollLeft + 5;
- if (left + width > windowWidth) left = windowWidth - width - 5;
- if (top + height > scrollTop + windowHeight) top = pos[1] - height - dist;
+ var tLeft = tooltipLeft(container);
+ var tTop = tooltipTop(container);
+ if (tLeft < scrollLeft) left = scrollLeft + 5;
+ if (tLeft + width > windowWidth) left = left - width/2 + 5;
+ if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
break;
case 's':
left = pos[0] - (width / 2);
top = pos[1] - height - dist;
- if (left < scrollLeft) left = scrollLeft + 5;
- if (left + width > windowWidth) left = windowWidth - width - 5;
- if (scrollTop > top) top = pos[1] + 20;
+ var tLeft = tooltipLeft(container);
+ var tTop = tooltipTop(container);
+ if (tLeft < scrollLeft) left = scrollLeft + 5;
+ if (tLeft + width > windowWidth) left = left - width/2 + 5;
+ if (scrollTop > tTop) top = scrollTop;
break;
}
@@ -243,7 +276,7 @@ nv.utils.windowSize = function() {
// Easy way to bind multiple functions to window.onresize
-// TODO: give a way to remove a function after its bound, other than removing alkl of them
+// TODO: give a way to remove a function after its bound, other than removing all of them
nv.utils.windowResize = function(fun){
var oldresize = window.onresize;
@@ -255,7 +288,7 @@ nv.utils.windowResize = function(fun){
// Backwards compatible way to implement more d3-like coloring of graphs.
// If passed an array, wrap it in a function which implements the old default
-// behaviour
+// behavior
nv.utils.getColor = function(color) {
if (!arguments.length) return nv.utils.defaultColor(); //if you pass in nothing, get default colors back
@@ -263,7 +296,7 @@ nv.utils.getColor = function(color) {
return function(d, i) { return d.color || color[i % color.length]; };
else
return color;
- //can't really help it if someone passes rubish as color
+ //can't really help it if someone passes rubbish as color
}
// Default color chooser uses the index of an object as before.
@@ -328,7 +361,7 @@ nv.models.axis = function() {
;
var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 60 //only used for tickLabel currently
+ , width = 75 //only used for tickLabel currently
, height = 60 //only used for tickLabel currently
, scale = d3.scale.linear()
, axisLabelText = null
@@ -337,6 +370,7 @@ nv.models.axis = function() {
, rotateLabels = 0
, rotateYLabel = true
, staggerLabels = false
+ , isOrdinal = false
, ticks = null
;
@@ -398,11 +432,11 @@ nv.models.axis = function() {
axisLabel.exit().remove();
switch (axis.orient()) {
case 'top':
- axisLabel.enter().append('text').attr('class', 'nv-axislabel')
- .attr('text-anchor', 'middle')
- .attr('y', 0);
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
axisLabel
+ .attr('text-anchor', 'middle')
+ .attr('y', 0)
.attr('x', w/2);
if (showMaxMin) {
var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
@@ -428,7 +462,7 @@ nv.models.axis = function() {
}
break;
case 'bottom':
- var xLabelMargin = 30;
+ var xLabelMargin = 36;
var maxTextWidth = 30;
var xTicks = g.selectAll('g').select("text");
if (rotateLabels%360) {
@@ -445,20 +479,22 @@ nv.models.axis = function() {
.attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
.attr('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
}
- axisLabel.enter().append('text').attr('class', 'nv-axislabel')
- .attr('text-anchor', 'middle')
- .attr('y', xLabelMargin);
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
axisLabel
+ .attr('text-anchor', 'middle')
+ .attr('y', xLabelMargin)
.attr('x', w/2);
if (showMaxMin) {
+ //if (showMaxMin && !isOrdinal) {
var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
- .data(scale.domain());
+ //.data(scale.domain())
+ .data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]);
axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
axisMaxMin.exit().remove();
axisMaxMin
.attr('transform', function(d,i) {
- return 'translate(' + scale(d) + ',0)'
+ return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
})
.select('text')
.attr('dy', '.71em')
@@ -471,7 +507,9 @@ nv.models.axis = function() {
});
d3.transition(axisMaxMin)
.attr('transform', function(d,i) {
- return 'translate(' + scale.range()[i] + ',0)'
+ //return 'translate(' + scale.range()[i] + ',0)'
+ //return 'translate(' + scale(d) + ',0)'
+ return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
});
}
if (staggerLabels)
@@ -480,11 +518,11 @@ nv.models.axis = function() {
break;
case 'right':
- axisLabel.enter().append('text').attr('class', 'nv-axislabel')
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ axisLabel
.attr('text-anchor', rotateYLabel ? 'middle' : 'begin')
.attr('transform', rotateYLabel ? 'rotate(90)' : '')
- .attr('y', rotateYLabel ? (-Math.max(margin.right,width) - 12) : -10); //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
- axisLabel
+ .attr('y', rotateYLabel ? (-Math.max(margin.right,width) + 12) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
.attr('x', rotateYLabel ? (scale.range()[0] / 2) : axis.tickPadding());
if (showMaxMin) {
var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
@@ -514,11 +552,19 @@ nv.models.axis = function() {
}
break;
case 'left':
- axisLabel.enter().append('text').attr('class', 'nv-axislabel')
+ /*
+ //For dynamically placing the label. Can be used with dynamically-sized chart axis margins
+ var yTicks = g.selectAll('g').select("text");
+ yTicks.each(function(d,i){
+ var labelPadding = this.getBBox().width + axis.tickPadding() + 16;
+ if(labelPadding > width) width = labelPadding;
+ });
+ */
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ axisLabel
.attr('text-anchor', rotateYLabel ? 'middle' : 'end')
.attr('transform', rotateYLabel ? 'rotate(-90)' : '')
- .attr('y', rotateYLabel ? (-Math.max(margin.left,width) + 12) : -10); //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
- axisLabel
+ .attr('y', rotateYLabel ? (-Math.max(margin.left,width) + 12) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
.attr('x', rotateYLabel ? (-scale.range()[0] / 2) : -axis.tickPadding());
if (showMaxMin) {
var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
@@ -552,27 +598,41 @@ nv.models.axis = function() {
.text(function(d) { return d });
- //check if max and min overlap other values, if so, hide the values that overlap
if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) {
+ //check if max and min overlap other values, if so, hide the values that overlap
g.selectAll('g') // the g's wrapping each tick
.each(function(d,i) {
+ d3.select(this).select('text').attr('opacity', 1);
if (scale(d) < scale.range()[1] + 10 || scale(d) > scale.range()[0] - 10) { // 10 is assuming text height is 16... if d is 0, leave it!
if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
- d3.select(this).remove();
- else
- d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
+ d3.select(this).attr('opacity', 0);
+
+ d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!!
}
});
+
+ //if Max and Min = 0 only show min, Issue #281
+ if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0)
+ wrap.selectAll('g.nv-axisMaxMin')
+ .style('opacity', function(d,i) { return !i ? 1 : 0 });
+
}
if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
var maxMinRange = [];
wrap.selectAll('g.nv-axisMaxMin')
.each(function(d,i) {
- if (i) // i== 1, max position
- maxMinRange.push(scale(d) - this.getBBox().width - 4) //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
- else // i==0, min position
- maxMinRange.push(scale(d) + this.getBBox().width + 4)
+ try {
+ if (i) // i== 1, max position
+ maxMinRange.push(scale(d) - this.getBBox().width - 4) //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
+ else // i==0, min position
+ maxMinRange.push(scale(d) + this.getBBox().width + 4)
+ }catch (err) {
+ if (i) // i== 1, max position
+ maxMinRange.push(scale(d) - 4) //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
+ else // i==0, min position
+ maxMinRange.push(scale(d) + 4)
+ }
});
g.selectAll('g') // the g's wrapping each tick
.each(function(d,i) {
@@ -660,6 +720,7 @@ nv.models.axis = function() {
if (!arguments.length) return scale;
scale = _;
axis.scale(scale);
+ isOrdinal = typeof scale.rangeBands === 'function';
d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands');
return chart;
}
@@ -705,6 +766,7 @@ nv.models.historicalBar = function() {
, getY = function(d) { return d.y }
, forceX = []
, forceY = [0]
+ , padData = false
, clipEdge = true
, color = nv.utils.defaultColor()
, xDomain
@@ -726,9 +788,13 @@ nv.models.historicalBar = function() {
// Setup Scales
x .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ))
- .range([0, availableWidth]);
- y .domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) ))
+ if (padData)
+ x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range([0, availableWidth]);
+
+ y .domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) ))
.range([availableHeight, 0]);
// If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
@@ -846,13 +912,20 @@ nv.models.historicalBar = function() {
bars
.attr('fill', function(d,i) { return color(d, i); })
.attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i })
- .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - ((availableWidth / data[0].values.length) * .5)) + ',0)'; }) //TODO: better width calculations that don't assume always uniform data spacing;w
+ .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; }) //TODO: better width calculations that don't assume always uniform data spacing;w
.attr('width', (availableWidth / data[0].values.length) * .9 )
d3.transition(bars)
- .attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
- .attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) });
+ //.attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
+ .attr('y', function(d,i) {
+ return getY(d,i) < 0 ?
+ y(0) :
+ y(0) - y(getY(d,i)) < 1 ?
+ y(0) - 1 :
+ y(getY(d,i))
+ })
+ .attr('height', function(d,i) { return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) });
//.order(); // not sure if this makes any sense for this model
});
@@ -936,6 +1009,12 @@ nv.models.historicalBar = function() {
return chart;
};
+ chart.padData = function(_) {
+ if (!arguments.length) return padData;
+ padData = _;
+ return chart;
+ };
+
chart.clipEdge = function(_) {
if (!arguments.length) return clipEdge;
clipEdge = _;
@@ -980,6 +1059,7 @@ nv.models.bullet = function() {
, width = 380
, height = 30
, tickFormat = null
+ , color = nv.utils.getColor(['#1f77b4'])
, dispatch = d3.dispatch('elementMouseover', 'elementMouseout')
;
@@ -1002,7 +1082,7 @@ nv.models.bullet = function() {
// Compute the new x-scale.
var x1 = d3.scale.linear()
- .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) // TODO: need to allow forceX and forceY, and xDomain, yDomain
+ .domain( d3.extent(d3.merge([forceX, rangez])) )
.range(reverse ? [availableWidth, 0] : [0, availableWidth]);
// Retrieve the old x-scale, if this is an update.
@@ -1013,6 +1093,11 @@ nv.models.bullet = function() {
// Stash the new scale.
this.__chart__ = x1;
+
+ var rangeMin = d3.min(rangez), //rangez[2]
+ rangeMax = d3.max(rangez), //rangez[0]
+ rangeAvg = rangez[1];
+
//------------------------------------------------------------
@@ -1024,6 +1109,12 @@ nv.models.bullet = function() {
var gEnter = wrapEnter.append('g');
var g = wrap.select('g');
+ gEnter.append('rect').attr('class', 'nv-range nv-rangeMax');
+ gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg');
+ gEnter.append('rect').attr('class', 'nv-range nv-rangeMin');
+ gEnter.append('rect').attr('class', 'nv-measure');
+ gEnter.append('path').attr('class', 'nv-markerTriangle');
+
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
//------------------------------------------------------------
@@ -1032,8 +1123,118 @@ nv.models.bullet = function() {
var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
+ var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
+ xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
+
+
+ g.select('rect.nv-rangeMax')
+ .attr('height', availableHeight)
+ .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin))
+ .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin))
+ .datum(rangeMax > 0 ? rangeMax : rangeMin)
+ /*
+ .attr('x', rangeMin < 0 ?
+ rangeMax > 0 ?
+ x1(rangeMin)
+ : x1(rangeMax)
+ : x1(0))
+ */
+
+ g.select('rect.nv-rangeAvg')
+ .attr('height', availableHeight)
+ .attr('width', w1(rangeAvg))
+ .attr('x', xp1(rangeAvg))
+ .datum(rangeAvg)
+ /*
+ .attr('width', rangeMax <= 0 ?
+ x1(rangeMax) - x1(rangeAvg)
+ : x1(rangeAvg) - x1(rangeMin))
+ .attr('x', rangeMax <= 0 ?
+ x1(rangeAvg)
+ : x1(rangeMin))
+ */
+
+ g.select('rect.nv-rangeMin')
+ .attr('height', availableHeight)
+ .attr('width', w1(rangeMax))
+ .attr('x', xp1(rangeMax))
+ .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax))
+ .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax))
+ .datum(rangeMax > 0 ? rangeMin : rangeMax)
+ /*
+ .attr('width', rangeMax <= 0 ?
+ x1(rangeAvg) - x1(rangeMin)
+ : x1(rangeMax) - x1(rangeAvg))
+ .attr('x', rangeMax <= 0 ?
+ x1(rangeMin)
+ : x1(rangeAvg))
+ */
+
+ g.select('rect.nv-measure')
+ .style('fill', color)
+ .attr('height', availableHeight / 3)
+ .attr('y', availableHeight / 3)
+ .attr('width', measurez < 0 ?
+ x1(0) - x1(measurez[0])
+ : x1(measurez[0]) - x1(0))
+ .attr('x', xp1(measurez))
+ .on('mouseover', function() {
+ dispatch.elementMouseover({
+ value: measurez[0],
+ label: 'Current',
+ pos: [x1(measurez[0]), availableHeight/2]
+ })
+ })
+ .on('mouseout', function() {
+ dispatch.elementMouseout({
+ value: measurez[0],
+ label: 'Current'
+ })
+ })
+
+ var h3 = availableHeight / 6;
+ if (markerz[0]) {
+ g.selectAll('path.nv-markerTriangle')
+ .attr('transform', function(d) { return 'translate(' + x1(markerz[0]) + ',' + (availableHeight / 2) + ')' })
+ .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
+ .on('mouseover', function() {
+ dispatch.elementMouseover({
+ value: markerz[0],
+ label: 'Previous',
+ pos: [x1(markerz[0]), availableHeight/2]
+ })
+ })
+ .on('mouseout', function() {
+ dispatch.elementMouseout({
+ value: markerz[0],
+ label: 'Previous'
+ })
+ });
+ } else {
+ g.selectAll('path.nv-markerTriangle').remove();
+ }
+
+
+ wrap.selectAll('.nv-range')
+ .on('mouseover', function(d,i) {
+ var label = !i ? "Maximum" : i == 1 ? "Mean" : "Minimum";
+
+ dispatch.elementMouseover({
+ value: d,
+ label: label,
+ pos: [x1(d), availableHeight/2]
+ })
+ })
+ .on('mouseout', function(d,i) {
+ var label = !i ? "Maximum" : i == 1 ? "Mean" : "Minimum";
+ dispatch.elementMouseout({
+ value: d,
+ label: label
+ })
+ })
+/* // THIS IS THE PREVIOUS BULLET IMPLEMENTATION, WILL REMOVE SHORTLY
// Update the range rects.
var range = g.selectAll('rect.nv-range')
.data(rangez);
@@ -1069,6 +1270,7 @@ nv.models.bullet = function() {
measure.enter().append('rect')
.attr('class', function(d, i) { return 'nv-measure nv-s' + i; })
+ .style('fill', function(d,i) { return color(d,i ) })
.attr('width', w0)
.attr('height', availableHeight / 3)
.attr('x', reverse ? x0 : 0)
@@ -1119,13 +1321,14 @@ nv.models.bullet = function() {
});
d3.transition(marker)
- .attr('transform', function(d) { return 'translate(' + x1(d) + ',' + (availableHeight / 2) + ')' });
+ .attr('transform', function(d) { return 'translate(' + (x1(d) - x1(0)) + ',' + (availableHeight / 2) + ')' });
marker.exit().remove();
+*/
});
- d3.timer.flush();
+ // d3.timer.flush(); // Not needed?
return chart;
}
@@ -1199,6 +1402,12 @@ nv.models.bullet = function() {
return chart;
};
+ chart.color = function(_) {
+ if (!arguments.length) return color;
+ color = nv.utils.getColor(_);
+ return chart;
+ };
+
//============================================================
@@ -1271,9 +1480,7 @@ nv.models.bulletChart = function() {
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
- /*
- // Disabled until I figure out a better way to check for no data with the bullet chart
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ if (!d || !ranges.call(this, d, i)) {
var noDataText = container.selectAll('.nv-noData').data([noData]);
noDataText.enter().append('text')
@@ -1283,14 +1490,13 @@ nv.models.bulletChart = function() {
noDataText
.attr('x', margin.left + availableWidth / 2)
- .attr('y', margin.top + availableHeight / 2)
+ .attr('y', 18 + margin.top + availableHeight / 2)
.text(function(d) { return d });
return chart;
} else {
container.selectAll('.nv-noData').remove();
}
- */
//------------------------------------------------------------
@@ -1382,7 +1588,7 @@ nv.models.bulletChart = function() {
// Update the tick groups.
var tick = g.selectAll('g.nv-tick')
- .data(x1.ticks( availableWidth / 100 ), function(d) {
+ .data(x1.ticks( availableWidth / 50 ), function(d) {
return this.textContent || format(d);
});
@@ -1467,6 +1673,8 @@ nv.models.bulletChart = function() {
chart.dispatch = dispatch;
chart.bullet = bullet;
+ d3.rebind(chart, bullet, 'color');
+
// left, right, top, bottom
chart.orient = function(x) {
if (!arguments.length) return orient;
@@ -1577,13 +1785,15 @@ nv.models.cumulativeLineChart = function() {
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, id = lines.id()
+ , state = { index: 0, rescaleY: rescaleY }
+ , defaultState = null
, noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
;
xAxis
.orient('bottom')
- .tickPadding(5)
+ .tickPadding(7)
;
yAxis
.orient('left')
@@ -1610,24 +1820,32 @@ nv.models.cumulativeLineChart = function() {
nv.tooltip.show([left, top], content, null, null, offsetElement);
};
-
+/*
+ //Moved to see if we can get better behavior to fix issue #315
var indexDrag = d3.behavior.drag()
.on('dragstart', dragStart)
.on('drag', dragMove)
.on('dragend', dragEnd);
- function dragStart(d,i) {}
+ function dragStart(d,i) {
+ d3.select(chart.container)
+ .style('cursor', 'ew-resize');
+ }
function dragMove(d,i) {
d.x += d3.event.dx;
d.i = Math.round(dx.invert(d.x));
d3.select(this).attr('transform', 'translate(' + dx(d.i) + ',0)');
+ chart.update();
}
function dragEnd(d,i) {
+ d3.select(chart.container)
+ .style('cursor', 'auto');
chart.update();
}
+*/
//============================================================
@@ -1646,6 +1864,48 @@ nv.models.cumulativeLineChart = function() {
chart.update = function() { chart(selection) };
chart.container = this;
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ var indexDrag = d3.behavior.drag()
+ .on('dragstart', dragStart)
+ .on('drag', dragMove)
+ .on('dragend', dragEnd);
+
+
+ function dragStart(d,i) {
+ d3.select(chart.container)
+ .style('cursor', 'ew-resize');
+ }
+
+ function dragMove(d,i) {
+ index.x = d3.event.x;
+ index.i = Math.round(dx.invert(index.x));
+ updateZero();
+ }
+
+ function dragEnd(d,i) {
+ d3.select(chart.container)
+ .style('cursor', 'auto');
+
+ // update state and send stateChange with new index
+ state.index = index.i;
+ dispatch.stateChange(state);
+ }
+
+
+
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
@@ -1684,6 +1944,9 @@ nv.models.cumulativeLineChart = function() {
.map(function(series,i) {
var initialDomain = d3.extent(series.values, lines.y());
+ //account for series being disabled when losing 95% or more
+ if (initialDomain[0] < -.95) initialDomain[0] = -.95;
+
return [
(initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]),
(initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0])
@@ -1720,6 +1983,7 @@ nv.models.cumulativeLineChart = function() {
gEnter.append('g').attr('class', 'nv-x nv-axis');
gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-background');
gEnter.append('g').attr('class', 'nv-linesWrap');
gEnter.append('g').attr('class', 'nv-legendWrap');
gEnter.append('g').attr('class', 'nv-controlsWrap');
@@ -1771,9 +2035,30 @@ nv.models.cumulativeLineChart = function() {
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ // Show error if series goes below 100%
+ var tempDisabled = data.filter(function(d) { return d.tempDisabled });
+
+ wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates
+ if (tempDisabled.length) {
+ wrap.append('text').attr('class', 'tempDisabled')
+ .attr('x', availableWidth / 2)
+ .attr('y', '-.71em')
+ .style('text-anchor', 'end')
+ .text(tempDisabled.map(function(d) { return d.key }).join(', ') + ' values cannot be calculated for this time period.');
+ }
+
+
+
//------------------------------------------------------------
// Main Chart Component(s)
+ gEnter.select('.nv-background')
+ .append('rect');
+
+ g.select('.nv-background rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
+
lines
//.x(function(d) { return d.x })
.y(function(d) { return d.display.y })
@@ -1781,14 +2066,15 @@ nv.models.cumulativeLineChart = function() {
.height(availableHeight)
.color(data.map(function(d,i) {
return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled }));
+ }).filter(function(d,i) { return !data[i].disabled && !data[i].tempDisabled }));
var linesWrap = g.select('.nv-linesWrap')
- .datum(data.filter(function(d) { return !d.disabled }))
+ .datum(data.filter(function(d) { return !d.disabled && !d.tempDisabled }));
- d3.transition(linesWrap).call(lines);
+ //d3.transition(linesWrap).call(lines);
+ linesWrap.call(lines);
var indexLine = linesWrap.selectAll('.nv-indexLine')
@@ -1812,7 +2098,8 @@ nv.models.cumulativeLineChart = function() {
xAxis
.scale(x)
- .ticks( availableWidth / 100 )
+ //Suggest how many ticks based on the chart width and D3 should listen (70 is the optimal number for MM/DD/YY dates)
+ .ticks( Math.min(data[0].values.length,availableWidth/70) )
.tickSize(-availableHeight, 0);
g.select('.nv-x.nv-axis')
@@ -1836,11 +2123,46 @@ nv.models.cumulativeLineChart = function() {
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
+
+ function updateZero() {
+ indexLine
+ .data([index]);
+
+ chart.update();
+ }
+
+ g.select('.nv-background rect')
+ .on('click', function() {
+ index.x = d3.mouse(this)[0];
+ index.i = Math.round(dx.invert(index.x));
+
+ // update state and send stateChange with new index
+ state.index = index.i;
+ dispatch.stateChange(state);
+
+ updateZero();
+ });
+
+ lines.dispatch.on('elementClick', function(e) {
+ index.i = e.pointIndex;
+ index.x = dx(index.i);
+
+ // update state and send stateChange with new index
+ state.index = index.i;
+ dispatch.stateChange(state);
+
+ updateZero();
+ });
+
controls.dispatch.on('legendClick', function(d,i) {
d.disabled = !d.disabled;
rescaleY = !d.disabled;
- selection.transition().call(chart);
+ state.rescaleY = rescaleY;
+ dispatch.stateChange(state);
+
+ //selection.transition().call(chart);
+ selection.call(chart);
});
@@ -1855,7 +2177,11 @@ nv.models.cumulativeLineChart = function() {
});
}
- selection.transition().call(chart);
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
+ //selection.transition().call(chart);
+ selection.call(chart);
});
/*
@@ -1875,6 +2201,37 @@ nv.models.cumulativeLineChart = function() {
if (tooltips) showTooltip(e, that.parentNode);
});
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+
+ if (typeof e.index !== 'undefined') {
+ index.i = e.index;
+ index.x = dx(index.i);
+
+ state.index = e.index;
+
+ indexLine
+ .data([index]);
+ }
+
+
+ if (typeof e.rescaleY !== 'undefined') {
+ rescaleY = e.rescaleY;
+ }
+
+ selection.call(chart);
+ });
+
//============================================================
});
@@ -1944,6 +2301,12 @@ nv.models.cumulativeLineChart = function() {
return chart;
};
+ chart.rescaleY = function(_) {
+ if (!arguments.length) return rescaleY;
+ rescaleY = _
+ return rescaleY;
+ };
+
chart.showControls = function(_) {
if (!arguments.length) return showControls;
showControls = _;
@@ -1968,6 +2331,18 @@ nv.models.cumulativeLineChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -1986,16 +2361,20 @@ nv.models.cumulativeLineChart = function() {
return data.map(function(line, i) {
var v = lines.y()(line.values[idx], idx);
+ //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
+ if (v < -.95) {
+ //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100)
+ line.tempDisabled = true;
+ return line;
+ }
+
+ line.tempDisabled = false;
+
line.values = line.values.map(function(point, pointIndex) {
point.display = {'y': (lines.y()(point, pointIndex) - v) / (1 + v) };
return point;
})
- /*
- TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
- if (v < -.9) {
- //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically currect till it hits 100)
- }
- */
+
return line;
})
}
@@ -2027,6 +2406,7 @@ nv.models.discreteBar = function() {
, xDomain
, yDomain
, dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
+ , rectClass = 'discreteBar'
;
//============================================================
@@ -2128,7 +2508,7 @@ nv.models.discreteBar = function() {
var barsEnter = bars.enter().append('g')
.attr('transform', function(d,i,j) {
- return 'translate(' + x(getX(d,i)) + ', ' + y(0) + ')'
+ return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05 ) + ', ' + y(0) + ')'
})
.on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
d3.select(this).classed('hover', true);
@@ -2180,13 +2560,13 @@ nv.models.discreteBar = function() {
barsEnter.append('rect')
.attr('height', 0)
- .attr('width', x.rangeBand() / data.length )
+ .attr('width', x.rangeBand() * .9 / data.length )
if (showValues) {
barsEnter.append('text')
.attr('text-anchor', 'middle')
bars.select('text')
- .attr('x', x.rangeBand() / 2)
+ .attr('x', x.rangeBand() * .9 / 2)
.attr('y', function(d,i) { return getY(d,i) < 0 ? y(getY(d,i)) - y(0) + 12 : -4 })
.text(function(d,i) { return valueFormat(getY(d,i)) });
} else {
@@ -2198,16 +2578,24 @@ nv.models.discreteBar = function() {
.style('fill', function(d,i) { return d.color || color(d,i) })
.style('stroke', function(d,i) { return d.color || color(d,i) })
.select('rect')
- .attr('width', x.rangeBand() / data.length);
+ .attr('class', rectClass)
+ .attr('width', x.rangeBand() * .9 / data.length);
d3.transition(bars)
//.delay(function(d,i) { return i * 1200 / data[0].values.length })
- .attr('transform', function(d,i) {
- return 'translate(' + x(getX(d,i)) + ', ' + (getY(d,i) < 0 ? y(0) : y(getY(d,i))) + ')'
+ .attr('transform', function(d,i) {
+ var left = x(getX(d,i)) + x.rangeBand() * .05,
+ top = getY(d,i) < 0 ?
+ y(0) :
+ y(0) - y(getY(d,i)) < 1 ?
+ y(0) - 1 : //make 1 px positive bars show up above y=0
+ y(getY(d,i));
+
+ return 'translate(' + left + ', ' + top + ')'
})
- .select('rect')
- .attr('height', function(d,i) {
- return Math.abs(y(getY(d,i)) - y(0))
- });
+ .select('rect')
+ .attr('height', function(d,i) {
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)) || 1)
+ });
//store old scales for use in transitions on update
@@ -2313,6 +2701,11 @@ nv.models.discreteBar = function() {
return chart;
};
+ chart.rectClass= function(_) {
+ if (!arguments.length) return rectClass;
+ rectClass = _;
+ return chart;
+ }
//============================================================
@@ -2343,7 +2736,7 @@ nv.models.discreteBarChart = function() {
, x
, y
, noData = "No Data Available."
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'beforeUpdate')
;
xAxis
@@ -2388,7 +2781,7 @@ nv.models.discreteBarChart = function() {
- margin.top - margin.bottom;
- chart.update = function() { selection.transition().call(chart); };
+ chart.update = function() { dispatch.beforeUpdate(); selection.transition().call(chart); };
chart.container = this;
@@ -2486,10 +2879,11 @@ nv.models.discreteBarChart = function() {
var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
- if (staggerLabels)
+ if (staggerLabels) {
xTicks
.selectAll('text')
- .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '0' : '12') + ')' })
+ .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' })
+ }
yAxis
.scale(y)
@@ -2753,7 +3147,6 @@ nv.models.distribution = function() {
return chart;
}
-
nv.models.indentedTree = function() {
//============================================================
@@ -2766,6 +3159,7 @@ nv.models.indentedTree = function() {
, color = nv.utils.defaultColor()
, id = Math.floor(Math.random() * 10000)
, header = true
+ , filterZero = false
, noData = "No Data Available."
, childIndent = 20
, columns = [{key:'key', label: 'Name', type:'text'}] //TODO: consider functions like chart.addColumn, chart.removeColumn, instead of a block like this
@@ -2777,30 +3171,32 @@ nv.models.indentedTree = function() {
//============================================================
+ var idx = 0;
function chart(selection) {
selection.each(function(data) {
- var i = 0,
- depth = 1;
+ var depth = 1,
+ container = d3.select(this);
var tree = d3.layout.tree()
.children(function(d) { return d.values })
.size([height, childIndent]); //Not sure if this is needed now that the result is HTML
- chart.update = function() { selection.transition().call(chart) };
- chart.container = this;
+ chart.update = function() { container.transition().duration(600).call(chart) };
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
-
- if (!data[0].key) data[0].key = noData;
+ if (!data[0]) data[0] = {key: noData};
//------------------------------------------------------------
var nodes = tree.nodes(data[0]);
+ // nodes.map(function(d) {
+ // d.id = i++;
+ // })
//------------------------------------------------------------
// Setup containers and skeleton of chart
@@ -2830,7 +3226,7 @@ nv.models.indentedTree = function() {
var tbody = table.selectAll('tbody')
- .data(function(d) {return d });
+ .data(function(d) { return d });
tbody.enter().append('tbody');
@@ -2842,12 +3238,12 @@ nv.models.indentedTree = function() {
// Update the nodes…
var node = tbody.selectAll('tr')
- .data(function(d) { return d }, function(d) { return d.id || (d.id == ++i)});
+ // .data(function(d) { return d; }, function(d) { return d.id || (d.id == ++i)});
+ .data(function(d) { return d.filter(function(d) { return (filterZero && !d.children) ? filterZero(d) : true; } )}, function(d,i) { return d.id || (d.id || ++idx)});
//.style('display', 'table-row'); //TODO: see if this does anything
node.exit().remove();
-
node.select('img.nv-treeicon')
.attr('src', icon)
.classed('folded', folded);
@@ -2880,22 +3276,24 @@ nv.models.indentedTree = function() {
.text(function(d) { return column.format ? column.format(d) :
(d[column.key] || '-') });
- if (column.showCount)
+ if (column.showCount) {
nodeName.append('span')
- .attr('class', 'nv-childrenCount')
- .text(function(d) {
- return ((d.values && d.values.length) || (d._values && d._values.length)) ?
- '(' + ((d.values && d.values.length) || (d._values && d._values.length)) + ')'
- : ''
- });
+ .attr('class', 'nv-childrenCount');
+ node.selectAll('span.nv-childrenCount').text(function(d) {
+ return ((d.values && d.values.length) || (d._values && d._values.length)) ? //If this is a parent
+ '(' + ((d.values && (d.values.filter(function(d) { return filterZero ? filterZero(d) : true; }).length)) //If children are in values check its children and filter
+ || (d._values && d._values.filter(function(d) { return filterZero ? filterZero(d) : true; }).length) //Otherwise, do the same, but with the other name, _values...
+ || 0) + ')' //This is the catch-all in case there are no children after a filter
+ : '' //If this is not a parent, just give an empty string
+ });
+ }
if (column.click)
nodeName.select('span').on('click', column.click);
});
-
node
.order()
.on('click', function(d) {
@@ -3031,6 +3429,12 @@ nv.models.indentedTree = function() {
return chart;
};
+ chart.filterZero = function(_) {
+ if (!arguments.length) return filterZero;
+ filterZero = _;
+ return chart;
+ };
+
chart.columns = function(_) {
if (!arguments.length) return columns;
columns = _;
@@ -3059,8 +3463,7 @@ nv.models.indentedTree = function() {
return chart;
-}
-nv.models.legend = function() {
+};nv.models.legend = function() {
//============================================================
// Public Variables with Default Settings
@@ -3270,24 +3673,24 @@ nv.models.line = function() {
// Public Variables with Default Settings
//------------------------------------------------------------
+ var scatter = nv.models.scatter()
+ ;
+
var margin = {top: 0, right: 0, bottom: 0, left: 0}
, width = 960
, height = 500
, color = nv.utils.defaultColor() // a function that returns a color
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID incase user doesn't select one
, getX = function(d) { return d.x } // accessor to get the x value from a data point
, getY = function(d) { return d.y } // accessor to get the y value from a data point
- , defined = function(d,i) { return !isNaN(getY(d,i)) && getY(d,i) !== null } // allows a line to be not continous when it is not defined
+ , defined = function(d,i) { return !isNaN(getY(d,i)) && getY(d,i) !== null } // allows a line to be not continuous when it is not defined
, isArea = function(d) { return d.area } // decides if a line is an area or just a line
, clipEdge = false // if true, masks lines within x and y scale
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, interpolate = "linear" // controls the line interpolation
- , scatter = nv.models.scatter()
;
scatter
- .id(id)
.size(16) // default size
.sizeDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor
;
@@ -3354,16 +3757,16 @@ nv.models.line = function() {
defsEnter.append('clipPath')
- .attr('id', 'nv-edge-clip-' + id)
+ .attr('id', 'nv-edge-clip-' + scatter.id())
.append('rect');
- wrap.select('#nv-edge-clip-' + id + ' rect')
+ wrap.select('#nv-edge-clip-' + scatter.id() + ' rect')
.attr('width', availableWidth)
.attr('height', availableHeight);
- g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
+ g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
scatterWrap
- .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
+ .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
@@ -3474,7 +3877,7 @@ nv.models.line = function() {
chart.dispatch = scatter.dispatch;
chart.scatter = scatter;
- d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius');
+ d3.rebind(chart, scatter, 'id', 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'padData');
chart.margin = function(_) {
if (!arguments.length) return margin;
@@ -3524,12 +3927,6 @@ nv.models.line = function() {
return chart;
};
- chart.id = function(_) {
- if (!arguments.length) return id;
- id = _;
- return chart;
- };
-
chart.interpolate = function(_) {
if (!arguments.length) return interpolate;
interpolate = _;
@@ -3566,6 +3963,7 @@ nv.models.lineChart = function() {
, legend = nv.models.legend()
;
+//set margin.right to 23 to fit dates on the x-axis within the chart
var margin = {top: 30, right: 20, bottom: 50, left: 60}
, color = nv.utils.defaultColor()
, width = null
@@ -3578,13 +3976,15 @@ nv.models.lineChart = function() {
}
, x
, y
+ , state = {}
+ , defaultState = null
, noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
;
xAxis
.orient('bottom')
- .tickPadding(5)
+ .tickPadding(7)
;
yAxis
.orient('left')
@@ -3637,6 +4037,19 @@ nv.models.lineChart = function() {
chart.update = function() { chart(selection) };
chart.container = this;
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
//------------------------------------------------------------
// Display noData message if there's nothing to show.
@@ -3770,6 +4183,9 @@ nv.models.lineChart = function() {
});
}
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
selection.transition().call(chart);
});
@@ -3789,6 +4205,20 @@ nv.models.lineChart = function() {
if (tooltips) showTooltip(e, that.parentNode);
});
+
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ selection.call(chart);
+ });
+
//============================================================
});
@@ -3876,6 +4306,18 @@ nv.models.lineChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -3917,16 +4359,23 @@ nv.models.linePlusBarChart = function() {
, x
, y1
, y2
+ , state = {}
+ , defaultState = null
, noData = "No Data Available."
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
;
+ bars
+ .padData(true)
+ ;
lines
.clipEdge(false)
+ .padData(true)
;
xAxis
.orient('bottom')
- .tickPadding(5)
+ .tickPadding(7)
+ .highlightZero(false)
;
y1Axis
.orient('left')
@@ -3943,14 +4392,15 @@ nv.models.linePlusBarChart = function() {
//------------------------------------------------------------
var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
- y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
+ y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
- nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
- };
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
+ }
+ ;
//------------------------------------------------------------
@@ -3969,6 +4419,19 @@ nv.models.linePlusBarChart = function() {
chart.update = function() { chart(selection) };
chart.container = this;
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
@@ -3997,51 +4460,19 @@ nv.models.linePlusBarChart = function() {
//------------------------------------------------------------
// Setup Scales
- x = xAxis.scale();
- y1 = bars.yScale();
- y2 = lines.yScale();
-
var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
+ //x = xAxis.scale();
+ x = dataLines.filter(function(d) { return !d.disabled; }).length && dataLines.filter(function(d) { return !d.disabled; })[0].values.length ? lines.xScale() : bars.xScale();
+ //x = dataLines.filter(function(d) { return !d.disabled; }).length ? lines.xScale() : bars.xScale(); //old code before change above
+ y1 = bars.yScale();
+ y2 = lines.yScale();
- //TODO: try to remove x scale computation from this layer
-
- var series1 = data.filter(function(d) { return !d.disabled && d.bar })
- .map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i) }
- })
- });
+ //------------------------------------------------------------
- var series2 = data.filter(function(d) { return !d.disabled && !d.bar })
- .map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i) }
- })
- });
-
- x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
- .range([0, availableWidth]);
-
-
-
- /*
- x .domain(d3.extent(d3.merge(data.map(function(d) { return d.values })), getX ))
- .range([0, availableWidth]);
-
- y1 .domain(d3.extent(d3.merge(dataBars), function(d) { return d.y } ))
- .range([availableHeight, 0]);
-
- y2 .domain(d3.extent(d3.merge(dataLines), function(d) { return d.y } ))
- .range([availableHeight, 0]);
- */
-
- //------------------------------------------------------------
-
-
- //------------------------------------------------------------
- // Setup containers and skeleton of chart
+ //------------------------------------------------------------
+ // Setup containers and skeleton of chart
var wrap = d3.select(this).selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
@@ -4111,7 +4542,7 @@ nv.models.linePlusBarChart = function() {
.datum(dataBars.length ? dataBars : [{values:[]}])
var linesWrap = g.select('.nv-linesWrap')
- .datum(!dataLines[0].disabled ? dataLines : [{values:[]}] );
+ .datum(dataLines[0] && !dataLines[0].disabled ? dataLines : [{values:[]}] );
//.datum(!dataLines[0].disabled ? dataLines : [{values:dataLines[0].values.map(function(d) { return [d[0], null] }) }] );
d3.transition(barsWrap).call(bars);
@@ -4124,6 +4555,7 @@ nv.models.linePlusBarChart = function() {
// Setup Axes
xAxis
+ .scale(x)
.ticks( availableWidth / 100 )
.tickSize(-availableHeight, 0);
@@ -4150,7 +4582,8 @@ nv.models.linePlusBarChart = function() {
g.select('.nv-y2.nv-axis')
.style('opacity', dataLines.length ? 1 : 0)
- .attr('transform', 'translate(' + x.range()[1] + ',0)');
+ .attr('transform', 'translate(' + availableWidth + ',0)');
+ //.attr('transform', 'translate(' + x.range()[1] + ',0)');
d3.transition(g.select('.nv-y2.nv-axis'))
.call(y2Axis);
@@ -4173,6 +4606,9 @@ nv.models.linePlusBarChart = function() {
});
}
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
selection.transition().call(chart);
});
@@ -4180,6 +4616,21 @@ nv.models.linePlusBarChart = function() {
if (tooltips) showTooltip(e, that.parentNode);
});
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ selection.call(chart);
+ });
+
//============================================================
@@ -4297,6 +4748,18 @@ nv.models.linePlusBarChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -4527,33 +4990,614 @@ nv.models.lineWithFocusChart = function() {
//------------------------------------------------------------
- /*
- var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
- .datum(data.filter(function(d) { return !d.disabled }))
+ /*
+ var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
+ .datum(data.filter(function(d) { return !d.disabled }))
+
+ d3.transition(focusLinesWrap).call(lines);
+ */
+
+
+ //------------------------------------------------------------
+ // Setup Main (Focus) Axes
+
+ xAxis
+ .scale(x)
+ .ticks( availableWidth / 100 )
+ .tickSize(-availableHeight1, 0);
+
+ yAxis
+ .scale(y)
+ .ticks( availableHeight1 / 36 )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-focus .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + availableHeight1 + ')');
+
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Setup Brush
+
+ brush
+ .x(x2)
+ .on('brush', onBrush);
+
+ if (brushExtent) brush.extent(brushExtent);
+
+ var brushBG = g.select('.nv-brushBackground').selectAll('g')
+ .data([brushExtent || brush.extent()])
+
+ var brushBGenter = brushBG.enter()
+ .append('g');
+
+ brushBGenter.append('rect')
+ .attr('class', 'left')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight2);
+
+ brushBGenter.append('rect')
+ .attr('class', 'right')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight2);
+
+ gBrush = g.select('.nv-x.nv-brush')
+ .call(brush);
+ gBrush.selectAll('rect')
+ //.attr('y', -5)
+ .attr('height', availableHeight2);
+ gBrush.selectAll('.resize').append('path').attr('d', resizePath);
+
+ onBrush();
+
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Setup Secondary (Context) Axes
+
+ x2Axis
+ .scale(x2)
+ .ticks( availableWidth / 100 )
+ .tickSize(-availableHeight2, 0);
+
+ g.select('.nv-context .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y2.range()[0] + ')');
+ d3.transition(g.select('.nv-context .nv-x.nv-axis'))
+ .call(x2Axis);
+
+
+ y2Axis
+ .scale(y2)
+ .ticks( availableHeight2 / 36 )
+ .tickSize( -availableWidth, 0);
+
+ d3.transition(g.select('.nv-context .nv-y.nv-axis'))
+ .call(y2Axis);
+
+ g.select('.nv-context .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y2.range()[0] + ')');
+
+ //------------------------------------------------------------
+
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ legend.dispatch.on('legendClick', function(d,i) {
+ d.disabled = !d.disabled;
+
+ if (!data.filter(function(d) { return !d.disabled }).length) {
+ data.map(function(d) {
+ d.disabled = false;
+ wrap.selectAll('.nv-series').classed('disabled', false);
+ return d;
+ });
+ }
+
+ selection.transition().call(chart);
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ //============================================================
+
+
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
+
+ // Taken from crossfilter (http://square.github.com/crossfilter/)
+ function resizePath(d) {
+ var e = +(d == 'e'),
+ x = e ? 1 : -1,
+ y = availableHeight2 / 3;
+ return 'M' + (.5 * x) + ',' + y
+ + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
+ + 'V' + (2 * y - 6)
+ + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
+ + 'Z'
+ + 'M' + (2.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8)
+ + 'M' + (4.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8);
+ }
+
+
+ function updateBrushBG() {
+ if (!brush.empty()) brush.extent(brushExtent);
+ brushBG
+ .data([brush.empty() ? x2.domain() : brushExtent])
+ .each(function(d,i) {
+ var leftWidth = x2(d[0]) - x.range()[0],
+ rightWidth = x.range()[1] - x2(d[1]);
+ d3.select(this).select('.left')
+ .attr('width', leftWidth < 0 ? 0 : leftWidth);
+
+ d3.select(this).select('.right')
+ .attr('x', x2(d[1]))
+ .attr('width', rightWidth < 0 ? 0 : rightWidth);
+ });
+ }
+
+
+ function onBrush() {
+ brushExtent = brush.empty() ? null : brush.extent();
+ extent = brush.empty() ? x2.domain() : brush.extent();
+
+
+ dispatch.brush({extent: extent, brush: brush});
+
+
+ updateBrushBG();
+
+ // Update Main (Focus)
+ var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
+ .datum(
+ data
+ .filter(function(d) { return !d.disabled })
+ .map(function(d,i) {
+ return {
+ key: d.key,
+ values: d.values.filter(function(d,i) {
+ return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
+ })
+ }
+ })
+ );
+ d3.transition(focusLinesWrap).call(lines);
+
+
+ // Update Main (Focus) Axes
+ d3.transition(g.select('.nv-focus .nv-x.nv-axis'))
+ .call(xAxis);
+ d3.transition(g.select('.nv-focus .nv-y.nv-axis'))
+ .call(yAxis);
+ }
+
+ //============================================================
+
+
+ });
+
+ return chart;
+ }
+
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ lines.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.legend = legend;
+ chart.lines = lines;
+ chart.lines2 = lines2;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.x2Axis = x2Axis;
+ chart.y2Axis = y2Axis;
+
+ d3.rebind(chart, lines, 'defined', 'isArea', 'size', 'xDomain', 'yDomain', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
+
+ chart.x = function(_) {
+ if (!arguments.length) return lines.x;
+ lines.x(_);
+ lines2.x(_);
+ return chart;
+ };
+
+ chart.y = function(_) {
+ if (!arguments.length) return lines.y;
+ lines.y(_);
+ lines2.y(_);
+ return chart;
+ };
+
+ chart.margin = function(_) {
+ if (!arguments.length) return margin;
+ margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
+ margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
+ margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
+ margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
+ return chart;
+ };
+
+ chart.margin2 = function(_) {
+ if (!arguments.length) return margin2;
+ margin2 = _;
+ return chart;
+ };
+
+ chart.width = function(_) {
+ if (!arguments.length) return width;
+ width = _;
+ return chart;
+ };
+
+ chart.height = function(_) {
+ if (!arguments.length) return height;
+ height = _;
+ return chart;
+ };
+
+ chart.height2 = function(_) {
+ if (!arguments.length) return height2;
+ height2 = _;
+ return chart;
+ };
+
+ chart.color = function(_) {
+ if (!arguments.length) return color;
+ color =nv.utils.getColor(_);
+ legend.color(color);
+ return chart;
+ };
+
+ chart.showLegend = function(_) {
+ if (!arguments.length) return showLegend;
+ showLegend = _;
+ return chart;
+ };
+
+ chart.tooltips = function(_) {
+ if (!arguments.length) return tooltips;
+ tooltips = _;
+ return chart;
+ };
+
+ chart.tooltipContent = function(_) {
+ if (!arguments.length) return tooltip;
+ tooltip = _;
+ return chart;
+ };
+
+ chart.interpolate = function(_) {
+ if (!arguments.length) return lines.interpolate();
+ lines.interpolate(_);
+ lines2.interpolate(_);
+ return chart;
+ };
+
+ chart.noData = function(_) {
+ if (!arguments.length) return noData;
+ noData = _;
+ return chart;
+ };
+
+ // Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below
+ chart.xTickFormat = function(_) {
+ if (!arguments.length) return xAxis.tickFormat();
+ xAxis.tickFormat(_);
+ x2Axis.tickFormat(_);
+ return chart;
+ };
+
+ chart.yTickFormat = function(_) {
+ if (!arguments.length) return yAxis.tickFormat();
+ yAxis.tickFormat(_);
+ y2Axis.tickFormat(_);
+ return chart;
+ };
+
+ //============================================================
+
+
+ return chart;
+}
+
+nv.models.linePlusBarWithFocusChart = function() {
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var lines = nv.models.line()
+ , lines2 = nv.models.line()
+ , bars = nv.models.historicalBar()
+ , bars2 = nv.models.historicalBar()
+ , xAxis = nv.models.axis()
+ , x2Axis = nv.models.axis()
+ , y1Axis = nv.models.axis()
+ , y2Axis = nv.models.axis()
+ , y3Axis = nv.models.axis()
+ , y4Axis = nv.models.axis()
+ , legend = nv.models.legend()
+ , brush = d3.svg.brush()
+ ;
+
+ var margin = {top: 30, right: 30, bottom: 30, left: 60}
+ , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
+ , width = null
+ , height = null
+ , height2 = 100
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , color = nv.utils.defaultColor()
+ , showLegend = true
+ , extent
+ , brushExtent = null
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '<h3>' + key + '</h3>' +
+ '<p>' + y + ' at ' + x + '</p>';
+ }
+ , x
+ , x2
+ , y1
+ , y2
+ , y3
+ , y4
+ , noData = "No Data Available."
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush')
+ ;
+
+ lines
+ .clipEdge(true)
+ ;
+ lines2
+ .interactive(false)
+ ;
+ xAxis
+ .orient('bottom')
+ .tickPadding(5)
+ ;
+ y1Axis
+ .orient('left')
+ ;
+ y2Axis
+ .orient('right')
+ ;
+ x2Axis
+ .orient('bottom')
+ .tickPadding(5)
+ ;
+ y3Axis
+ .orient('left')
+ ;
+ y4Axis
+ .orient('right')
+ ;
+
+ //============================================================
+
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+ if (extent) {
+ e.pointIndex += Math.ceil(extent[0]);
+ }
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
+ y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
+ };
+
+ //------------------------------------------------------------
+
+
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight1 = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom - height2,
+ availableHeight2 = height2 - margin2.top - margin2.bottom;
+
+ chart.update = function() { chart(selection) };
+ chart.container = this;
+
+
+ //------------------------------------------------------------
+ // Display No Data message if there's nothing to show.
+
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight1 / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Setup Scales
+
+ var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
+ var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
+
+ x = bars.xScale();
+ x2 = x2Axis.scale();
+ y1 = bars.yScale();
+ y2 = lines.yScale();
+ y3 = bars2.yScale();
+ y4 = lines2.yScale();
+
+ var series1 = data
+ .filter(function(d) { return !d.disabled && d.bar })
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i) }
+ })
+ });
+
+ var series2 = data
+ .filter(function(d) { return !d.disabled && !d.bar })
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i) }
+ })
+ });
+
+ x .range([0, availableWidth]);
+
+ x2 .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
+ .range([0, availableWidth]);
+
+
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Setup containers and skeleton of chart
+
+ var wrap = container.selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+
+ var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
+ focusEnter.append('g').attr('class', 'nv-x nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y1 nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y2 nv-axis');
+ focusEnter.append('g').attr('class', 'nv-barsWrap');
+ focusEnter.append('g').attr('class', 'nv-linesWrap');
+
+ var contextEnter = gEnter.append('g').attr('class', 'nv-context');
+ contextEnter.append('g').attr('class', 'nv-x nv-axis');
+ contextEnter.append('g').attr('class', 'nv-y1 nv-axis');
+ contextEnter.append('g').attr('class', 'nv-y2 nv-axis');
+ contextEnter.append('g').attr('class', 'nv-barsWrap');
+ contextEnter.append('g').attr('class', 'nv-linesWrap');
+ contextEnter.append('g').attr('class', 'nv-brushBackground');
+ contextEnter.append('g').attr('class', 'nv-x nv-brush');
+
+
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Legend
+
+ if (showLegend) {
+ legend.width( availableWidth / 2 );
+
+ g.select('.nv-legendWrap')
+ .datum(data.map(function(series) {
+ series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
+ series.key = series.originalKey + (series.bar ? ' (left axis)' : ' (right axis)');
+ return series;
+ }))
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight1 = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom - height2;
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
+ }
+
+ //------------------------------------------------------------
+
- d3.transition(focusLinesWrap).call(lines);
- */
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
//------------------------------------------------------------
- // Setup Main (Focus) Axes
+ // Context Components
- xAxis
- .scale(x)
- .ticks( availableWidth / 100 )
- .tickSize(-availableHeight1, 0);
+ bars2
+ .width(availableWidth)
+ .height(availableHeight2)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
- yAxis
- .scale(y)
- .ticks( availableHeight1 / 36 )
- .tickSize( -availableWidth, 0);
+ lines2
+ .width(availableWidth)
+ .height(availableHeight2)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
+
+ var bars2Wrap = g.select('.nv-context .nv-barsWrap')
+ .datum(dataBars.length ? dataBars : [{values:[]}]);
+
+ var lines2Wrap = g.select('.nv-context .nv-linesWrap')
+ .datum(!dataLines[0].disabled ? dataLines : [{values:[]}]);
+
+ g.select('.nv-context')
+ .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')')
- g.select('.nv-focus .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + availableHeight1 + ')');
+ d3.transition(bars2Wrap).call(bars2);
+ d3.transition(lines2Wrap).call(lines2);
//------------------------------------------------------------
+
//------------------------------------------------------------
// Setup Brush
@@ -4581,51 +5625,60 @@ nv.models.lineWithFocusChart = function() {
.attr('y', 0)
.attr('height', availableHeight2);
- gBrush = g.select('.nv-x.nv-brush')
+ var gBrush = g.select('.nv-x.nv-brush')
.call(brush);
gBrush.selectAll('rect')
//.attr('y', -5)
.attr('height', availableHeight2);
gBrush.selectAll('.resize').append('path').attr('d', resizePath);
- onBrush();
-
//------------------------------------------------------------
-
//------------------------------------------------------------
// Setup Secondary (Context) Axes
x2Axis
- .scale(x2)
.ticks( availableWidth / 100 )
.tickSize(-availableHeight2, 0);
g.select('.nv-context .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y2.range()[0] + ')');
+ .attr('transform', 'translate(0,' + y3.range()[0] + ')');
d3.transition(g.select('.nv-context .nv-x.nv-axis'))
.call(x2Axis);
- y2Axis
- .scale(y2)
+ y3Axis
+ .scale(y3)
.ticks( availableHeight2 / 36 )
.tickSize( -availableWidth, 0);
- d3.transition(g.select('.nv-context .nv-y.nv-axis'))
- .call(y2Axis);
+ g.select('.nv-context .nv-y1.nv-axis')
+ .style('opacity', dataBars.length ? 1 : 0)
+ .attr('transform', 'translate(0,' + x2.range()[0] + ')');
+
+ d3.transition(g.select('.nv-context .nv-y1.nv-axis'))
+ .call(y3Axis);
+
+
+ y4Axis
+ .scale(y4)
+ .ticks( availableHeight2 / 36 )
+ .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
- g.select('.nv-context .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y2.range()[0] + ')');
+ g.select('.nv-context .nv-y2.nv-axis')
+ .style('opacity', dataLines.length ? 1 : 0)
+ .attr('transform', 'translate(' + x2.range()[1] + ',0)');
+ d3.transition(g.select('.nv-context .nv-y2.nv-axis'))
+ .call(y4Axis);
+
//------------------------------------------------------------
-
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
- legend.dispatch.on('legendClick', function(d,i) {
+ legend.dispatch.on('legendClick', function(d,i) {
d.disabled = !d.disabled;
if (!data.filter(function(d) { return !d.disabled }).length) {
@@ -4636,7 +5689,7 @@ nv.models.lineWithFocusChart = function() {
});
}
- selection.transition().call(chart);
+ selection.call(chart);
});
dispatch.on('tooltipShow', function(e) {
@@ -4672,8 +5725,8 @@ nv.models.lineWithFocusChart = function() {
brushBG
.data([brush.empty() ? x2.domain() : brushExtent])
.each(function(d,i) {
- var leftWidth = x2(d[0]) - x.range()[0],
- rightWidth = x.range()[1] - x2(d[1]);
+ var leftWidth = x2(d[0]) - x2.range()[0],
+ rightWidth = x2.range()[1] - x2(d[1]);
d3.select(this).select('.left')
.attr('width', leftWidth < 0 ? 0 : leftWidth);
@@ -4691,14 +5744,43 @@ nv.models.lineWithFocusChart = function() {
dispatch.brush({extent: extent, brush: brush});
-
updateBrushBG();
- // Update Main (Focus)
+
+ //------------------------------------------------------------
+ // Prepare Main (Focus) Bars and Lines
+
+ bars
+ .width(availableWidth)
+ .height(availableHeight1)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
+
+
+ lines
+ .width(availableWidth)
+ .height(availableHeight1)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
+
+ var focusBarsWrap = g.select('.nv-focus .nv-barsWrap')
+ .datum(!dataBars.length ? [{values:[]}] :
+ dataBars
+ .map(function(d,i) {
+ return {
+ key: d.key,
+ values: d.values.filter(function(d,i) {
+ return bars.x()(d,i) >= extent[0] && bars.x()(d,i) <= extent[1];
+ })
+ }
+ })
+ );
+
var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
- .datum(
- data
- .filter(function(d) { return !d.disabled })
+ .datum(dataLines[0].disabled ? [{values:[]}] :
+ dataLines
.map(function(d,i) {
return {
key: d.key,
@@ -4707,19 +5789,75 @@ nv.models.lineWithFocusChart = function() {
})
}
})
- );
+ );
+
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Update Main (Focus) X Axis
+
+ if (dataBars.length) {
+ x = bars.xScale();
+ } else {
+ x = lines.xScale();
+ }
+
+ xAxis
+ .scale(x)
+ .ticks( availableWidth / 100 )
+ .tickSize(-availableHeight1, 0);
+
+ xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]);
+
+ d3.transition(g.select('.nv-x.nv-axis'))
+ .call(xAxis);
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Update Main (Focus) Bars and Lines
+
+ d3.transition(focusBarsWrap).call(bars);
d3.transition(focusLinesWrap).call(lines);
+
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Setup and Update Main (Focus) Y Axes
+
+ g.select('.nv-focus .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y1.range()[0] + ')');
- // Update Main (Focus) Axes
- d3.transition(g.select('.nv-focus .nv-x.nv-axis'))
- .call(xAxis);
- d3.transition(g.select('.nv-focus .nv-y.nv-axis'))
- .call(yAxis);
+ y1Axis
+ .scale(y1)
+ .ticks( availableHeight1 / 36 )
+ .tickSize(-availableWidth, 0);
+
+ g.select('.nv-focus .nv-y1.nv-axis')
+ .style('opacity', dataBars.length ? 1 : 0);
+
+
+ y2Axis
+ .scale(y2)
+ .ticks( availableHeight1 / 36 )
+ .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
+
+ g.select('.nv-focus .nv-y2.nv-axis')
+ .style('opacity', dataLines.length ? 1 : 0)
+ .attr('transform', 'translate(' + x.range()[1] + ',0)');
+
+ d3.transition(g.select('.nv-focus .nv-y1.nv-axis'))
+ .call(y1Axis);
+ d3.transition(g.select('.nv-focus .nv-y2.nv-axis'))
+ .call(y2Axis);
}
//============================================================
+ onBrush();
});
@@ -4740,6 +5878,15 @@ nv.models.lineWithFocusChart = function() {
dispatch.tooltipHide(e);
});
+ bars.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ bars.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
dispatch.on('tooltipHide', function() {
if (tooltips) nv.tooltip.cleanup();
});
@@ -4756,24 +5903,32 @@ nv.models.lineWithFocusChart = function() {
chart.legend = legend;
chart.lines = lines;
chart.lines2 = lines2;
+ chart.bars = bars;
+ chart.bars2 = bars2;
chart.xAxis = xAxis;
- chart.yAxis = yAxis;
chart.x2Axis = x2Axis;
+ chart.y1Axis = y1Axis;
chart.y2Axis = y2Axis;
+ chart.y3Axis = y3Axis;
+ chart.y4Axis = y4Axis;
- d3.rebind(chart, lines, 'defined', 'isArea', 'size', 'xDomain', 'yDomain', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
+ d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate');
+ //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc.
+ //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
chart.x = function(_) {
- if (!arguments.length) return lines.x;
+ if (!arguments.length) return getX;
+ getX = _;
lines.x(_);
- lines2.x(_);
+ bars.x(_);
return chart;
};
chart.y = function(_) {
- if (!arguments.length) return lines.y;
+ if (!arguments.length) return getY;
+ getY = _;
lines.y(_);
- lines2.y(_);
+ bars.y(_);
return chart;
};
@@ -4786,12 +5941,6 @@ nv.models.lineWithFocusChart = function() {
return chart;
};
- chart.margin2 = function(_) {
- if (!arguments.length) return margin2;
- margin2 = _;
- return chart;
- };
-
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
@@ -4806,7 +5955,7 @@ nv.models.lineWithFocusChart = function() {
chart.color = function(_) {
if (!arguments.length) return color;
- color =nv.utils.getColor(_);
+ color = nv.utils.getColor(_);
legend.color(color);
return chart;
};
@@ -4829,33 +5978,18 @@ nv.models.lineWithFocusChart = function() {
return chart;
};
- chart.interpolate = function(_) {
- if (!arguments.length) return lines.interpolate();
- lines.interpolate(_);
- lines2.interpolate(_);
- return chart;
- };
-
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
return chart;
};
- // Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below
- chart.xTickFormat = function(_) {
- if (!arguments.length) return xAxis.tickFormat();
- xAxis.tickFormat(_);
- x2Axis.tickFormat(_);
+ chart.brushExtent = function(_) {
+ if (!arguments.length) return brushExtent;
+ brushExtent = _;
return chart;
};
- chart.yTickFormat = function(_) {
- if (!arguments.length) return yAxis.tickFormat();
- yAxis.tickFormat(_);
- y2Axis.tickFormat(_);
- return chart;
- };
//============================================================
@@ -4881,6 +6015,9 @@ nv.models.multiBar = function() {
, clipEdge = true
, stacked = false
, color = nv.utils.defaultColor()
+ , hideable = false
+ , barColor = null // adding the ability to set the color for each rather than the whole group
+ , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
, delay = 1200
, xDomain
, yDomain
@@ -4906,12 +6043,22 @@ nv.models.multiBar = function() {
availableHeight = height - margin.top - margin.bottom,
container = d3.select(this);
+ if(hideable && data.length) hideable = [{
+ values: data[0].values.map(function(d) {
+ return {
+ x: d.x,
+ y: 0,
+ series: d.series,
+ size: 0.01
+ };}
+ )}];
+
if (stacked)
data = d3.layout.stack()
.offset('zero')
.values(function(d){ return d.values })
.y(getY)
- (data);
+ (!data.length && hideable ? hideable : data);
//add series index to each data point for reference
@@ -4925,23 +6072,42 @@ nv.models.multiBar = function() {
//------------------------------------------------------------
+ // HACK for negative value stacking
+ if (stacked)
+ data[0].values.map(function(d,i) {
+ var posBase = 0, negBase = 0;
+ data.map(function(d) {
+ var f = d.values[i]
+ f.size = Math.abs(f.y);
+ if (f.y<0) {
+ f.y1 = negBase;
+ negBase = negBase - f.size;
+ } else
+ {
+ f.y1 = f.size + posBase;
+ posBase = posBase + f.size;
+ }
+ });
+ });
+
+ //------------------------------------------------------------
// Setup Scales
// remap and flatten the data for use in calculating the scales' domains
var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
data.map(function(d) {
return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i), y0: d.y0 }
+ return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
})
});
x .domain(d3.merge(seriesData).map(function(d) { return d.x }))
.rangeBands([0, availableWidth], .1);
- y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y0 : 0) }).concat(forceY)))
+ //y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y1 : 0) }).concat(forceY)))
+ y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 : d.y1 + d.y ) : d.y }).concat(forceY)))
.range([availableHeight, 0]);
-
// If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
if (x.domain()[0] === x.domain()[1] || y.domain()[0] === y.domain()[1]) singlePoint = true;
if (x.domain()[0] === x.domain()[1])
@@ -5013,7 +6179,7 @@ nv.models.multiBar = function() {
var bars = groups.selectAll('rect.nv-bar')
- .data(function(d) { return d.values });
+ .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values });
bars.exit().remove();
@@ -5079,14 +6245,28 @@ nv.models.multiBar = function() {
bars
.attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
.attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
+
+ if (barColor) {
+ if (!disabled) disabled = data.map(function() { return true });
+ bars
+ //.style('fill', barColor)
+ //.style('stroke', barColor)
+ //.style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker(j).toString(); })
+ //.style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker(j).toString(); })
+ .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
+ .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
+ }
+
+
if (stacked)
d3.transition(bars)
.delay(function(d,i) { return i * delay / data[0].values.length })
.attr('y', function(d,i) {
- return y(getY(d,i) + (stacked ? d.y0 : 0));
+
+ return y((stacked ? d.y1 : 0));
})
.attr('height', function(d,i) {
- return Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0)))
+ return Math.max(Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0))),1);
})
.each('end', function() {
d3.transition(d3.select(this))
@@ -5106,11 +6286,13 @@ nv.models.multiBar = function() {
d3.transition(d3.select(this))
.attr('y', function(d,i) {
return getY(d,i) < 0 ?
- y(0) :
- y(getY(d,i))
- })
- .attr('height', function(d,i) {
- return Math.abs(y(getY(d,i)) - y(0))
+ y(0) :
+ y(0) - y(getY(d,i)) < 1 ?
+ y(0) - 1 :
+ y(getY(d,i)) || 0;
+ })
+ .attr('height', function(d,i) {
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0;
});
})
@@ -5212,12 +6394,30 @@ nv.models.multiBar = function() {
return chart;
};
+ chart.barColor = function(_) {
+ if (!arguments.length) return barColor;
+ barColor = nv.utils.getColor(_);
+ return chart;
+ };
+
+ chart.disabled = function(_) {
+ if (!arguments.length) return disabled;
+ disabled = _;
+ return chart;
+ };
+
chart.id = function(_) {
if (!arguments.length) return id;
id = _;
return chart;
};
+ chart.hideable = function(_) {
+ if (!arguments.length) return hideable;
+ hideable = _;
+ return chart;
+ };
+
chart.delay = function(_) {
if (!arguments.length) return delay;
delay = _;
@@ -5258,8 +6458,11 @@ nv.models.multiBarChart = function() {
}
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
+ , state = { stacked: false }
+ , defaultState = null
, noData = "No Data Available."
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
+ , controlWidth = function() { return showControls ? 180 : 0 }
;
multibar
@@ -5267,7 +6470,7 @@ nv.models.multiBarChart = function() {
;
xAxis
.orient('bottom')
- .tickPadding(5)
+ .tickPadding(7)
.highlightZero(false)
.showMaxMin(false)
.tickFormat(function(d) { return d })
@@ -5310,7 +6513,19 @@ nv.models.multiBarChart = function() {
chart.update = function() { selection.transition().call(chart) };
chart.container = this;
-
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
//------------------------------------------------------------
// Display noData message if there's nothing to show.
@@ -5364,7 +6579,12 @@ nv.models.multiBarChart = function() {
// Legend
if (showLegend) {
- legend.width(availableWidth / 2);
+ legend.width(availableWidth - controlWidth());
+
+ if (multibar.barColor())
+ data.forEach(function(series,i) {
+ series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
+ })
g.select('.nv-legendWrap')
.datum(data)
@@ -5377,7 +6597,7 @@ nv.models.multiBarChart = function() {
}
g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + (availableWidth / 2) + ',' + (-margin.top) +')');
+ .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
}
//------------------------------------------------------------
@@ -5392,7 +6612,7 @@ nv.models.multiBarChart = function() {
{ key: 'Stacked', disabled: !multibar.stacked() }
];
- controls.width(180).color(['#444', '#444', '#444']);
+ controls.width(controlWidth()).color(['#444', '#444', '#444']);
g.select('.nv-controlsWrap')
.datum(controlsData)
.attr('transform', 'translate(0,' + (-margin.top) +')')
@@ -5409,6 +6629,7 @@ nv.models.multiBarChart = function() {
// Main Chart Component(s)
multibar
+ .disabled(data.map(function(series) { return series.disabled }))
.width(availableWidth)
.height(availableHeight)
.color(data.map(function(d,i) {
@@ -5453,9 +6674,12 @@ nv.models.multiBarChart = function() {
if(rotateLabels)
xTicks
- .selectAll('text')
- .attr('transform', function(d,i,j) { return 'rotate('+rotateLabels+' 0,0)' })
- .attr('text-transform', rotateLabels > 0 ? 'start' : 'end');
+ .selectAll('text')
+ .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
+ .attr('text-anchor', rotateLabels > 0 ? 'start' : 'end');
+
+ g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
+ .style('opacity', 1);
yAxis
.scale(y)
@@ -5484,6 +6708,9 @@ nv.models.multiBarChart = function() {
});
}
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
selection.transition().call(chart);
});
@@ -5504,6 +6731,9 @@ nv.models.multiBarChart = function() {
break;
}
+ state.stacked = multibar.stacked();
+ dispatch.stateChange(state);
+
selection.transition().call(chart);
});
@@ -5511,6 +6741,25 @@ nv.models.multiBarChart = function() {
if (tooltips) showTooltip(e, that.parentNode)
});
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ if (typeof e.stacked !== 'undefined') {
+ multibar.stacked(e.stacked);
+ state.stacked = e.stacked;
+ }
+
+ selection.call(chart);
+ });
+
//============================================================
@@ -5550,7 +6799,7 @@ nv.models.multiBarChart = function() {
chart.xAxis = xAxis;
chart.yAxis = yAxis;
- d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'forceX', 'forceY', 'clipEdge', 'id', 'stacked', 'delay');
+ d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'forceX', 'forceY', 'clipEdge', 'id', 'stacked', 'delay', 'barColor');
chart.margin = function(_) {
if (!arguments.length) return margin;
@@ -5622,6 +6871,18 @@ nv.models.multiBarChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -5650,6 +6911,8 @@ nv.models.multiBarHorizontal = function() {
, getY = function(d) { return d.y }
, forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
, color = nv.utils.defaultColor()
+ , barColor = null // adding the ability to set the color for each rather than the whole group
+ , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
, stacked = false
, showValues = false
, valuePadding = 60
@@ -5698,6 +6961,28 @@ nv.models.multiBarHorizontal = function() {
});
+
+ //------------------------------------------------------------
+ // HACK for negative value stacking
+ if (stacked)
+ data[0].values.map(function(d,i) {
+ var posBase = 0, negBase = 0;
+ data.map(function(d) {
+ var f = d.values[i]
+ f.size = Math.abs(f.y);
+ if (f.y<0) {
+ f.y1 = negBase - f.size;
+ negBase = negBase - f.size;
+ } else
+ {
+ f.y1 = posBase;
+ posBase = posBase + f.size;
+ }
+ });
+ });
+
+
+
//------------------------------------------------------------
// Setup Scales
@@ -5705,14 +6990,15 @@ nv.models.multiBarHorizontal = function() {
var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
data.map(function(d) {
return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i), y0: d.y0 }
+ return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
})
});
x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
.rangeBands([0, availableHeight], .1);
- y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y0 : 0) }).concat(forceY)))
+ //y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y0 : 0) }).concat(forceY)))
+ y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 + d.y : d.y1 ) : d.y }).concat(forceY)))
if (showValues && !stacked)
y.range([(y.domain()[0] < 0 ? valuePadding : 0), availableWidth - (y.domain()[1] > 0 ? valuePadding : 0) ]);
@@ -5825,32 +7111,45 @@ nv.models.multiBarHorizontal = function() {
d3.event.stopPropagation();
});
+
+ barsEnter.append('text');
+
if (showValues && !stacked) {
- barsEnter.append('text')
- .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' })
bars.select('text')
- .attr('y', x.rangeBand() / 2)
- .attr('dy', '-.32em')
+ .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' })
+ .attr('y', x.rangeBand() / (data.length * 2))
+ .attr('dy', '.32em')
.text(function(d,i) { return valueFormat(getY(d,i)) })
d3.transition(bars)
//.delay(function(d,i) { return i * delay / data[0].values.length })
.select('text')
.attr('x', function(d,i) { return getY(d,i) < 0 ? -4 : y(getY(d,i)) - y(0) + 4 })
} else {
- bars.selectAll('text').remove();
+ //bars.selectAll('text').remove();
+ bars.selectAll('text').text('');
}
bars
.attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
- //.attr('transform', function(d,i,j) {
- //return 'translate(' + y0(stacked ? d.y0 : 0) + ',' + x(getX(d,i)) + ')'
- //})
+
+ if (barColor) {
+ if (!disabled) disabled = data.map(function() { return true });
+ bars
+ //.style('fill', barColor)
+ //.style('stroke', barColor)
+ //.style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker(j).toString(); })
+ //.style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker(j).toString(); })
+ .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
+ .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
+ }
+
if (stacked)
d3.transition(bars)
//.delay(function(d,i) { return i * delay / data[0].values.length })
.attr('transform', function(d,i) {
//return 'translate(' + y(d.y0) + ',0)'
- return 'translate(' + y(d.y0) + ',' + x(getX(d,i)) + ')'
+ //return 'translate(' + y(d.y0) + ',' + x(getX(d,i)) + ')'
+ return 'translate(' + y(d.y1) + ',' + x(getX(d,i)) + ')'
})
.select('rect')
.attr('width', function(d,i) {
@@ -5873,7 +7172,7 @@ nv.models.multiBarHorizontal = function() {
.select('rect')
.attr('height', x.rangeBand() / data.length )
.attr('width', function(d,i) {
- return Math.abs(y(getY(d,i)) - y(0))
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)),1)
});
@@ -5968,6 +7267,18 @@ nv.models.multiBarHorizontal = function() {
return chart;
};
+ chart.barColor = function(_) {
+ if (!arguments.length) return barColor;
+ barColor = nv.utils.getColor(_);
+ return chart;
+ };
+
+ chart.disabled = function(_) {
+ if (!arguments.length) return disabled;
+ disabled = _;
+ return chart;
+ };
+
chart.id = function(_) {
if (!arguments.length) return id;
id = _;
@@ -6031,8 +7342,11 @@ nv.models.multiBarHorizontalChart = function() {
}
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
+ , state = { stacked: stacked }
+ , defaultState = null
, noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
+ , controlWidth = function() { return showControls ? 180 : 0 }
;
multibar
@@ -6083,6 +7397,19 @@ nv.models.multiBarHorizontalChart = function() {
chart.update = function() { selection.transition().call(chart) };
chart.container = this;
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
@@ -6137,7 +7464,12 @@ nv.models.multiBarHorizontalChart = function() {
// Legend
if (showLegend) {
- legend.width(availableWidth / 2);
+ legend.width(availableWidth - controlWidth());
+
+ if (multibar.barColor())
+ data.forEach(function(series,i) {
+ series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
+ })
g.select('.nv-legendWrap')
.datum(data)
@@ -6150,7 +7482,7 @@ nv.models.multiBarHorizontalChart = function() {
}
g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + (availableWidth / 2) + ',' + (-margin.top) +')');
+ .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
}
//------------------------------------------------------------
@@ -6165,7 +7497,7 @@ nv.models.multiBarHorizontalChart = function() {
{ key: 'Stacked', disabled: !multibar.stacked() }
];
- controls.width(180).color(['#444', '#444', '#444']);
+ controls.width(controlWidth()).color(['#444', '#444', '#444']);
g.select('.nv-controlsWrap')
.datum(controlsData)
.attr('transform', 'translate(0,' + (-margin.top) +')')
@@ -6182,6 +7514,7 @@ nv.models.multiBarHorizontalChart = function() {
// Main Chart Component(s)
multibar
+ .disabled(data.map(function(series) { return series.disabled }))
.width(availableWidth)
.height(availableHeight)
.color(data.map(function(d,i) {
@@ -6244,6 +7577,9 @@ nv.models.multiBarHorizontalChart = function() {
});
}
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
selection.transition().call(chart);
});
@@ -6264,6 +7600,9 @@ nv.models.multiBarHorizontalChart = function() {
break;
}
+ state.stacked = multibar.stacked();
+ dispatch.stateChange(state);
+
selection.transition().call(chart);
});
@@ -6271,6 +7610,24 @@ nv.models.multiBarHorizontalChart = function() {
if (tooltips) showTooltip(e, that.parentNode);
});
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ if (typeof e.stacked !== 'undefined') {
+ multibar.stacked(e.stacked);
+ state.stacked = e.stacked;
+ }
+
+ selection.call(chart);
+ });
//============================================================
@@ -6310,7 +7667,7 @@ nv.models.multiBarHorizontalChart = function() {
chart.xAxis = xAxis;
chart.yAxis = yAxis;
- d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'forceX', 'forceY', 'clipEdge', 'id', 'delay', 'showValues', 'valueFormat', 'stacked');
+ d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'forceX', 'forceY', 'clipEdge', 'id', 'delay', 'showValues', 'valueFormat', 'stacked', 'barColor');
chart.margin = function(_) {
if (!arguments.length) return margin;
@@ -6370,6 +7727,18 @@ nv.models.multiBarHorizontalChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -6427,7 +7796,7 @@ nv.models.multiChart = function() {
var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
top = e.pos[1] + ( offsetElement.offsetTop || 0),
x = xAxis.tickFormat()(lines1.x()(e.point, e.pointIndex)),
- y = (e.series.bar ? yAxis1 : yAxis2).tickFormat()(lines1.y()(e.point, e.pointIndex)),
+ y = ((e.series.yAxis == 2) ? yAxis2 : yAxis1).tickFormat()(lines1.y()(e.point, e.pointIndex)),
content = tooltip(e.series.key, x, y, e, chart);
nv.tooltip.show([left, top], content, undefined, undefined, offsetElement.offsetParent);
@@ -6846,6 +8215,7 @@ nv.models.ohlcBar = function() {
, getLow = function(d) { return d.low }
, forceX = []
, forceY = []
+ , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
, clipEdge = true
, color = nv.utils.defaultColor()
, xDomain
@@ -6874,8 +8244,12 @@ nv.models.ohlcBar = function() {
//------------------------------------------------------------
// Setup Scales
- x .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ))
- .range([0, availableWidth]);
+ x .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
+
+ if (padData)
+ x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range([0, availableWidth]);
y .domain(yDomain || [
d3.min(data[0].values.map(getLow).concat(forceY)),
@@ -7156,6 +8530,12 @@ nv.models.ohlcBar = function() {
return chart;
};
+ chart.padData = function(_) {
+ if (!arguments.length) return padData;
+ padData = _;
+ return chart;
+ };
+
chart.clipEdge = function(_) {
if (!arguments.length) return clipEdge;
clipEdge = _;
@@ -7179,7 +8559,6 @@ nv.models.ohlcBar = function() {
return chart;
}
-
nv.models.pie = function() {
//============================================================
@@ -7192,13 +8571,19 @@ nv.models.pie = function() {
, getValues = function(d) { return d.values }
, getX = function(d) { return d.x }
, getY = function(d) { return d.y }
+ , getDescription = function(d) { return d.description }
, id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
, color = nv.utils.defaultColor()
, valueFormat = d3.format(',.2f')
, showLabels = true
+ , pieLabelsOutside = true
, donutLabelsOutside = false
, labelThreshold = .02 //if slice percentage is under this, don't show label
, donut = false
+ , labelSunbeamLayout = false
+ , startAngle = false
+ , endAngle = false
+ , donutRatio = 0.5
, dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
;
@@ -7210,6 +8595,7 @@ nv.models.pie = function() {
var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom,
radius = Math.min(availableWidth, availableHeight) / 2,
+ arcRadius = radius-(radius / 5),
container = d3.select(this);
@@ -7242,10 +8628,11 @@ nv.models.pie = function() {
var arc = d3.svg.arc()
- .outerRadius((radius-(radius / 5)));
-
- if (donut) arc.innerRadius(radius / 2);
+ .outerRadius(arcRadius);
+ if (startAngle) arc.startAngle(startAngle)
+ if (endAngle) arc.endAngle(endAngle);
+ if (donut) arc.innerRadius(radius * donutRatio);
// Setup the Pie chart and choose the data element
var pie = d3.layout.pie()
@@ -7317,10 +8704,11 @@ nv.models.pie = function() {
if (showLabels) {
// This does the normal label
- var labelsArc = arc;
- if (donutLabelsOutside) {
- labelsArc = d3.svg.arc().outerRadius(arc.outerRadius())
- }
+ var labelsArc = d3.svg.arc().innerRadius(0);
+
+ if (pieLabelsOutside){ labelsArc = arc; }
+
+ if (donutLabelsOutside) { labelsArc = d3.svg.arc().outerRadius(arc.outerRadius()); }
ae.append("g").classed("nv-label", true)
.each(function(d, i) {
@@ -7328,9 +8716,21 @@ nv.models.pie = function() {
group
.attr('transform', function(d) {
- d.outerRadius = radius + 10; // Set Outer Coordinate
- d.innerRadius = radius + 15; // Set Inner Coordinate
- return 'translate(' + labelsArc.centroid(d) + ')'
+ if (labelSunbeamLayout) {
+ d.outerRadius = arcRadius + 10; // Set Outer Coordinate
+ d.innerRadius = arcRadius + 15; // Set Inner Coordinate
+ var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
+ if ((d.startAngle+d.endAngle)/2 < Math.PI) {
+ rotateAngle -= 90;
+ } else {
+ rotateAngle += 90;
+ }
+ return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
+ } else {
+ d.outerRadius = radius + 10; // Set Outer Coordinate
+ d.innerRadius = radius + 15; // Set Inner Coordinate
+ return 'translate(' + labelsArc.centroid(d) + ')'
+ }
});
group.append('rect')
@@ -7340,7 +8740,7 @@ nv.models.pie = function() {
.attr("ry", 3);
group.append('text')
- .style('text-anchor', 'middle') //center the text on it's origin
+ .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
.style('fill', '#000')
@@ -7348,9 +8748,21 @@ nv.models.pie = function() {
slices.select(".nv-label").transition()
.attr('transform', function(d) {
- d.outerRadius = radius + 10; // Set Outer Coordinate
- d.innerRadius = radius + 15; // Set Inner Coordinate
- return 'translate(' + labelsArc.centroid(d) + ')';
+ if (labelSunbeamLayout) {
+ d.outerRadius = arcRadius + 10; // Set Outer Coordinate
+ d.innerRadius = arcRadius + 15; // Set Inner Coordinate
+ var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
+ if ((d.startAngle+d.endAngle)/2 < Math.PI) {
+ rotateAngle -= 90;
+ } else {
+ rotateAngle += 90;
+ }
+ return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
+ } else {
+ d.outerRadius = radius + 10; // Set Outer Coordinate
+ d.innerRadius = radius + 15; // Set Inner Coordinate
+ return 'translate(' + labelsArc.centroid(d) + ')'
+ }
});
slices.each(function(d, i) {
@@ -7358,6 +8770,7 @@ nv.models.pie = function() {
slice
.select(".nv-label text")
+ .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
.text(function(d, i) {
var percent = (d.endAngle - d.startAngle) / (2 * Math.PI);
return (d.value && percent > labelThreshold) ? getX(d.data) : '';
@@ -7447,24 +8860,60 @@ nv.models.pie = function() {
getY = d3.functor(_);
return chart;
};
+
+ chart.description = function(_) {
+ if (!arguments.length) return getDescription;
+ getDescription = _;
+ return chart;
+ };
chart.showLabels = function(_) {
if (!arguments.length) return showLabels;
showLabels = _;
return chart;
};
+
+ chart.labelSunbeamLayout = function(_) {
+ if (!arguments.length) return labelSunbeamLayout;
+ labelSunbeamLayout = _;
+ return chart;
+ };
chart.donutLabelsOutside = function(_) {
if (!arguments.length) return donutLabelsOutside;
donutLabelsOutside = _;
return chart;
};
+
+ chart.pieLabelsOutside = function(_) {
+ if (!arguments.length) return pieLabelsOutside;
+ pieLabelsOutside = _;
+ return chart;
+ };
chart.donut = function(_) {
if (!arguments.length) return donut;
donut = _;
return chart;
};
+
+ chart.donutRatio = function(_) {
+ if (!arguments.length) return donutRatio;
+ donutRatio = _;
+ return chart;
+ };
+
+ chart.startAngle = function(_) {
+ if (!arguments.length) return startAngle;
+ startAngle = _;
+ return chart;
+ };
+
+ chart.endAngle = function(_) {
+ if (!arguments.length) return endAngle;
+ endAngle = _;
+ return chart;
+ };
chart.id = function(_) {
if (!arguments.length) return id;
@@ -7489,13 +8938,11 @@ nv.models.pie = function() {
labelThreshold = _;
return chart;
};
-
//============================================================
return chart;
}
-
nv.models.pieChart = function() {
//============================================================
@@ -7516,8 +8963,10 @@ nv.models.pieChart = function() {
return '<h3>' + key + '</h3>' +
'<p>' + y + '</p>'
}
+ , state = {}
+ , defaultState = null
, noData = "No Data Available."
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
;
//============================================================
@@ -7528,10 +8977,11 @@ nv.models.pieChart = function() {
//------------------------------------------------------------
var showTooltip = function(e, offsetElement) {
+ var tooltipLabel = pie.description()(e.point) || pie.x()(e.point)
var left = e.pos[0] + ( (offsetElement && offsetElement.offsetLeft) || 0 ),
top = e.pos[1] + ( (offsetElement && offsetElement.offsetTop) || 0),
y = pie.valueFormat()(pie.y()(e.point)),
- content = tooltip(pie.x()(e.point), y, e, chart);
+ content = tooltip(tooltipLabel, y, e, chart);
nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
};
@@ -7544,7 +8994,7 @@ nv.models.pieChart = function() {
var container = d3.select(this),
that = this;
- var availableWidth = (width || parseInt(container.style('width')) || 960)
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
- margin.left - margin.right,
availableHeight = (height || parseInt(container.style('height')) || 400)
- margin.top - margin.bottom;
@@ -7552,11 +9002,24 @@ nv.models.pieChart = function() {
chart.update = function() { chart(selection); };
chart.container = this;
+ //set state.disabled
+ state.disabled = data[0].map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
- if (!data || !data.length) {
+ if (!data[0] || !data[0].length) {
var noDataText = container.selectAll('.nv-noData').data([noData]);
noDataText.enter().append('text')
@@ -7649,6 +9112,9 @@ nv.models.pieChart = function() {
});
}
+ state.disabled = data[0].map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
selection.transition().call(chart)
});
@@ -7656,6 +9122,20 @@ nv.models.pieChart = function() {
dispatch.tooltipHide(e);
});
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data[0].forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ selection.call(chart);
+ });
+
//============================================================
@@ -7689,10 +9169,11 @@ nv.models.pieChart = function() {
//------------------------------------------------------------
// expose chart's sub-components
+ chart.legend = legend;
chart.dispatch = dispatch;
chart.pie = pie;
- d3.rebind(chart, pie, 'valueFormat', 'values', 'x', 'y', 'id', 'showLabels', 'donutLabelsOutside', 'donut', 'labelThreshold');
+ d3.rebind(chart, pie, 'valueFormat', 'values', 'x', 'y', 'description', 'id', 'showLabels', 'donutLabelsOutside', 'pieLabelsOutside', 'donut', 'donutRatio', 'labelThreshold');
chart.margin = function(_) {
if (!arguments.length) return margin;
@@ -7741,6 +9222,18 @@ nv.models.pieChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -7759,33 +9252,36 @@ nv.models.scatter = function() {
// Public Variables with Default Settings
//------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 960
- , height = 500
- , color = nv.utils.defaultColor() // chooses color
- , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't selet one
- , x = d3.scale.linear()
- , y = d3.scale.linear()
- , z = d3.scale.linear() //linear because d3.svg.shape.size is treated as area
- , getX = function(d) { return d.x } // accessor to get the x value
- , getY = function(d) { return d.y } // accessor to get the y value
- , getSize = function(d) { return d.size } // accessor to get the point size
- , getShape = function(d) { return d.shape || 'circle' } // accessor to get point shape
- , forceX = [] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
- , forceY = [] // List of numbers to Force into the Y scale
- , forceSize = [] // List of numbers to Force into the Size scale
- , interactive = true // If true, plots a voronoi overlay for advanced point interection
- , pointActive = function(d) { return !d.notActive } // any points that return false will be filtered out
- , clipEdge = false // if true, masks points within x and y scale
- , clipVoronoi = true // if true, masks each point with a circle... can turn off to slightly increase performance
- , clipRadius = function() { return 25 } // function to get the radius for voronoi point clips
- , xDomain = null // Override x domain (skips the calculation from data)
- , yDomain = null // Override y domain
- , sizeDomain = null // Override point size domain
- , sizeRange = null
- , singlePoint = false
- , dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout')
- , useVoronoi = true
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , color = nv.utils.defaultColor() // chooses color
+ , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , z = d3.scale.linear() //linear because d3.svg.shape.size is treated as area
+ , getX = function(d) { return d.x } // accessor to get the x value
+ , getY = function(d) { return d.y } // accessor to get the y value
+ , getSize = function(d) { return d.size || 1} // accessor to get the point size
+ , getShape = function(d) { return d.shape || 'circle' } // accessor to get point shape
+ , onlyCircles = true // Set to false to use shapes
+ , forceX = [] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
+ , forceY = [] // List of numbers to Force into the Y scale
+ , forceSize = [] // List of numbers to Force into the Size scale
+ , interactive = true // If true, plots a voronoi overlay for advanced point intersection
+ , pointActive = function(d) { return !d.notActive } // any points that return false will be filtered out
+ , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
+ , padDataOuter = .1 //outerPadding to imitate ordinal scale outer padding
+ , clipEdge = false // if true, masks points within x and y scale
+ , clipVoronoi = true // if true, masks each point with a circle... can turn off to slightly increase performance
+ , clipRadius = function() { return 25 } // function to get the radius for voronoi point clips
+ , xDomain = null // Override x domain (skips the calculation from data)
+ , yDomain = null // Override y domain
+ , sizeDomain = null // Override point size domain
+ , sizeRange = null
+ , singlePoint = false
+ , dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout')
+ , useVoronoi = true
;
//============================================================
@@ -7797,6 +9293,7 @@ nv.models.scatter = function() {
var x0, y0, z0 // used to store previous scales
, timeoutID
+ , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
;
//============================================================
@@ -7831,7 +9328,12 @@ nv.models.scatter = function() {
);
x .domain(xDomain || d3.extent(seriesData.map(function(d) { return d.x }).concat(forceX)))
- .range([0, availableWidth]);
+
+ if (padData && data[0])
+ x.range([(availableWidth * padDataOuter + availableWidth) / (2 *data[0].values.length), availableWidth - availableWidth * (1 + padDataOuter) / (2 * data[0].values.length) ]);
+ //x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range([0, availableWidth]);
y .domain(yDomain || d3.extent(seriesData.map(function(d) { return d.y }).concat(forceY)))
.range([availableHeight, 0]);
@@ -7907,45 +9409,109 @@ nv.models.scatter = function() {
);
- if (clipVoronoi) {
- defsEnter.append('clipPath').attr('id', 'nv-points-clip-' + id);
- var pointClips = wrap.select('#nv-points-clip-' + id).selectAll('circle')
- .data(vertices);
- pointClips.enter().append('circle')
- .attr('r', clipRadius);
- pointClips.exit().remove();
- pointClips
- .attr('cx', function(d) { return d[0] })
- .attr('cy', function(d) { return d[1] });
+ //inject series and point index for reference into voronoi
+ if (useVoronoi === true) {
+
+ if (clipVoronoi) {
+ var pointClipsEnter = wrap.select('defs').selectAll('.nv-point-clips')
+ .data([id])
+ .enter();
+
+ pointClipsEnter.append('clipPath')
+ .attr('class', 'nv-point-clips')
+ .attr('id', 'nv-points-clip-' + id);
+
+ var pointClips = wrap.select('#nv-points-clip-' + id).selectAll('circle')
+ .data(vertices);
+ pointClips.enter().append('circle')
+ .attr('r', clipRadius);
+ pointClips.exit().remove();
+ pointClips
+ .attr('cx', function(d) { return d[0] })
+ .attr('cy', function(d) { return d[1] });
+
+ wrap.select('.nv-point-paths')
+ .attr('clip-path', 'url(#nv-points-clip-' + id + ')');
+ }
- wrap.select('.nv-point-paths')
- .attr('clip-path', 'url(#nv-points-clip-' + id + ')');
- }
+ // if(vertices.length < 3) {
+ // Issue #283 - Adding 2 dummy points to the voronoi b/c voronoi requires min 3 points to work
+ vertices.push([x.range()[0] - 20, y.range()[0] - 20, null, null]);
+ vertices.push([x.range()[1] + 20, y.range()[1] + 20, null, null]);
+ vertices.push([x.range()[0] - 20, y.range()[0] + 20, null, null]);
+ vertices.push([x.range()[1] + 20, y.range()[1] - 20, null, null]);
+ // }
+
+ var bounds = d3.geom.polygon([
+ [-10,-10],
+ [-10,height + 10],
+ [width + 10,height + 10],
+ [width + 10,-10]
+ ]);
- //inject series and point index for reference into voronoi
- if (useVoronoi === true) {
var voronoi = d3.geom.voronoi(vertices).map(function(d, i) {
return {
- 'data': d,
+ 'data': bounds.clip(d),
'series': vertices[i][2],
'point': vertices[i][3]
}
});
+
var pointPaths = wrap.select('.nv-point-paths').selectAll('path')
.data(voronoi);
pointPaths.enter().append('path')
.attr('class', function(d,i) { return 'nv-path-'+i; });
pointPaths.exit().remove();
pointPaths
- .attr('d', function(d) { return 'M' + d.data.join(',') + 'Z'; });
+ .attr('d', function(d) { return 'M' + d.data.join('L') + 'Z'; });
+
+ pointPaths
+ .on('click', function(d) {
+ if (needsUpdate) return 0;
+ var series = data[d.series],
+ point = series.values[d.point];
+
+ dispatch.elementClick({
+ point: point,
+ series: series,
+ pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: d.point
+ });
+ })
+ .on('mouseover', function(d) {
+ if (needsUpdate) return 0;
+ var series = data[d.series],
+ point = series.values[d.point];
+
+ dispatch.elementMouseover({
+ point: point,
+ series: series,
+ pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: d.point
+ });
+ })
+ .on('mouseout', function(d, i) {
+ if (needsUpdate) return 0;
+ var series = data[d.series],
+ point = series.values[d.point];
+
+ dispatch.elementMouseout({
+ point: point,
+ series: series,
+ seriesIndex: d.series,
+ pointIndex: d.point
+ });
+ });
- eventElements = pointPaths;
} else {
+ /*
// bring data in form needed for click handlers
var dataWithPoints = vertices.map(function(d, i) {
return {
@@ -7954,54 +9520,58 @@ nv.models.scatter = function() {
'point': vertices[i][3]
}
});
+ */
// add event handlers to points instead voronoi paths
- eventElements = wrap.select('.nv-groups').selectAll('.nv-group')
- .selectAll('path.nv-point')
- .data(dataWithPoints)
- .style('pointer-events', 'auto'); // recativate events, disabled by css
- }
+ wrap.select('.nv-groups').selectAll('.nv-group')
+ .selectAll('.nv-point')
+ //.data(dataWithPoints)
+ //.style('pointer-events', 'auto') // recativate events, disabled by css
+ .on('click', function(d,i) {
+ //nv.log('test', d, i);
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
- eventElements
- .on('click', function(d) {
- var series = data[d.series],
- point = series.values[d.point];
-
- dispatch.elementClick({
- point: point,
- series: series,
- pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
- seriesIndex: d.series,
- pointIndex: d.point
- });
- })
- .on('mouseover', function(d) {
- var series = data[d.series],
- point = series.values[d.point];
+ dispatch.elementClick({
+ point: point,
+ series: series,
+ pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: i
+ });
+ })
+ .on('mouseover', function(d,i) {
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
- dispatch.elementMouseover({
- point: point,
- series: series,
- pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
- seriesIndex: d.series,
- pointIndex: d.point
- });
- })
- .on('mouseout', function(d, i) {
- var series = data[d.series],
- point = series.values[d.point];
+ dispatch.elementMouseover({
+ point: point,
+ series: series,
+ pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: i
+ });
+ })
+ .on('mouseout', function(d,i) {
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
- dispatch.elementMouseout({
- point: point,
- series: series,
- seriesIndex: d.series,
- pointIndex: d.point
+ dispatch.elementMouseout({
+ point: point,
+ series: series,
+ seriesIndex: d.series,
+ pointIndex: i
+ });
});
- });
+ }
+ needsUpdate = false;
}
-
+ needsUpdate = true;
var groups = wrap.select('.nv-groups').selectAll('.nv-group')
.data(function(d) { return d }, function(d) { return d.key });
@@ -8022,38 +9592,62 @@ nv.models.scatter = function() {
.style('fill-opacity', .5);
- var points = groups.selectAll('path.nv-point')
- .data(function(d) { return d.values });
- points.enter().append('path')
- .attr('transform', function(d,i) {
- return 'translate(' + x0(getX(d,i)) + ',' + y0(getY(d,i)) + ')'
- })
- .attr('d',
- d3.svg.symbol()
- .type(getShape)
- .size(function(d,i) { return z(getSize(d,i)) })
- );
- points.exit().remove();
- d3.transition(groups.exit().selectAll('path.nv-point'))
- .attr('transform', function(d,i) {
- return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
- })
- .remove();
- points.attr('class', function(d,i) { return 'nv-point nv-point-' + i });
- d3.transition(points)
- .attr('transform', function(d,i) {
- return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
- })
- .attr('d',
- d3.svg.symbol()
- .type(getShape)
- .size(function(d,i) { return z(getSize(d,i)) })
- );
+ if (onlyCircles) {
+
+ var points = groups.selectAll('circle.nv-point')
+ .data(function(d) { return d.values });
+ points.enter().append('circle')
+ .attr('cx', function(d,i) { return x0(getX(d,i)) })
+ .attr('cy', function(d,i) { return y0(getY(d,i)) })
+ .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
+ points.exit().remove();
+ d3.transition(groups.exit().selectAll('path.nv-point'))
+ .attr('cx', function(d,i) { return x(getX(d,i)) })
+ .attr('cy', function(d,i) { return y(getY(d,i)) })
+ .remove();
+ points.attr('class', function(d,i) { return 'nv-point nv-point-' + i });
+ d3.transition(points)
+ .attr('cx', function(d,i) { return x(getX(d,i)) })
+ .attr('cy', function(d,i) { return y(getY(d,i)) })
+ .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
+
+ } else {
+
+ var points = groups.selectAll('path.nv-point')
+ .data(function(d) { return d.values });
+ points.enter().append('path')
+ .attr('transform', function(d,i) {
+ return 'translate(' + x0(getX(d,i)) + ',' + y0(getY(d,i)) + ')'
+ })
+ .attr('d',
+ d3.svg.symbol()
+ .type(getShape)
+ .size(function(d,i) { return z(getSize(d,i)) })
+ );
+ points.exit().remove();
+ d3.transition(groups.exit().selectAll('path.nv-point'))
+ .attr('transform', function(d,i) {
+ return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
+ })
+ .remove();
+ points.attr('class', function(d,i) { return 'nv-point nv-point-' + i });
+ d3.transition(points)
+ .attr('transform', function(d,i) {
+ //nv.log(d,i,getX(d,i), x(getX(d,i)));
+ return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
+ })
+ .attr('d',
+ d3.svg.symbol()
+ .type(getShape)
+ .size(function(d,i) { return z(getSize(d,i)) })
+ );
+ }
// Delay updating the invisible interactive layer for smoother animation
clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer
- timeoutID = setTimeout(updateInteractiveLayer, 1000);
+ timeoutID = setTimeout(updateInteractiveLayer, 300);
+ //updateInteractiveLayer();
//store old scales for use in transitions on update
x0 = x.copy();
@@ -8202,6 +9796,18 @@ nv.models.scatter = function() {
return chart;
};
+ chart.padData = function(_) {
+ if (!arguments.length) return padData;
+ padData = _;
+ return chart;
+ };
+
+ chart.padDataOuter = function(_) {
+ if (!arguments.length) return padDataOuter;
+ padDataOuter = _;
+ return chart;
+ };
+
chart.clipEdge = function(_) {
if (!arguments.length) return clipEdge;
clipEdge = _;
@@ -8241,6 +9847,12 @@ nv.models.scatter = function() {
return chart;
};
+ chart.onlyCircles = function(_) {
+ if (!arguments.length) return onlyCircles;
+ onlyCircles = _;
+ return chart;
+ };
+
chart.id = function(_) {
if (!arguments.length) return id;
id = _;
@@ -8274,7 +9886,7 @@ nv.models.scatterChart = function() {
, distY = nv.models.distribution()
;
- var margin = {top: 30, right: 20, bottom: 50, left: 60}
+ var margin = {top: 30, right: 20, bottom: 50, left: 75}
, width = null
, height = null
, color = nv.utils.defaultColor()
@@ -8293,7 +9905,9 @@ nv.models.scatterChart = function() {
, tooltipY = function(key, x, y) { return '<strong>' + y + '</strong>' }
//, tooltip = function(key, x, y) { return '<h3>' + key + '</h3>' }
, tooltip = null
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , state = {}
+ , defaultState = null
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
, noData = "No Data Available."
;
@@ -8365,6 +9979,19 @@ nv.models.scatterChart = function() {
chart.update = function() { chart(selection) };
chart.container = this;
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
//------------------------------------------------------------
// Display noData message if there's nothing to show.
@@ -8405,10 +10032,10 @@ nv.models.scatterChart = function() {
var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
var gEnter = wrapEnter.append('g');
- var g = wrap.select('g')
+ var g = wrap.select('g');
// background for pointer events
- gEnter.append('rect').attr('class', 'nvd3 nv-background')
+ gEnter.append('rect').attr('class', 'nvd3 nv-background');
gEnter.append('g').attr('class', 'nv-x nv-axis');
gEnter.append('g').attr('class', 'nv-y nv-axis');
@@ -8417,8 +10044,6 @@ nv.models.scatterChart = function() {
gEnter.append('g').attr('class', 'nv-legendWrap');
gEnter.append('g').attr('class', 'nv-controlsWrap');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
//------------------------------------------------------------
@@ -8459,6 +10084,9 @@ nv.models.scatterChart = function() {
//------------------------------------------------------------
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+
//------------------------------------------------------------
// Main Chart Component(s)
@@ -8493,7 +10121,7 @@ nv.models.scatterChart = function() {
xAxis
.scale(x)
- .ticks( xAxis.ticks() ? xAxis.ticks() : availableWidth / 100 )
+ .ticks( xAxis.ticks() && xAxis.ticks().length ? xAxis.ticks() : availableWidth / 100 )
.tickSize( -availableHeight , 0);
g.select('.nv-x.nv-axis')
@@ -8503,7 +10131,7 @@ nv.models.scatterChart = function() {
yAxis
.scale(y)
- .ticks( yAxis.ticks() ? yAxis.ticks() : availableHeight / 36 )
+ .ticks( yAxis.ticks() && yAxis.ticks().length ? yAxis.ticks() : availableHeight / 36 )
.tickSize( -availableWidth, 0);
g.select('.nv-y.nv-axis')
@@ -8623,6 +10251,9 @@ nv.models.scatterChart = function() {
});
}
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
chart(selection);
});
@@ -8652,6 +10283,20 @@ nv.models.scatterChart = function() {
if (tooltips) showTooltip(e, that.parentNode);
});
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ selection.call(chart);
+ });
+
//============================================================
@@ -8797,6 +10442,18 @@ nv.models.scatterChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -8824,7 +10481,7 @@ nv.models.scatterPlusLineChart = function() {
, distY = nv.models.distribution()
;
- var margin = {top: 30, right: 20, bottom: 50, left: 60}
+ var margin = {top: 30, right: 20, bottom: 50, left: 75}
, width = null
, height = null
, color = nv.utils.defaultColor()
@@ -8839,9 +10496,12 @@ nv.models.scatterPlusLineChart = function() {
, tooltips = true
, tooltipX = function(key, x, y) { return '<strong>' + x + '</strong>' }
, tooltipY = function(key, x, y) { return '<strong>' + y + '</strong>' }
- //, tooltip = function(key, x, y) { return '<h3>' + key + '</h3>' }
- , tooltip = null
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , tooltip = function(key, x, y, date) { return '<h3>' + key + '</h3>'
+ + '<p>' + date + '</p>' }
+ //, tooltip = null
+ , state = {}
+ , defaultState = null
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
, noData = "No Data Available."
;
@@ -8890,7 +10550,7 @@ nv.models.scatterPlusLineChart = function() {
if( tooltipY != null )
nv.tooltip.show([leftY, topY], tooltipY(e.series.key, xVal, yVal, e, chart), 'e', 1, offsetElement, 'y-nvtooltip');
if( tooltip != null )
- nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
+ nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e.point.tooltip, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
};
var controlsData = [
@@ -8913,6 +10573,19 @@ nv.models.scatterPlusLineChart = function() {
chart.update = function() { chart(selection) };
chart.container = this;
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
//------------------------------------------------------------
// Display noData message if there's nothing to show.
@@ -9187,6 +10860,9 @@ nv.models.scatterPlusLineChart = function() {
});
}
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
chart(selection);
});
@@ -9216,6 +10892,20 @@ nv.models.scatterPlusLineChart = function() {
if (tooltips) showTooltip(e, that.parentNode);
});
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ selection.call(chart);
+ });
+
//============================================================
@@ -9349,6 +11039,18 @@ nv.models.scatterPlusLineChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -9429,15 +11131,32 @@ nv.models.sparkline = function() {
// TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent)
var points = wrap.selectAll('circle.nv-point')
- .data(function(d) { return d.filter(function(p,i) { return y.domain().indexOf(getY(p,i)) != -1 || getX(p,i) == x.domain()[1] }) });
- points.enter().append('circle').attr('class', 'nv-point');
+ .data(function(data) {
+ var yValues = data.map(function(d, i) { return getY(d,i); });
+ function pointIndex(index) {
+ if (index != -1) {
+ var result = data[index];
+ result.pointIndex = index;
+ return result;
+ } else {
+ return null;
+ }
+ }
+ var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])),
+ minPoint = pointIndex(yValues.indexOf(y.domain()[0])),
+ currentPoint = pointIndex(yValues.length - 1);
+ return [minPoint, maxPoint, currentPoint].filter(function (d) {return d != null;});
+ });
+ points.enter().append('circle');
points.exit().remove();
points
- .attr('cx', function(d,i) { return x(getX(d,i)) })
- .attr('cy', function(d,i) { return y(getY(d,i)) })
+ .attr('cx', function(d,i) { return x(getX(d,d.pointIndex)) })
+ .attr('cy', function(d,i) { return y(getY(d,d.pointIndex)) })
.attr('r', 2)
- .style('stroke', function(d,i) { return d.x == x.domain()[1] ? '#444' : d.y == y.domain()[0] ? '#d62728' : '#2ca02c' })
- .style('fill', function(d,i) { return d.x == x.domain()[1] ? '#444' : d.y == y.domain()[0] ? '#d62728' : '#2ca02c' });
+ .attr('class', function(d,i) {
+ return getX(d, d.pointIndex) == x.domain()[1] ? 'nv-point nv-currentValue' :
+ getY(d, d.pointIndex) == y.domain()[0] ? 'nv-point nv-minValue' : 'nv-point nv-maxValue'
+ });
});
return chart;
@@ -9531,16 +11250,18 @@ nv.models.sparklinePlus = function() {
var sparkline = nv.models.sparkline();
- var margin = {top: 15, right: 40, bottom: 3, left: 40}
+ var margin = {top: 15, right: 100, bottom: 10, left: 50}
, width = null
, height = null
, x
, y
- , color = nv.utils.defaultColor()
- , index
+ , index = []
, paused = false
, xTickFormat = d3.format(',r')
, yTickFormat = d3.format(',.2f')
+ , showValue = true
+ , alignValue = true
+ , rightAlignValue = false
, noData = "No Data Available."
;
@@ -9556,6 +11277,7 @@ nv.models.sparklinePlus = function() {
availableHeight = (height || parseInt(container.style('height')) || 400)
- margin.top - margin.bottom;
+ var currentValue = sparkline.y()(data[data.length-1], data.length-1);
chart.update = function() { chart(selection) };
chart.container = this;
@@ -9604,6 +11326,7 @@ nv.models.sparklinePlus = function() {
var g = wrap.select('g');
gEnter.append('g').attr('class', 'nv-sparklineWrap');
+ gEnter.append('g').attr('class', 'nv-valueWrap');
gEnter.append('g').attr('class', 'nv-hoverArea');
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -9621,81 +11344,99 @@ nv.models.sparklinePlus = function() {
.height(availableHeight);
sparklineWrap
- .style('stroke', function(d, i){ return d.color || color(d, i) })
.call(sparkline);
//------------------------------------------------------------
+ var valueWrap = g.select('.nv-valueWrap');
+
+ var value = valueWrap.selectAll('.nv-currentValue')
+ .data([currentValue]);
+
+ value.enter().append('text').attr('class', 'nv-currentValue')
+ .attr('dx', rightAlignValue ? -8 : 8)
+ .attr('dy', '.9em')
+ .style('text-anchor', rightAlignValue ? 'end' : 'start');
+
+ value
+ .attr('x', availableWidth + (rightAlignValue ? margin.right : 0))
+ .attr('y', alignValue ? function(d) { return y(d) } : 0)
+ .style('fill', sparkline.color()(data[data.length-1], data.length-1))
+ .text(yTickFormat(currentValue));
+
+
gEnter.select('.nv-hoverArea').append('rect')
.on('mousemove', sparklineHover)
.on('click', function() { paused = !paused })
- .on('mouseout', function() { index = null; updateValueLine(); });
+ .on('mouseout', function() { index = []; updateValueLine(); });
+ //.on('mouseout', function() { index = null; updateValueLine(); });
g.select('.nv-hoverArea rect')
.attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' })
.attr('width', availableWidth + margin.left + margin.right)
.attr('height', availableHeight + margin.top);
- // if index is not set, default to last point
- //index = typeof index == 'undefined' ? data.length - 1 : index;
- // if index is not set, default to null
- index = typeof index == 'undefined' ? null : index;
- var hoverValue = g.selectAll('.nv-hoverValue').data([index]);
- var hoverG = hoverValue.enter().append('g').attr('class', 'nv-hoverValue');
+ function updateValueLine() { //index is currently global (within the chart), may or may not keep it that way
+ if (paused) return;
- var hoverLine = hoverG.append('line')
- .attr('x1', 0)
- .attr('y1', -margin.top)
- .attr('x2', 0)
- .attr('y2', availableHeight);
+ var hoverValue = g.selectAll('.nv-hoverValue').data(index)
- var hoverX = hoverG.append('text').attr('class', 'nv-xValue')
- .attr('x', -6)
- .attr('y', -margin.top)
- .attr('text-anchor', 'end')
- .attr('dy', '.9em');
+ var hoverEnter = hoverValue.enter()
+ .append('g').attr('class', 'nv-hoverValue')
+ .style('stroke-opacity', 0)
+ .style('fill-opacity', 0);
- var hoverY = hoverG.append('text').attr('class', 'nv-yValue')
- .attr('x', 6)
- .attr('y', -margin.top)
- .attr('text-anchor', 'start')
- .attr('dy', '.9em');
+ hoverValue.exit()
+ .transition().duration(250)
+ .style('stroke-opacity', 0)
+ .style('fill-opacity', 0)
+ .remove();
- updateValueLine();
+ hoverValue
+ .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' })
+ .transition().duration(250)
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', 1);
+ if (!index.length) return;
- function updateValueLine() { //index is currently global (within the chart), may or may not keep it that way
- if (paused) return;
+ hoverEnter.append('line')
+ .attr('x1', 0)
+ .attr('y1', -margin.top)
+ .attr('x2', 0)
+ .attr('y2', availableHeight);
- hoverValue.data([index])
- //d3.transition(hoverValue)
- hoverValue
- .transition().duration(250)
- .style('stroke-opacity', function(d) { return d === null ? 0 : 1 })
- .style('fill-opacity', function(d) { return d === null ? 0 : 1 });
+ hoverEnter.append('text').attr('class', 'nv-xValue')
+ .attr('x', -6)
+ .attr('y', -margin.top)
+ .attr('text-anchor', 'end')
+ .attr('dy', '.9em')
- if (index == null) return;
- hoverValue
- .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' })
+ g.select('.nv-hoverValue .nv-xValue')
+ .text(xTickFormat(sparkline.x()(data[index[0]], index[0])));
+
+ hoverEnter.append('text').attr('class', 'nv-yValue')
+ .attr('x', 6)
+ .attr('y', -margin.top)
+ .attr('text-anchor', 'start')
+ .attr('dy', '.9em')
- hoverValue.select('.nv-xValue')
- .text(xTickFormat(sparkline.x()(data[index], index)));
+ g.select('.nv-hoverValue .nv-yValue')
+ .text(yTickFormat(sparkline.y()(data[index[0]], index[0])));
- hoverValue.select('.nv-yValue')
- .text(yTickFormat(sparkline.y()(data[index], index)));
}
function sparklineHover() {
if (paused) return;
- var pos = d3.event.offsetX - margin.left;
+ var pos = d3.mouse(this)[0] - margin.left;
function getClosestIndex(data, x) {
var distance = Math.abs(sparkline.x()(data[0], 0) - x);
@@ -9709,7 +11450,7 @@ nv.models.sparklinePlus = function() {
return closestIndex;
}
- index = getClosestIndex(data, Math.round(x.invert(pos)));
+ index = [getClosestIndex(data, Math.round(x.invert(pos)))];
updateValueLine();
}
@@ -9727,7 +11468,7 @@ nv.models.sparklinePlus = function() {
// expose chart's sub-components
chart.sparkline = sparkline;
- d3.rebind(chart, sparkline, 'x', 'y', 'xScale', 'yScale');
+ d3.rebind(chart, sparkline, 'x', 'y', 'xScale', 'yScale', 'color');
chart.margin = function(_) {
if (!arguments.length) return margin;
@@ -9762,6 +11503,24 @@ nv.models.sparklinePlus = function() {
return chart;
};
+ chart.showValue = function(_) {
+ if (!arguments.length) return showValue;
+ showValue = _;
+ return chart;
+ };
+
+ chart.alignValue = function(_) {
+ if (!arguments.length) return alignValue;
+ alignValue = _;
+ return chart;
+ };
+
+ chart.rightAlignValue = function(_) {
+ if (!arguments.length) return rightAlignValue;
+ rightAlignValue = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -9800,7 +11559,7 @@ nv.models.stackedArea = function() {
scatter
.size(2.2) // default size
- .sizeDomain([2.2]) // all the same size by default
+ .sizeDomain([2.2,2.2]) // all the same size by default
;
/************************************
@@ -9891,7 +11650,8 @@ nv.models.stackedArea = function() {
var scatterWrap = g.select('.nv-scatterWrap')
.datum(data.filter(function(d) { return !d.disabled }))
- d3.transition(scatterWrap).call(scatter);
+ //d3.transition(scatterWrap).call(scatter);
+ scatterWrap.call(scatter);
@@ -9953,13 +11713,15 @@ nv.models.stackedArea = function() {
seriesIndex: i
});
})
- d3.transition(path.exit())
+ //d3.transition(path.exit())
+ path.exit()
.attr('d', function(d,i) { return zeroArea(d.values,i) })
.remove();
path
.style('fill', function(d,i){ return d.color || color(d, i) })
.style('stroke', function(d,i){ return d.color || color(d, i) });
- d3.transition(path)
+ //d3.transition(path)
+ path
.attr('d', function(d,i) { return area(d.values,i) })
@@ -10134,13 +11896,16 @@ nv.models.stackedAreaChart = function() {
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, yAxisTickFormat = d3.format(',.2f')
+ , state = { style: stacked.style() }
+ , defaultState = null
, noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
+ , controlWidth = 250
;
xAxis
.orient('bottom')
- .tickPadding(5)
+ .tickPadding(7)
;
yAxis
.orient('left')
@@ -10185,6 +11950,20 @@ nv.models.stackedAreaChart = function() {
chart.update = function() { chart(selection) };
chart.container = this;
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
@@ -10239,7 +12018,7 @@ nv.models.stackedAreaChart = function() {
if (showLegend) {
legend
- .width( availableWidth * 2 / 3 );
+ .width( availableWidth - controlWidth );
g.select('.nv-legendWrap')
.datum(data)
@@ -10252,7 +12031,7 @@ nv.models.stackedAreaChart = function() {
}
g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + ( availableWidth * 1 / 3 ) + ',' + (-margin.top) +')');
+ .attr('transform', 'translate(' + controlWidth + ',' + (-margin.top) +')');
}
//------------------------------------------------------------
@@ -10269,7 +12048,7 @@ nv.models.stackedAreaChart = function() {
];
controls
- .width( Math.min(280, availableWidth * 1 / 3) )
+ .width( controlWidth )
.color(['#444', '#444', '#444']);
g.select('.nv-controlsWrap')
@@ -10303,7 +12082,8 @@ nv.models.stackedAreaChart = function() {
var stackedWrap = g.select('.nv-stackedWrap')
.datum(data);
- d3.transition(stackedWrap).call(stacked);
+ //d3.transition(stackedWrap).call(stacked);
+ stackedWrap.call(stacked);
//------------------------------------------------------------
@@ -10318,7 +12098,9 @@ nv.models.stackedAreaChart = function() {
g.select('.nv-x.nv-axis')
.attr('transform', 'translate(0,' + availableHeight + ')');
- d3.transition(g.select('.nv-x.nv-axis'))
+ //d3.transition(g.select('.nv-x.nv-axis'))
+ g.select('.nv-x.nv-axis')
+ .transition().duration(0)
.call(xAxis);
yAxis
@@ -10327,7 +12109,9 @@ nv.models.stackedAreaChart = function() {
.tickSize(-availableWidth, 0)
.setTickFormat(stacked.offset() == 'expand' ? d3.format('%') : yAxisTickFormat);
- d3.transition(g.select('.nv-y.nv-axis'))
+ //d3.transition(g.select('.nv-y.nv-axis'))
+ g.select('.nv-y.nv-axis')
+ .transition().duration(0)
.call(yAxis);
//------------------------------------------------------------
@@ -10349,7 +12133,11 @@ nv.models.stackedAreaChart = function() {
return d
});
- selection.transition().call(chart);
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
+ //selection.transition().call(chart);
+ chart(selection);
});
legend.dispatch.on('legendClick', function(d,i) {
@@ -10362,7 +12150,11 @@ nv.models.stackedAreaChart = function() {
});
}
- selection.transition().call(chart);
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
+ //selection.transition().call(chart);
+ chart(selection);
});
controls.dispatch.on('legendClick', function(d,i) {
@@ -10386,13 +12178,35 @@ nv.models.stackedAreaChart = function() {
break;
}
- selection.transition().call(chart);
+ state.style = stacked.style();
+ dispatch.stateChange(state);
+
+ //selection.transition().call(chart);
+ chart(selection);
});
dispatch.on('tooltipShow', function(e) {
if (tooltips) showTooltip(e, that.parentNode);
});
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ if (typeof e.style !== 'undefined') {
+ stacked.style(e.style);
+ }
+
+ selection.call(chart);
+ });
+
});
@@ -10502,6 +12316,18 @@ nv.models.stackedAreaChart = function() {
return chart;
};
+ chart.state = function(_) {
+ if (!arguments.length) return state;
+ state = _;
+ return chart;
+ };
+
+ chart.defaultState = function(_) {
+ if (!arguments.length) return defaultState;
+ defaultState = _;
+ return chart;
+ };
+
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
@@ -10509,6 +12335,7 @@ nv.models.stackedAreaChart = function() {
};
yAxis.setTickFormat = yAxis.tickFormat;
+
yAxis.tickFormat = function(_) {
if (!arguments.length) return yAxisTickFormat;
yAxisTickFormat = _;
11 years
[rhq] Branch 'feature/cassandra-backend' - modules/core
by Jay Shaughnessy
modules/core/dbutils/pom.xml | 2
modules/core/dbutils/src/main/scripts/dbsetup/measurement-schema.xml | 16
modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml | 37 +
modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java | 239 ++++++++++
modules/core/domain/src/main/java/org/rhq/core/domain/criteria/StorageNodeCriteria.java | 99 ++++
5 files changed, 392 insertions(+), 1 deletion(-)
New commits:
commit af37e8382dea898f19939d3454a3f95737944ab1
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri May 3 17:32:55 2013 -0400
Add initial StorageNode entity support: schema/dbupgrade/entity/criteria
TODO: SLSB CRUD
diff --git a/modules/core/dbutils/pom.xml b/modules/core/dbutils/pom.xml
index 65bb69a..703950a 100644
--- a/modules/core/dbutils/pom.xml
+++ b/modules/core/dbutils/pom.xml
@@ -17,7 +17,7 @@
<description>Database schema setup, upgrade and other utilities</description>
<properties>
- <db.schema.version>2.132</db.schema.version>
+ <db.schema.version>2.133</db.schema.version>
<rhq.ds.type-mapping>${rhq.test.ds.type-mapping}</rhq.ds.type-mapping>
<rhq.ds.server-name>${rhq.test.ds.server-name}</rhq.ds.server-name>
<rhq.ds.db-name>${rhq.test.ds.db-name}</rhq.ds.db-name>
diff --git a/modules/core/dbutils/src/main/scripts/dbsetup/measurement-schema.xml b/modules/core/dbutils/src/main/scripts/dbsetup/measurement-schema.xml
index 90bd30f..c15aa1f 100644
--- a/modules/core/dbutils/src/main/scripts/dbsetup/measurement-schema.xml
+++ b/modules/core/dbutils/src/main/scripts/dbsetup/measurement-schema.xml
@@ -446,4 +446,20 @@
</constraint>
</table>
+ <table name="RHQ_STORAGE_NODE">
+ <column name="ID" default="sequence-only" initial="10001" primarykey="true" required="true" type="INTEGER" />
+ <column name="ADDRESS" required="true" size="255" type="VARCHAR2" />
+ <column name="JMX_PORT" required="true" type="INTEGER" />
+ <column name="CQL_PORT" required="true" type="INTEGER" />
+ <column name="OPERATION_MODE" required="true" size="32" type="VARCHAR2" />
+ <column name="CTIME" required="true" type="LONG" />
+ <column name="MTIME" required="true" type="LONG" />
+ <column name="RESOURCE_ID" required="false" type="INTEGER" references="RHQ_RESOURCE(ID)" />
+
+ <!-- This index is for constraint, not performance -->
+ <index name="RHQ_STORAGE_NODE_UNIQUE" unique="true">
+ <field ref="ADDRESS" />
+ </index>
+ </table>
+
</dbsetup>
\ No newline at end of file
diff --git a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
index 862944a..f9729cf 100644
--- a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
+++ b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
@@ -2037,6 +2037,43 @@
</schema-directSQL>
</schemaSpec>
+ <schemaSpec version="2.133">
+ <!-- RHQ_STORAGE_NODE -->
+ <schema-createSequence name="RHQ_STORAGE_NODE_ID_SEQ" initial="10001" />
+ <schema-directSQL>
+ <statement desc="Creating table RHQ_STORAGE_NODE">
+ CREATE TABLE RHQ_STORAGE_NODE (
+ ID INTEGER PRIMARY KEY)
+ </statement>
+ </schema-directSQL>
+ <schema-addColumn table="RHQ_STORAGE_NODE" column="ADDRESS" columnType="VARCHAR2" precision="255" />
+ <schema-alterColumn table="RHQ_STORAGE_NODE" column="ADDRESS" nullable="FALSE" />
+ <schema-addColumn table="RHQ_STORAGE_NODE" column="JMX_PORT" columnType="INTEGER" />
+ <schema-alterColumn table="RHQ_STORAGE_NODE" column="JMX_PORT" nullable="FALSE" />
+ <schema-addColumn table="RHQ_STORAGE_NODE" column="CQL_PORT" columnType="INTEGER" />
+ <schema-alterColumn table="RHQ_STORAGE_NODE" column="CQL_PORT" nullable="FALSE" />
+ <schema-addColumn table="RHQ_STORAGE_NODE" column="OPERATION_MODE" columnType="VARCHAR2" precision="32"/>
+ <schema-alterColumn table="RHQ_STORAGE_NODE" column="OPERATION_MODE" nullable="FALSE" />
+ <schema-addColumn table="RHQ_STORAGE_NODE" column="CTIME" columnType="LONG" />
+ <schema-alterColumn table="RHQ_STORAGE_NODE" column="CTIME" nullable="FALSE" />
+ <schema-addColumn table="RHQ_STORAGE_NODE" column="MTIME" columnType="LONG" />
+ <schema-alterColumn table="RHQ_STORAGE_NODE" column="MTIME" nullable="FALSE" />
+ <schema-addColumn table="RHQ_STORAGE_NODE" column="RESOURCE_ID" columnType="INTEGER" />
+ <schema-directSQL>
+ <statement desc="Creating RHQ_STORAGE_NODE foreign key to RHQ_RESOURCE">
+ ALTER TABLE RHQ_STORAGE_NODE
+ ADD CONSTRAINT RHQ_SN_RESOURCE_ID_FK
+ FOREIGN KEY (RESOURCE_ID)
+ REFERENCES RHQ_RESOURCE (ID)
+ </statement>
+ </schema-directSQL>
+ <schema-directSQL ignoreError="true">
+ <statement desc="Creating unique index on ADDRESS column">
+ CREATE UNIQUE INDEX RHQ_ADDRESS ON RHQ_STORAGE_NODE ( ADDRESS )
+ </statement>
+ </schema-directSQL>
+ </schemaSpec>
+
</dbupgrade>
</target>
</project>
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java b/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java
new file mode 100644
index 0000000..79b3ece
--- /dev/null
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java
@@ -0,0 +1,239 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2008 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.rhq.core.domain.cloud;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToOne;
+import javax.persistence.PrePersist;
+import javax.persistence.SequenceGenerator;
+import javax.persistence.Table;
+
+import org.rhq.core.domain.resource.Resource;
+
+/**
+ * An RHQ Storage Node (Cassandra)
+ *
+ * @author Jay Shaughnessy
+ */
+@Entity(name = "StorageNode")
+@NamedQueries( //
+{
+ @NamedQuery(name = StorageNode.QUERY_FIND_ALL, query = "SELECT s FROM StorageNode s"),
+ @NamedQuery(name = StorageNode.QUERY_FIND_BY_ADDRESS, query = "" //
+ + " SELECT s " //
+ + " FROM StorageNode s " //
+ + "LEFT JOIN FETCH s.resource r " //
+ + " WHERE s.address = :address"),
+ @NamedQuery(name = StorageNode.QUERY_FIND_ALL_NOT_INSTALLED, query = "SELECT s FROM StorageNode s WHERE NOT s.operationMode = 'INSTALLED'"),
+ @NamedQuery(name = StorageNode.QUERY_FIND_ALL_NORMAL, query = "SELECT s FROM StorageNode s WHERE s.operationMode = 'NORMAL'"),
+ @NamedQuery(name = StorageNode.QUERY_DELETE_BY_ID, query = "" //
+ + "DELETE FROM StorageNode s WHERE s.id = :storageNodeId ") })
+@SequenceGenerator(allocationSize = org.rhq.core.domain.util.Constants.ALLOCATION_SIZE, name = "RHQ_STORAGE_NODE_ID_SEQ", sequenceName = "RHQ_STORAGE_NODE_ID_SEQ")
+@Table(name = "RHQ_STORAGE_NODE")
+public class StorageNode implements Serializable {
+
+ public static final long serialVersionUID = 1L;
+
+ public static final String QUERY_FIND_ALL = "StorageNode.findAll";
+ public static final String QUERY_FIND_BY_ADDRESS = "StorageNode.findByName";
+ public static final String QUERY_FIND_ALL_NOT_INSTALLED = "StorageNode.findAllCloudMembers";
+ public static final String QUERY_DELETE_BY_ID = "StorageNode.deleteById";
+ public static final String QUERY_FIND_ALL_NORMAL = "StorageNode.findAllNormalCloudMembers";
+
+ @Column(name = "ID", nullable = false)
+ @GeneratedValue(strategy = GenerationType.AUTO, generator = "RHQ_STORAGE_NODE_ID_SEQ")
+ @Id
+ private int id;
+
+ @Column(name = "ADDRESS", nullable = false)
+ private String address;
+
+ @Column(name = "JMX_PORT", nullable = false)
+ private int jmxPort;
+
+ @Column(name = "CQL_PORT", nullable = false)
+ private int cqlPort;
+
+ @Column(name = "OPERATION_MODE", nullable = false)
+ @Enumerated(EnumType.STRING)
+ private OperationMode operationMode;
+
+ // the time this storage node was installed into the infrastructure
+ @Column(name = "CTIME", nullable = false)
+ private long ctime;
+
+ // the time this storage node was last updated
+ @Column(name = "MTIME", nullable = false)
+ private long mtime;
+
+ @JoinColumn(name = "RESOURCE_ID", referencedColumnName = "ID", nullable = true)
+ @OneToOne(fetch = FetchType.LAZY, optional = true)
+ private Resource resource;
+
+ // required for JPA
+ public StorageNode() {
+ }
+
+ public StorageNode(int storageNodeId) {
+ this.id = storageNodeId;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public int getJmxPort() {
+ return jmxPort;
+ }
+
+ public void setJmxPort(int jmxPort) {
+ this.jmxPort = jmxPort;
+ }
+
+ public int getCqlPort() {
+ return cqlPort;
+ }
+
+ public void setCqlPort(int cqlPort) {
+ this.cqlPort = cqlPort;
+ }
+
+ public long getCtime() {
+ return ctime;
+ }
+
+ public long getMtime() {
+ return mtime;
+ }
+
+ public void setMtime(long mtime) {
+ this.mtime = mtime;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public void setResource(Resource resource) {
+ this.resource = resource;
+ }
+
+ public OperationMode getOperationMode() {
+ return operationMode;
+ }
+
+ public void setOperationMode(OperationMode operationMode) {
+ this.operationMode = operationMode;
+ }
+
+ public enum OperationMode {
+
+ DOWN("This storage node is down"), //
+ INSTALLED("This storage node is newly installed but not yet operationial"), //
+ MAINTENANCE("This storage node is in maintenance mode"), //
+ NORMAL("This storage node is running normally");
+
+ public final String message;
+
+ private OperationMode(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StorageNode [id=" + id + ", address=" + address + ", jmxPort=" + jmxPort + ", cqlPort=" + cqlPort
+ + ", operationMode=" + operationMode + ", mtime=" + mtime + "]";
+ }
+
+ @PrePersist
+ void onPersist() {
+ this.ctime = System.currentTimeMillis();
+ this.mtime = this.ctime;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (ctime ^ (ctime >>> 32));
+ result = prime * result + ((address == null) ? 0 : address.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || !(obj instanceof StorageNode)) {
+ return false;
+ }
+
+ final StorageNode other = (StorageNode) obj;
+
+ if (ctime != other.ctime) {
+ return false;
+ }
+
+ if (address == null) {
+ if (other.address != null) {
+ return false;
+ }
+ } else if (!address.equals(other.address)) {
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/StorageNodeCriteria.java b/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/StorageNodeCriteria.java
new file mode 100644
index 0000000..18ae492
--- /dev/null
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/StorageNodeCriteria.java
@@ -0,0 +1,99 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2012 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.core.domain.criteria;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+
+import org.rhq.core.domain.cloud.StorageNode;
+import org.rhq.core.domain.cloud.StorageNode.OperationMode;
+import org.rhq.core.domain.util.PageOrdering;
+
+/**
+ * Criteria object for querying {@link StorageNode}s.
+ * Only subject with MANAGE_SETTINGS can fetch these instances.
+ *
+ * @author Jay Shaughnessy
+ */
+(a)XmlAccessorType(XmlAccessType.FIELD)
+@SuppressWarnings("unused")
+public class StorageNodeCriteria extends Criteria {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String SORT_FIELD_CTIME = "ctime";
+
+ public static final String SORT_FIELD_ADDRESS = "address";
+
+ private String filterAddress;
+ private Integer filterJmxPort;
+ private Integer filterCqlPort;
+ private List<OperationMode> filterOperationMode; // requires override
+ private Integer filterResourceId; // requires override
+
+ private boolean fetchResource;
+
+ private PageOrdering sortCtime;
+ private PageOrdering sortAddress;
+
+ public StorageNodeCriteria() {
+ filterOverrides.put("operationMode", "operationMode IN ( ? )");
+ filterOverrides.put("resourceId", "resource.id = ?");
+ }
+
+ public Class<?> getPersistentClass() {
+ return StorageNode.class;
+ }
+
+ public void addFilterAddress(String filterAddress) {
+ this.filterAddress = filterAddress;
+ }
+
+ public void addFilterJmxPort(Integer filterJmxPort) {
+ this.filterJmxPort = filterJmxPort;
+ }
+
+ public void addFilterCqlPort(Integer filterCqlPort) {
+ this.filterCqlPort = filterCqlPort;
+ }
+
+ public void addFilterOperationMode(OperationMode... operationMode) {
+ if (operationMode != null && operationMode.length > 0) {
+ this.filterOperationMode = Arrays.asList(operationMode);
+ }
+ }
+
+ public void addFilterResourceId(Integer filterResourceId) {
+ this.filterResourceId = filterResourceId;
+ }
+
+ public void addSortCtime(PageOrdering sort) {
+ addSortField(SORT_FIELD_CTIME);
+ this.sortCtime = sort;
+ }
+
+ public void addSortAddress(PageOrdering sort) {
+ addSortField(SORT_FIELD_ADDRESS);
+ this.sortAddress = sort;
+ }
+}
11 years
[rhq] modules/enterprise
by mike thompson
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java | 1 +
1 file changed, 1 insertion(+)
New commits:
commit d81ef0a6ec22ddc6ce32eeb2a47754a6f9abb01b
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Fri May 3 13:42:29 2013 -0700
[BZ 958938] - Dashboard graphs need refresh when switching View/Edit portlet modes.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java
index 779c795..9c310ca 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java
@@ -784,6 +784,7 @@ public class DashboardView extends EnhancedVLayout {
}
this.editForm.markForRedraw();
this.portalLayout.show();
+ redraw();
this.portalLayout.markForRedraw();
}
11 years