* @author konigsberg@google.com (Robert Konigsberg)
*/
var jstestdriver = {
- jQuery : jQuery
+ jQuery : jQuery,
+ listeners_ : [],
+ announce_ : function(name, args) {
+ for (var idx = 0; idx < jstestdriver.listeners_.length; idx++) {
+ var listener = jstestdriver.listeners_[idx];
+ if (listener[name]) {
+ listener[name].apply(null, args);
+ }
+ }
+ },
+ attachListener: function(listener) {
+ jstestdriver.listeners_.push(listener);
+ }
};
if (!console) {
testCase.prototype.setUp = function() { };
testCase.prototype.tearDown = function() { };
+ testCase.prototype.name = name;
/**
* name can be a string, which is looked up in this object, or it can be a
* function, in which case it's run.
* Chrome's console completion.
*/
testCase.prototype.runTest = function(func) {
+ var result = false;
+ var ex = null;
+ var name = typeof(func) == "string" ? func : "(anonymous function)";
+ jstestdriver.announce_("start", [this, name]);
try {
- this.setUp();
-
- var fn = null;
- var parameterType = typeof(func);
- if (typeof(func) == "function") {
- fn = func;
- } else if (typeof(func) == "string") {
- fn = this[func];
- } else {
- fail("can't supply " + typeof(func) + " to runTest");
- }
-
- fn.apply(this, []);
- this.tearDown();
- return true;
+ result = this.runTest_(func);
} catch (e) {
- console.log(e);
- if (e.stack) {
- console.log(e.stack);
- }
- return false;
+ ex = e;
}
+ jstestdriver.announce_("finish", [this, name, result, ex]);
+ }
+
+ testCase.prototype.runTest_ = function(func) {
+ this.setUp();
+
+ var fn = null;
+ var parameterType = typeof(func);
+ if (typeof(func) == "function") {
+ fn = func;
+ } else if (typeof(func) == "string") {
+ fn = this[func];
+ } else {
+ fail("can't supply " + typeof(func) + " to runTest");
+ }
+
+ fn.apply(this, []);
+ this.tearDown();
+ return true;
};
testCase.prototype.runAllTests = function() {
function getAllTestCases() {
return testCaseList;
}
+
+jstestdriver.attachListener({
+ finish : function(tc, name, result, e) {
+ if (e) {
+ console.log(e);
+ if (e.stack) {
+ console.log(e.stack);
+ }
+ }
+ }
+});
+
<script type="text/javascript" src="../tests/DygraphOps.js"></script>
<script type="text/javascript" src="../tests/PixelSampler.js"></script>
<script type="text/javascript" src="../tests/Util.js"></script>
+ <script type="text/javascript" src="local.js"></script>
<!-- Scripts for automated tests -->
<script type="text/javascript" src="../tests/annotations.js"></script>
<script type="text/javascript" src="../tests/axis_labels.js"></script>
<script type="text/javascript" src="../tests/axis_labels-deprecated.js"></script>
<script type="text/javascript" src="../tests/callback.js"></script>
+ <script type="text/javascript" src="../tests/connect_separated_points.js"></script>
<script type="text/javascript" src="../tests/css.js"></script>
<script type="text/javascript" src="../tests/custom_bars.js"></script>
<script type="text/javascript" src="../tests/date_formats.js"></script>
+ <script type="text/javascript" src="../tests/dygraph-options-tests.js"></script>
<script type="text/javascript" src="../tests/error_bars.js"></script>
<script type="text/javascript" src="../tests/formats.js"></script>
+ <script type="text/javascript" src="../tests/grid_per_axis.js"></script>
<script type="text/javascript" src="../tests/interaction_model.js"></script>
<script type="text/javascript" src="../tests/missing_points.js"></script>
<script type="text/javascript" src="../tests/multi_csv.js"></script>
<script type="text/javascript" src="../tests/multiple_axes.js"></script>
<script type="text/javascript" src="../tests/multiple_axes-old.js"></script>
- <script type="text/javascript" src="../tests/numeric-labels.js"></script>
<script type="text/javascript" src="../tests/no_hours.js"></script>
<script type="text/javascript" src="../tests/parser.js"></script>
<script type="text/javascript" src="../tests/pathological_cases.js"></script>
text-decoration: none;
}
</style>
- <script type="text/javascript">
-
- // save Dygraph.warn so we can catch warnings.
- if (false) { // Set true if you want warnings to cause failures.
- var originalDygraphWarn = Dygraph.warn;
- Dygraph.warn = function(msg) {
- if (msg == "Using default labels. Set labels explicitly via 'labels' in the options parameter") {
- originalDygraphWarn(msg);
- return;
- }
- throw "Warnings not permitted: " + msg;
- }
- Dygraph.prototype.warn = Dygraph.warn;
- }
-
- var tc = null; // Selected test case
- var name = null;
-
- var resultDiv = null;
-
- function processVariables() {
- var splitVariables = function() { // http://www.idealog.us/2006/06/javascript_to_p.html
- var query = window.location.search.substring(1);
- var args = {};
- var vars = query.split("&");
- for (var i = 0; i < vars.length; i++) {
- if (vars[i].length > 0) {
- var pair = vars[i].split("=");
- args[pair[0]] = pair[1];
- }
- }
- return args;
- }
-
- var args = splitVariables();
- var test = args.test;
- var command = args.command;
-
- // args.testCaseName uses the string name of the test.
- if (args.testCaseName) {
- var testCases = getAllTestCases();
- name = args.testCaseName;
- for (var idx in testCases) {
- var entry = testCases[idx];
- if (entry.name == args.testCaseName) {
- var prototype = entry.testCase;
- tc = new entry.testCase();
- break;
- }
- }
- } else if (args.testCase) { // The class name of the test.
- name = args.testCase;
- eval("tc = new " + args.testCase + "()");
- }
-
- var results = null;
- // If the test class is defined.
- if (tc != null) {
- if (args.command == "runAllTests") {
- console.log("Running all tests for " + args.testCase);
- results = tc.runAllTests();
- } else if (args.command == "runTest") {
- console.log("Running test " + args.testCase + "." + args.test);
- results = tc.runTest(args.test);
- }
- } else {
- if (args.command == "runAllTests") {
- console.log("Running all tests for all test cases");
- var testCases = getAllTestCases();
- results = {};
- for (var idx in testCases) {
- var entry = testCases[idx];
- var prototype = entry.testCase;
- tc = new entry.testCase();
- results[entry.name] = tc.runAllTests();
- }
- }
- }
- resultsDiv = createResultsDiv();
- var summary = { failed: 0, passed: 0 };
- postResults(results, summary);
- resultsDiv.appendChild(document.createElement("hr"));
- document.getElementById('summary').innerHTML = "(" + summary.failed + " failed, " + summary.passed + " passed)";
- }
-
- function createResultsDiv() {
- var body = document.getElementsByTagName("body")[0];
- div = document.createElement("div");
- div.id='results';
- div.innerHTML = "Test results: <span id='summary'></span> <a href='#' id='passed'>passed</a> <a href='#' id='failed'>failed</a> <a href='#' id='all'>all</a><br/>";
- body.insertBefore(div, body.firstChild);
-
- var setByClassName = function(name, displayStyle) {
- var elements = div.getElementsByClassName(name);
- for (var i = 0; i < elements.length; i++) {
- elements[i].style.display = displayStyle;
- }
- }
-
- var passedAnchor = document.getElementById('passed');
- var failedAnchor = document.getElementById('failed');
- var allAnchor = document.getElementById('all');
- passedAnchor.onclick = function() {
- setByClassName('fail', 'none');
- setByClassName('pass', 'block');
-
- passedAnchor.setAttribute("class", 'activeAnchor');
- failedAnchor.setAttribute("class", '');
- };
- failedAnchor.onclick = function() {
- setByClassName('fail', 'block');
- setByClassName('pass', 'none');
- passedAnchor.setAttribute("class", '');
- failedAnchor.setAttribute("class", 'activeAnchor');
- };
- allAnchor.onclick = function() {
- setByClassName('fail', 'block');
- setByClassName('pass', 'block');
- passedAnchor.setAttribute("class", '');
- failedAnchor.setAttribute("class", '');
- };
- return div;
- }
-
- function postResults(results, summary, title) {
- if (typeof(results) == "boolean") {
- var elem = document.createElement("div");
- elem.setAttribute("class", results ? 'pass' : 'fail');
-
- var prefix = title ? (title + ": ") : "";
- elem.innerHTML = prefix + '<span class=\'outcome\'>' + (results ? 'pass' : 'fail') + '</span>';
- resultsDiv.appendChild(elem);
- if (results) {
- summary.passed++;
- } else {
- summary.failed++;
- }
- } else { // hash
- var failed = 0;
- var html = "";
- for (var key in results) {
- if (results.hasOwnProperty(key)) {
- var elem = results[key];
- if (typeof(elem) == "boolean" && title) {
- postResults(results[key], summary, title + "." + key);
- } else {
- postResults(results[key], summary, key);
- }
- }
- }
- }
- }
-
- </script>
</head>
<body>
<div id='graph'></div>
Example: <code>local.html?testCase=ScrollingDivTestCase&test=testNestedDiv_Scrolled&command=runTest</code>
<p/>
</body>
-<script>
-processVariables();
-addGlobalTestSymbols();
-
-var selector = document.getElementById("selector");
-
-if (selector != null) { // running a test
- var createAttached = function(name, parent) {
- var elem = document.createElement(name);
- parent.appendChild(elem);
- return elem;
- }
-
- var description = createAttached("div", selector);
- var list = createAttached("ul", selector);
- var parent = list.parentElement;
- var createLink = function(parent, text, url) {
- var li = createAttached("li", parent);
- var a = createAttached("a", li);
- a.innerHTML = text;
- a.href = url;
- }
- if (tc == null) {
- description.innerHTML = "Test cases:";
- var testCases = getAllTestCases();
- createLink(list, "(run all tests)", document.URL + "?command=runAllTests");
- for (var idx in testCases) {
- var entryName = testCases[idx].name;
- createLink(list, entryName, document.URL + "?testCaseName=" + entryName);
- }
- } else {
- description.innerHTML = "Tests for " + name;
- var names = tc.getTestNames();
- createLink(list, "Run All Tests", document.URL + "&command=runAllTests");
- for (var idx in names) {
- var name = names[idx];
- createLink(list, name, document.URL + "&test=" + name + "&command=runTest");
- }
- }
-}
+<script type="text/javascript">
+ var tester = new DygraphsLocalTester();
+ // tester.overrideWarn(); // uncomment if you want warnings to be errors.
+ tester.processVariables();
+ addGlobalTestSymbols();
+ tester.run();
</script>
</html>
--- /dev/null
+var DygraphsLocalTester = function() {
+ this.tc = null; // Selected test case
+ this.name = null;
+ this.resultsDiv = null;
+ this.results = [];
+ this.summary = { failed: 0, passed: 0 };
+
+ var self = this;
+ jstestdriver.attachListener({
+ start : function(tc) {
+ self.start_(tc);
+ },
+ finish : function(tc, name, result, e) {
+ self.finish_(tc, name, result, e);
+ }
+ });
+};
+
+/**
+ * Call this to replace Dygraphs.warn so it throws an error.
+ *
+ * In some cases we will still allow warnings to be warnings, however.
+ */
+DygraphsLocalTester.prototype.overrideWarn = function() {
+ // save Dygraph.warn so we can catch warnings.
+ var originalDygraphWarn = Dygraph.warn;
+ Dygraph.warn = function(msg) {
+ // This warning is still
+ if (msg == "Using default labels. Set labels explicitly via 'labels' in the options parameter") {
+ originalDygraphWarn(msg);
+ return;
+ }
+ throw "Warnings not permitted: " + msg;
+ }
+ Dygraph.prototype.warn = Dygraph.warn;
+};
+
+DygraphsLocalTester.prototype.processVariables = function() {
+ var splitVariables = function() { // http://www.idealog.us/2006/06/javascript_to_p.html
+ var query = window.location.search.substring(1);
+ var args = {};
+ var vars = query.split("&");
+ for (var i = 0; i < vars.length; i++) {
+ if (vars[i].length > 0) {
+ var pair = vars[i].split("=");
+ args[pair[0]] = pair[1];
+ }
+ }
+ return args;
+ }
+
+ var args = splitVariables();
+ var test = args.test;
+ var command = args.command;
+
+ // args.testCaseName uses the string name of the test.
+ if (args.testCaseName) {
+ var testCases = getAllTestCases();
+ name = args.testCaseName;
+ for (var idx in testCases) {
+ var entry = testCases[idx];
+ if (entry.name == args.testCaseName) {
+ var prototype = entry.testCase;
+ this.tc = new entry.testCase();
+ break;
+ }
+ }
+ } else if (args.testCase) { // The class name of the test.
+ name = args.testCase;
+ eval("tc__= new " + args.testCase + "()");
+ this.tc = tc_;
+ }
+
+ // If the test class is defined.
+ if (this.tc != null) {
+ if (args.command == "runAllTests") {
+ console.log("Running all tests for " + args.testCase);
+ this.tc.runAllTests();
+ } else if (args.command == "runTest") {
+ console.log("Running test " + args.testCase + "." + args.test);
+ this.tc.runTest(args.test);
+ }
+ } else {
+ if (args.command == "runAllTests") {
+ console.log("Running all tests for all test cases");
+ var testCases = getAllTestCases();
+ for (var idx in testCases) {
+ var entry = testCases[idx];
+ var prototype = entry.testCase;
+ this.tc = new entry.testCase();
+ this.tc.runAllTests();
+ }
+ }
+ }
+ this.resultsDiv = this.createResultsDiv();
+ this.postResults();
+ this.resultsDiv.appendChild(document.createElement("hr"));
+ document.getElementById('summary').innerHTML = "(" + this.summary.failed + " failed, " + this.summary.passed + " passed)";
+}
+
+DygraphsLocalTester.prototype.createResultsDiv = function() {
+ div = document.createElement("div");
+ div.id='results';
+ div.innerHTML = "Test results: <span id='summary'></span> <a href='#' id='passed'>passed</a> <a href='#' id='failed'>failed</a> <a href='#' id='all'>all</a><br/>";
+
+ var body = document.getElementsByTagName("body")[0];
+ body.insertBefore(div, body.firstChild);
+
+ var setByClassName = function(name, displayStyle) {
+ var elements = div.getElementsByClassName(name);
+ for (var i = 0; i < elements.length; i++) {
+ elements[i].style.display = displayStyle;
+ }
+ }
+
+ var passedAnchor = document.getElementById('passed');
+ var failedAnchor = document.getElementById('failed');
+ var allAnchor = document.getElementById('all');
+ passedAnchor.onclick = function() {
+ setByClassName('fail', 'none');
+ setByClassName('pass', 'block');
+
+ passedAnchor.setAttribute("class", 'activeAnchor');
+ failedAnchor.setAttribute("class", '');
+ };
+ failedAnchor.onclick = function() {
+ setByClassName('fail', 'block');
+ setByClassName('pass', 'none');
+ passedAnchor.setAttribute("class", '');
+ failedAnchor.setAttribute("class", 'activeAnchor');
+ };
+ allAnchor.onclick = function() {
+ setByClassName('fail', 'block');
+ setByClassName('pass', 'block');
+ passedAnchor.setAttribute("class", '');
+ failedAnchor.setAttribute("class", '');
+ };
+ return div;
+}
+
+DygraphsLocalTester.prototype.postResults = function() {
+ var table = document.createElement("table");
+ this.resultsDiv.appendChild(table);
+ for (var idx = 0; idx < this.results.length; idx++) {
+ var result = this.results[idx];
+ var tr = document.createElement("tr");
+ tr.setAttribute("class", result.result ? 'pass' : 'fail');
+
+ var tdResult = document.createElement("td");
+ tdResult.setAttribute("class", "outcome");
+ tdResult.innerText = result.result ? 'pass' : 'fail';
+ tr.appendChild(tdResult);
+
+ var tdName = document.createElement("td");
+ tdName.innerText = result.name;
+ tr.appendChild(tdName);
+
+ var tdDuration = document.createElement("td");
+ tdDuration.innerText = result.duration;
+ tr.appendChild(tdDuration);
+
+ table.appendChild(tr);
+ }
+}
+
+DygraphsLocalTester.prototype.run = function() {
+ var selector = document.getElementById("selector");
+
+ if (selector != null) { // running a test
+ var createAttached = function(name, parent) {
+ var elem = document.createElement(name);
+ parent.appendChild(elem);
+ return elem;
+ }
+
+ var description = createAttached("div", selector);
+ var list = createAttached("ul", selector);
+ var parent = list.parentElement;
+ var createLink = function(parent, text, url) {
+ var li = createAttached("li", parent);
+ var a = createAttached("a", li);
+ a.innerHTML = text;
+ a.href = url;
+ }
+ if (this.tc == null) {
+ description.innerHTML = "Test cases:";
+ var testCases = getAllTestCases();
+ createLink(list, "(run all tests)", document.URL + "?command=runAllTests");
+ for (var idx in testCases) {
+ var entryName = testCases[idx].name;
+ createLink(list, entryName, document.URL + "?testCaseName=" + entryName);
+ }
+ } else {
+ description.innerHTML = "Tests for " + name;
+ var names = this.tc.getTestNames();
+ createLink(list, "Run All Tests", document.URL + "&command=runAllTests");
+ for (var idx in names) {
+ var name = names[idx];
+ createLink(list, name, document.URL + "&test=" + name + "&command=runTest");
+ }
+ }
+ }
+}
+
+DygraphsLocalTester.prototype.start_ = function(tc) {
+ this.startms_ = new Date().getTime();
+}
+
+DygraphsLocalTester.prototype.finish_ = function(tc, name, result, e) {
+ var endms_ = new Date().getTime();
+ this.results.push({
+ name : tc.name + "." + name,
+ result : result,
+ duration : endms_ - this.startms_
+ });
+ this.summary.passed += result ? 1 : 0;
+ this.summary.failed += result ? 0 : 1;
+}
assertEquals(0, yMin);
assertEquals(10, yMax);
};
+
+/**
+ * Test that drawPointCallback is called for isolated points and correct idx for the point is returned.
+ */
+CallbackTestCase.prototype.testDrawPointCallback_idx = function() {
+ var indices = [];
+
+ var g;
+ var callback = function(g, seriesName, canvasContext, cx, cy, color, pointSizeParam,idx) {
+ indices.push(idx);
+ Dygraph.Circles.DEFAULT.apply(this, arguments);
+ };
+
+ var graph = document.getElementById("graph");
+
+ var testdata = [[10, 2], [11, 3], [12, NaN], [13, 2], [14, NaN], [15, 3]];
+ var graphOpts = {
+ labels: ['X', 'Y'],
+ valueRange: [0, 4],
+ drawPoints : false,
+ drawPointCallback : callback,
+ pointSize : 8
+ };
+
+ // Test that correct idx for isolated points are passed to the callback.
+ g = new Dygraph(graph, testdata, graphOpts);
+ assertEquals(2, indices.length);
+ assertEquals([3, 5],indices);
+
+ // Test that correct indices for isolated points + gap points are passed to the callback when
+ // drawGapEdgePoints is set. This should add one point at the right
+ // edge of the segment at x=11, but not at the graph edge at x=10.
+ indices = []; // Reset for new test
+ graphOpts.drawGapEdgePoints = true;
+ g = new Dygraph(graph, testdata, graphOpts);
+ assertEquals(3, indices.length);
+ assertEquals([1, 3, 5],indices);
+
+
+ //Test that correct indices are passed to the callback when zoomed in.
+ indices = []; // Reset for new test
+ graphOpts.dateWindow = [12.5,13.5]
+ graphOpts.drawPoints = true;
+ testdata = [[10, 2], [11, 3], [12, 4], [13, 2], [14, 5], [15, 3]];
+ g = new Dygraph(graph, testdata, graphOpts);
+ assertEquals(3, indices.length);
+ assertEquals([2, 3, 4],indices);
+};
+
+/**
+ * Test that the correct idx is returned for the point in the onHiglightCallback.
+ */
+CallbackTestCase.prototype.testDrawHighlightPointCallback_idx = function() {
+ var idxToCheck = null;
+
+ var drawHighlightPointCallback = function(g, seriesName, canvasContext, cx, cy, color, pointSizeParam,idx) {
+ idxToCheck = idx;
+ };
+ var testdata = [[1, 2], [2, 3], [3, NaN], [4, 2], [5, NaN], [6, 3]];
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, testdata,
+ {
+ drawHighlightPointCallback : drawHighlightPointCallback
+ });
+
+ assertNull(idxToCheck);
+ DygraphOps.dispatchMouseMove(g, 3, 0);
+ // check that NaN point is not highlighted
+ assertNull(idxToCheck);
+ DygraphOps.dispatchMouseMove(g, 1, 2);
+ // check that correct index is returned
+ assertEquals(0,idxToCheck);
+ DygraphOps.dispatchMouseMove(g, 6, 3);
+ assertEquals(5,idxToCheck);
+};
\ No newline at end of file
--- /dev/null
+/**
+ * @fileoverview Test cases for the option "connectSeparatedPoints" especially for the scenario where not every series has a value for each timestamp.
+ *
+ * @author julian.eichstaedt@ch.sauter-bc.com (Fr. Sauter AG)
+ */
+var ConnectSeparatedPointsTestCase = TestCase("connect-separated-points");
+
+ConnectSeparatedPointsTestCase.prototype.setUp = function() {
+ document.body.innerHTML = "<div id='graph'></div>";
+};
+
+ConnectSeparatedPointsTestCase.origFunc = Dygraph.getContext;
+
+ConnectSeparatedPointsTestCase.prototype.setUp = function() {
+ document.body.innerHTML = "<div id='graph'></div>";
+ Dygraph.getContext = function(canvas) {
+ return new Proxy(ConnectSeparatedPointsTestCase.origFunc(canvas));
+ };
+};
+
+ConnectSeparatedPointsTestCase.prototype.tearDown = function() {
+ Dygraph.getContext = ConnectSeparatedPointsTestCase.origFunc;
+};
+
+ConnectSeparatedPointsTestCase.prototype.testEdgePointsSimple = function() {
+ var opts = {
+ width: 480,
+ height: 320,
+ labels: ["x", "series1", "series2", "additionalSeries"],
+ connectSeparatedPoints: true,
+ dateWindow: [2.5,7.5]
+ };
+
+ var data = [
+ [0,-1,0,null],
+ [1,null,2,null],
+ [2,null,4,null],
+ [3,0.5,0,null],
+ [4,1,-1,5],
+ [5,2,-2,6],
+ [6,2.5,-2.5,7],
+ [7,3,-3,null],
+ [8,4,null,null],
+ [9,4,-10,null]
+ ];
+
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+
+ htx = g.hidden_ctx_;
+
+ var attrs = {};
+
+ // Test if series1 is drawn correctly.
+ // ------------------------------------
+
+ // The first point of the first series
+ var x1 = data[0][0];
+ var y1 = data[0][1];
+ var xy1 = g.toDomCoords(x1, y1);
+
+ // The next valid point of this series
+ var x2 = data[3][0];
+ var y2 = data[3][1];
+ var xy2 = g.toDomCoords(x2, y2);
+
+ // Check if both points are connected at the left edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // Test if series2 is drawn correctly.
+ // ------------------------------------
+
+ // The last point of the second series.
+ var x2 = data[9][0];
+ var y2 = data[9][2];
+ var xy2 = g.toDomCoords(x2, y2);
+
+ // The previous valid point of this series
+ var x1 = data[7][0];
+ var y1 = data[7][2];
+ var xy1 = g.toDomCoords(x1, y1);
+
+ // Check if both points are connected at the right edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+};
+
+ConnectSeparatedPointsTestCase.prototype.testEdgePointsCustomBars = function() {
+ var opts = {
+ width: 480,
+ height: 320,
+ labels: ["x", "series1", "series2", "additionalSeries"],
+ connectSeparatedPoints: true,
+ dateWindow: [2.5,7.5],
+ customBars: true
+ };
+
+ var data = [
+ [0,[4,5,6], [1,2,3], [null, null, null]],
+ [1,[null,null,null], [2,3,4], [null, null, null]],
+ [2,[null,null,null], [3,4,5], [null, null, null]],
+ [3,[0,1,2], [2,3,4], [null, null, null]],
+ [4,[1,2,3], [2,3,4], [4, 5, 6]],
+ [5,[1,2,3], [3,4,5], [4, 5, 6]],
+ [6,[0,1,2], [4,5,6], [5, 6, 7]],
+ [7,[0,1,2], [4,5,6], [null, null, null]],
+ [8,[2,3,4], [null,null,null], [null, null, null]],
+ [9,[0,1,2], [2,4,9], [null, null, null]]
+
+ ];
+
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+
+ htx = g.hidden_ctx_;
+
+ var attrs = {};
+
+
+ // Test if values of the series1 are drawn correctly.
+ // ------------------------------------
+
+ // The first point of the first series
+ var x1 = data[0][0];
+ var y1 = data[0][1][1];
+ var xy1 = g.toDomCoords(x1, y1);
+
+ // The next valid point of this series
+ var x2 = data[3][0];
+ var y2 = data[3][1][1];
+ var xy2 = g.toDomCoords(x2, y2);
+
+ // Check if both points are connected at the left edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // Test if the custom bars of the series1 are drawn correctly
+ // --------------------------------------------
+
+ // The first min-point of this series
+ x1 = data[0][0];
+ y1 = data[0][1][0];
+ xy1 = g.toDomCoords(x1, y1);
+
+ // The next valid min-point of the second series.
+ x2 = data[3][0];
+ y2 = data[3][1][0];
+ xy2 = g.toDomCoords(x2, y2);
+
+ // Check if both points are connected at the left edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // The first max-point of this series
+ x1 = data[0][0];
+ y1 = data[0][1][2];
+ xy1 = g.toDomCoords(x1, y1);
+
+ // The next valid max-point of the second series.
+ x2 = data[3][0];
+ y2 = data[3][1][2];
+ xy2 = g.toDomCoords(x2, y2);
+
+ // Check if both points are connected at the left edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // Test if values of the series2 are drawn correctly.
+ // ------------------------------------
+
+ // The last point of the second series.
+ var x2 = data[9][0];
+ var y2 = data[9][2][1];
+ var xy2 = g.toDomCoords(x2, y2);
+
+ // The previous valid point of this series
+ var x1 = data[7][0];
+ var y1 = data[7][2][1];
+ var xy1 = g.toDomCoords(x1, y1);
+
+ // Check if both points are connected at the right edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // Test if the custom bars of the series2 are drawn correctly
+ // --------------------------------------------
+
+ // The last min-point of the second series.
+ x2 = data[9][0];
+ y2 = data[9][2][0];
+ xy2 = g.toDomCoords(x2, y2);
+
+ // The previous valid min-point of this series
+ x1 = data[7][0];
+ y1 = data[7][2][0];
+ xy1 = g.toDomCoords(x1, y1);
+
+ // Check if both points are connected at the right edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // The last max-point of the second series.
+ x2 = data[9][0];
+ y2 = data[9][2][2];
+ xy2 = g.toDomCoords(x2, y2);
+
+ // The previous valid max-point of this series
+ x1 = data[7][0];
+ y1 = data[7][2][2];
+ xy1 = g.toDomCoords(x1, y1);
+
+ // Check if both points are connected at the right edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+};
+
+ConnectSeparatedPointsTestCase.prototype.testEdgePointsErrorBars = function() {
+ var opts = {
+ width: 480,
+ height: 320,
+ labels: ["x", "series1", "series2", "seriesTestHelper"],
+ connectSeparatedPoints: true,
+ dateWindow: [2,7.5],
+ errorBars: true
+
+ };
+
+ var data = [
+ [0,[5,1], [2,1], [null,null]],
+ [1,[null,null], [3,1], [null,null]],
+ [2,[null,null], [4,1], [null,null]],
+ [3,[1,1], [3,1], [null,null]],
+ [4,[2,1], [3,1], [5,1]],
+ [5,[2,1], [4,1], [5,1]],
+ [6,[1,1], [5,1], [6,1]],
+ [7,[1,1], [5,1], [null,null]],
+ [8,[3,1], [null,null], [null,null]],
+ [9,[1,1], [4,1], [null,null]]
+
+ ];
+
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+
+ htx = g.hidden_ctx_;
+
+ var attrs = {};
+
+
+ // Test if values of the series1 are drawn correctly.
+ // ------------------------------------
+
+ // The first point of the first series
+ var x1 = data[0][0];
+ var y1 = data[0][1][0];
+ var xy1 = g.toDomCoords(x1, y1);
+
+ // The next valid point of this series
+ var x2 = data[3][0];
+ var y2 = data[3][1][0];
+ var xy2 = g.toDomCoords(x2, y2);
+
+ // Check if both points are connected at the left edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // Test if the upper error bars of series1 are drawn correctly
+ // --------------------------------------------
+
+ // The first upper error-point of this series
+ x1 = data[0][0];
+ var y1error = y1 + (data[0][1][1]*2);
+ xy1 = g.toDomCoords(x1, y1error);
+
+ // The next valid upper error-point of the second series.
+ x2 = data[3][0];
+ var y2error = y2 + (data[3][1][1]*2);
+ xy2 = g.toDomCoords(x2, y2error);
+
+ // Check if both points are connected at the left edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // Test if the lower error bars of series1 are drawn correctly
+ // --------------------------------------------
+
+ // The first lower error-point of this series
+ x1 = data[0][0];
+ y1error = y1 - (data[0][1][1]*2);
+ xy1 = g.toDomCoords(x1, y1error);
+
+ //The next valid lower error-point of the second series.
+ x2 = data[3][0];
+ y2error = y2 - (data[3][1][1]*2);
+ xy2 = g.toDomCoords(x2, y2error);
+
+ // Check if both points are connected at the left edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+
+ // Test if values of the series2 are drawn correctly.
+ // ------------------------------------
+
+ // The last point of this series
+ x2 = data[9][0];
+ y2 = data[9][2][0];
+ xy2 = g.toDomCoords(x2, y2);
+
+ // The previous valid point of the first series
+ x1 = data[7][0];
+ y1 = data[7][2][0];
+ xy1 = g.toDomCoords(x1, y1);
+
+ // Check if both points are connected at the right edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // Test if the upper error bars of series2 are drawn correctly
+ // --------------------------------------------
+
+ // The last upper error-point of the second series.
+ x2 = data[9][0];
+ var y2error = y2 + (data[9][2][1]*2);
+ xy2 = g.toDomCoords(x2, y2error);
+
+ // The previous valid upper error-point of this series
+ x1 = data[7][0];
+ var y1error = y1 + (data[7][2][1]*2);
+ xy1 = g.toDomCoords(x1, y1error);
+
+ // Check if both points are connected at the right edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+ // Test if the lower error bars of series1 are drawn correctly
+ // --------------------------------------------
+
+ // The last lower error-point of the second series.
+ x2 = data[9][0];
+ y2error = y2 - (data[9][2][1]*2);
+ xy2 = g.toDomCoords(x2, y2error);
+
+ // The previous valid lower error-point of this series
+ x1 = data[7][0];
+ y1error = y1 - (data[7][2][1]*2);
+ xy1 = g.toDomCoords(x1, y1error);
+
+ // Check if both points are connected at the right edge of the canvas and if the option "connectSeparatedPoints" works properly
+ // even if the point is outside the visible range and only one series has a valid value for this point.
+ CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+};
--- /dev/null
+/**
+ * @fileoverview Test cases for DygraphOptions.
+ */
+var DygraphOptionsTestCase = TestCase("dygraph-options-tests");
+
+DygraphOptionsTestCase.prototype.setUp = function() {
+ document.body.innerHTML = "<div id='graph'></div>";
+};
+
+DygraphOptionsTestCase.prototype.tearDown = function() {
+};
+
+/*
+ * Pathalogical test to ensure getSeriesNames works
+ */
+DygraphOptionsTestCase.prototype.testGetSeriesNames = function() {
+ var opts = {
+ width: 480,
+ height: 320
+ };
+ var data = "X,Y,Y2,Y3\n" +
+ "0,-1,0,0";
+
+ // Kind of annoying that you need a DOM to test the object.
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+
+ // We don't need to get at g's attributes_ object just
+ // to test DygraphOptions.
+ var o = new DygraphOptions(g);
+ assertEquals(["Y", "Y2", "Y3"], o.seriesNames());
+};
--- /dev/null
+/**
+ * @fileoverview Test cases for the per-axis grid options, including the new
+ * option "gridLinePattern".
+ *
+ * @author david.eberlein@ch.sauter-bc.com (Fr. Sauter AG)
+ */
+var GridPerAxisTestCase = TestCase("grid-per-axis");
+
+GridPerAxisTestCase.prototype.setUp = function() {
+ document.body.innerHTML = "<div id='graph'></div>";
+};
+
+GridPerAxisTestCase.origFunc = Dygraph.getContext;
+
+GridPerAxisTestCase.prototype.setUp = function() {
+ document.body.innerHTML = "<div id='graph'></div>";
+ Dygraph.getContext = function(canvas) {
+ return new Proxy(GridPerAxisTestCase.origFunc(canvas));
+ };
+};
+
+GridPerAxisTestCase.prototype.tearDown = function() {
+ Dygraph.getContext = GridPerAxisTestCase.origFunc;
+};
+
+GridPerAxisTestCase.prototype.testIndependentGrids = function() {
+ var opts = {
+ width : 480,
+ height : 320,
+ errorBars : false,
+ labels : [ "X", "Left", "Right" ],
+ series : {
+ Left : {
+ axis : "y"
+ },
+ Right : {
+ axis : "y2"
+ }
+ },
+ axes : {
+ y2 : {
+ drawGrid : true,
+ independentTicks : true
+ }
+ }
+ };
+
+ var data = [ [ 1, 0, 0 ], [ 2, 12, 88 ], [ 3, 88, 122 ], [ 4, 63, 273 ],
+ [ 5, 110, 333 ] ];
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+
+ htx = g.hidden_ctx_;
+
+ // The expected gridlines
+ var yGridlines = [ 0, 20, 40, 60, 80, 100, 120 ];
+ var y2Gridlines = [ 0, 50, 100, 150, 200, 250, 300, 350 ];
+ var gridlines = [ yGridlines, y2Gridlines ];
+
+ function halfUp(x) {
+ return Math.round(x) + 0.5;
+ }
+ function halfDown(y) {
+ return Math.round(y) - 0.5;
+ }
+
+ var attrs = {}, x, y;
+ x = halfUp(g.plotter_.area.x);
+ // Step through y(0) and y2(1) axis
+ for ( var axis = 0; axis < 2; axis++) {
+ // Step through all gridlines of the axis
+ for ( var i = 0; i < gridlines[axis].length; i++) {
+ // Check the labels:
+ var labels = Util.getYLabels(axis + 1);
+ assertEquals("Expected label not found.", gridlines[axis][i], labels[i]);
+
+ // Check that the grid was drawn.
+ y = halfDown(g.toDomYCoord(gridlines[axis][i], axis));
+ var p1 = [ x, y ];
+ var p2 = [ x + g.plotter_.area.w, y ];
+ CanvasAssertions.assertLineDrawn(htx, p1, p2, attrs);
+ }
+ }
+};
+
+GridPerAxisTestCase.prototype.testPerAxisGridColors = function() {
+ var opts = {
+ width : 480,
+ height : 320,
+ errorBars : false,
+ labels : [ "X", "Left", "Right" ],
+ series : {
+ Left : {
+ axis : "y"
+ },
+ Right : {
+ axis : "y2"
+ }
+ },
+ axes : {
+ y : {
+ gridLineColor : "#0000ff",
+ gridLineWidth : 2
+ },
+ y2 : {
+ drawGrid : true,
+ independentTicks : true,
+ gridLineColor : "#ff0000",
+ gridLineWidth : 2,
+ }
+ }
+ };
+ var data = [ [ 1, 0, 0 ], [ 2, 12, 88 ], [ 3, 88, 122 ], [ 4, 63, 273 ],
+ [ 5, 110, 333 ] ];
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+ htx = g.hidden_ctx_;
+
+ // The expected gridlines
+ var yGridlines = [ 20, 40, 60, 80, 100, 120 ];
+ var y2Gridlines = [ 50, 100, 150, 200, 250, 300, 350 ];
+ var gridlines = [ yGridlines, y2Gridlines ];
+ var gridColors = [ [ 0, 0, 255, 255 ], [ 255, 0, 0, 255 ] ];
+
+ function halfUp(x) {
+ return Math.round(x) + 1;
+ }
+ function halfDown(y) {
+ return Math.round(y) - 1;
+ }
+ var x, y;
+ x = halfUp(g.plotter_.area.x);
+ // Step through y(0) and y2(1) axis
+ for ( var axis = 0; axis < 2; axis++) {
+ // Step through all gridlines of the axis
+ for ( var i = 0; i < gridlines[axis].length; i++) {
+ y = halfDown(g.toDomYCoord(gridlines[axis][i], axis));
+ // Check the grid colors.
+ assertEquals("Unexpected grid color found at pixel: x: " + x + "y: " + y,
+ gridColors[axis], Util.samplePixel(g.hidden_, x, y));
+ }
+ }
+};
+GridPerAxisTestCase.prototype.testPerAxisGridWidth = function() {
+ var opts = {
+ width : 480,
+ height : 320,
+ errorBars : false,
+ gridLineColor : "#ff0000",
+ labels : [ "X", "Left", "Right" ],
+ series : {
+ Left : {
+ axis : "y"
+ },
+ Right : {
+ axis : "y2"
+ }
+ },
+ axes : {
+ x : {
+ gridLineWidth : 4
+ },
+ y : {
+ gridLineWidth : 2
+ },
+ y2 : {
+ drawGrid : true,
+ independentTicks : true,
+ gridLineWidth : 1
+ }
+ }
+ };
+ var data = [ [ 1, 0, 0 ], [ 2, 12, 88 ], [ 3, 88, 122 ], [ 4, 63, 273 ],
+ [ 5, 110, 333 ] ];
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+ htx = g.hidden_ctx_;
+
+ // The expected gridlines
+ var yGridlines = [ 20, 40, 60, 80 ];
+ var y2Gridlines = [ 50, 100, 150, 200, 250, 350 ];
+ var gridlines = [ yGridlines, y2Gridlines ];
+ var xGridlines = [ 2, 3, 4 ];
+ var gridColor = [ 255, 0, 0 ];
+ var emptyColor = [ 0, 0, 0 ];
+
+ function halfUp(x) {
+ return Math.round(x) + 1;
+ }
+ function halfDown(y) {
+ return Math.round(y) - 1;
+ }
+ var x, y;
+ x = halfUp(g.plotter_.area.x + 10);
+ // Step through y(0) and y2(1) axis
+ for ( var axis = 0; axis < 2; axis++) {
+ // Step through all gridlines of the axis
+ for ( var i = 0; i < gridlines[axis].length; i++) {
+ y = halfDown(g.toDomYCoord(gridlines[axis][i], axis));
+ // Ignore the alpha value
+ var drawnPixeldown2 = Util.samplePixel(g.hidden_, x, y - 2).slice(0, 3);
+ var drawnPixeldown1 = Util.samplePixel(g.hidden_, x, y - 1).slice(0, 3);
+ var drawnPixel = Util.samplePixel(g.hidden_, x, y).slice(0, 3);
+ var drawnPixelup1 = Util.samplePixel(g.hidden_, x, y + 1).slice(0, 3);
+ var drawnPixelup2 = Util.samplePixel(g.hidden_, x, y + 2).slice(0, 3);
+ // Check the grid width.
+ switch (axis) {
+ case 0: // y with 2 pixels width
+ assertEquals("Unexpected y-grid color found at pixel: x: " + x + "y: "
+ + y, emptyColor, drawnPixeldown2);
+ assertEquals("Unexpected y-grid color found at pixel: x: " + x + "y: "
+ + y, gridColor, drawnPixeldown1);
+ assertEquals("Unexpected y-grid color found at pixel: x: " + x + "y: "
+ + y, gridColor, drawnPixel);
+ assertEquals("Unexpected y-grid color found at pixel: x: " + x + "y: "
+ + y, gridColor, drawnPixelup1);
+ assertEquals("Unexpected y-grid color found at pixel: x: " + x + "y: "
+ + y, emptyColor, drawnPixelup2);
+ break;
+ case 1: // y2 with 1 pixel width
+ assertEquals("Unexpected y2-grid color found at pixel: x: " + x + "y: "
+ + y, emptyColor, drawnPixeldown1);
+ assertEquals("Unexpected y2-grid color found at pixel: x: " + x + "y: "
+ + y, gridColor, drawnPixel);
+ assertEquals("Unexpected y2-grid color found at pixel: x: " + x + "y: "
+ + y, emptyColor, drawnPixelup1);
+ break;
+ }
+ }
+ }
+
+ // Check the x axis grid
+ y = halfDown(g.plotter_.area.y) + 10;
+ for ( var i = 0; i < xGridlines.length; i++) {
+ x = halfUp(g.toDomXCoord(xGridlines[i]));
+ assertEquals("Unexpected x-grid color found at pixel: x: " + x + "y: " + y,
+ emptyColor, Util.samplePixel(g.hidden_, x - 4, y).slice(0, 3));
+ assertEquals("Unexpected x-grid color found at pixel: x: " + x + "y: " + y,
+ gridColor, Util.samplePixel(g.hidden_, x - 3, y).slice(0, 3));
+ assertEquals("Unexpected x-grid color found at pixel: x: " + x + "y: " + y,
+ gridColor, Util.samplePixel(g.hidden_, x - 2, y).slice(0, 3));
+ assertEquals("Unexpected x-grid color found at pixel: x: " + x + "y: " + y,
+ gridColor, Util.samplePixel(g.hidden_, x - 1, y).slice(0, 3));
+ assertEquals("Unexpected x-grid color found at pixel: x: " + x + "y: " + y,
+ gridColor, Util.samplePixel(g.hidden_, x, y).slice(0, 3));
+ assertEquals("Unexpected x-grid color found at pixel: x: " + x + "y: " + y,
+ gridColor, Util.samplePixel(g.hidden_, x + 1, y).slice(0, 3));
+ assertEquals("Unexpected x-grid color found at pixel: x: " + x + "y: " + y,
+ emptyColor, Util.samplePixel(g.hidden_, x + 2, y).slice(0, 3));
+ }
+};
+GridPerAxisTestCase.prototype.testGridLinePattern = function() {
+ var opts = {
+ width : 480,
+ height : 320,
+ errorBars : false,
+ drawXGrid : false,
+ drawXAxis : false,
+ drawYAxis : false,
+ labels : [ "X", "Left", "Right" ],
+ colors : [ "rgba(0,0,0,0)", "rgba(0,0,0,0)" ],
+ series : {
+ Left : {
+ axis : "y"
+ },
+ Right : {
+ axis : "y2"
+ }
+ },
+ axes : {
+ y : {
+ gridLineColor : "#0000ff",
+ gridLinePattern : [ 10, 10 ]
+ }
+ }
+ };
+ var data = [ [ 1, 0, 0 ], [ 2, 12, 88 ], [ 3, 88, 122 ], [ 4, 63, 273 ],
+ [ 5, 110, 333 ] ];
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+ htx = g.hidden_ctx_;
+
+ // The expected gridlines
+ var yGridlines = [ 0, 20, 40, 60, 80, 100, 120 ];
+
+ function halfUp(x) {
+ return Math.round(x) + 1;
+ }
+ function halfDown(y) {
+ return Math.round(y) - 1;
+ }
+ var x, y;
+ // Step through all gridlines of the axis
+ for ( var i = 0; i < yGridlines.length; i++) {
+ y = halfDown(g.toDomYCoord(yGridlines[i], 0));
+ // Step through the pixels of the line and test the pattern.
+ for (x = halfUp(g.plotter_.area.x); x < g.plotter_.area.w; x++) {
+ // avoid checking the edge pixels since they differ depending on the OS.
+ var pixelpos = x % 10;
+ if(pixelpos < 1 || pixelpos > 8) continue;
+
+ // Ignore alpha
+ var drawnPixel = Util.samplePixel(g.hidden_, x, y).slice(0,3);
+ var pattern = (Math.floor((x) / 10)) % 2;
+ switch (pattern) {
+ case 0: // fill
+ assertEquals("Unexpected filled grid-pattern color found at pixel: x: " + x + "y: "
+ + y, [ 0, 0, 255 ], drawnPixel);
+ break;
+ case 1: // no fill
+ assertEquals("Unexpected empty grid-pattern color found at pixel: x: " + x + "y: "
+ + y, [ 0, 0, 0 ], drawnPixel);
+ break;
+ }
+ }
+ }
+};
var htx = g.hidden_ctx_;
assertEquals(2, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
assertEquals(0, CanvasAssertions.numLinesDrawn(htx, '#0000ff'));
-}
+};
MissingPointsTestCase.prototype.testSeparatedPointsDontDraw_expanded = function() {
var graph = document.getElementById("graph");
{ strokeStyle: '#0000ff', });
CanvasAssertions.assertLineDrawn(htx, [370, 87], [475, 25],
{ strokeStyle: '#0000ff', });
-}
+};
MissingPointsTestCase.prototype.testSeparatedPointsDontDraw_expanded_connected = function() {
var graph = document.getElementById("graph");
CanvasAssertions.assertConsecutiveLinesDrawn(htx,
[[56, 275], [161, 212], [370, 87], [475, 25]],
{ strokeStyle: '#0000ff' });
-}
+};
/**
* At the time of writing this test, the blue series is only points, and not lines.
CanvasAssertions.assertConsecutiveLinesDrawn(htx,
[[140, 275], [307, 125], [475, 225]],
{ strokeStyle: '#ff0000' });
-}
+};
/**
* At the time of writing this test, the blue series is only points, and not lines.
CanvasAssertions.assertConsecutiveLinesDrawn(htx,
[[56, 244], [149, 181], [242, 118]],
{ strokeStyle: '#0000ff' });
-}
+};
/* These lines contain awesome powa!
var lines = CanvasAssertions.getLinesDrawn(htx, {strokeStyle: "#0000ff"});
console.log(line[0].args, line[1].args, line[0].properties.strokeStyle);
}
*/
+
+MissingPointsTestCase.prototype.testErrorBarsWithMissingPoints = function() {
+ var data = [
+ [1, [2,1]],
+ [2, [3,1]],
+ [3, null],
+ [4, [5,1]],
+ [5, [4,1]],
+ [6, [null,null]],
+ ];
+ var g = new Dygraph(
+ document.getElementById("graph"),
+ data,
+ {
+ errorBars: true,
+ colors: ['red']
+ }
+ );
+
+ var htx = g.hidden_ctx_;
+
+ assertEquals(8, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+
+ var p0 = g.toDomCoords(data[0][0], data[0][1][0]);
+ var p1 = g.toDomCoords(data[1][0], data[1][1][0]);
+ var p2 = g.toDomCoords(data[3][0], data[3][1][0]);
+ var p3 = g.toDomCoords(data[4][0], data[4][1][0]);
+ CanvasAssertions.assertConsecutiveLinesDrawn(htx,
+ [p0, p1], { strokeStyle: '#ff0000' });
+ CanvasAssertions.assertConsecutiveLinesDrawn(htx,
+ [p2, p3], { strokeStyle: '#ff0000' });
+};
+
+MissingPointsTestCase.prototype.testErrorBarsWithMissingPointsConnected = function() {
+ var data = [
+ [1, [null,1]],
+ [2, [2,1]],
+ [3, null],
+ [4, [5,1]],
+ [5, [null,null]],
+ [6, [3,1]]
+ ];
+ var g = new Dygraph(
+ document.getElementById("graph"),
+ data,
+ {
+ connectSeparatedPoints: true,
+ drawPoints: true,
+ errorBars: true,
+ colors: ['red']
+ }
+ );
+
+ var htx = g.hidden_ctx_;
+
+ assertEquals(8, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+
+ var p1 = g.toDomCoords(data[1][0], data[1][1][0]);
+ var p2 = g.toDomCoords(data[3][0], data[3][1][0]);
+ var p3 = g.toDomCoords(data[5][0], data[5][1][0]);
+ CanvasAssertions.assertConsecutiveLinesDrawn(htx,
+ [p1, p2, p3],
+ { strokeStyle: '#ff0000' });
+};
+MissingPointsTestCase.prototype.testCustomBarsWithMissingPoints = function() {
+ var data = [
+ [1, [1,2,3]],
+ [2, [2,3,4]],
+ [3, null],
+ [4, [4,5,6]],
+ [5, [3,4,5]],
+ [6, [null,null,null]],
+ [7, [2,3,4]],
+ [8, [1,2,3]],
+ [9, NaN],
+ [10, [2,3,4]],
+ [11, [3,4,5]],
+ [12, [NaN,NaN,NaN]]
+ ];
+ var g = new Dygraph(
+ document.getElementById("graph"),
+ data,
+ {
+ customBars: true,
+ colors: ['red']
+ }
+ );
+
+ var htx = g.hidden_ctx_;
+
+ assertEquals(16, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+
+ var p0 = g.toDomCoords(data[0][0], data[0][1][1]);
+ var p1 = g.toDomCoords(data[1][0], data[1][1][1]);
+ CanvasAssertions.assertLineDrawn(htx, p0, p1, { strokeStyle: '#ff0000' });
+
+ p0 = g.toDomCoords(data[3][0], data[3][1][1]);
+ p1 = g.toDomCoords(data[4][0], data[4][1][1]);
+ CanvasAssertions.assertLineDrawn(htx, p0, p1, { strokeStyle: '#ff0000' });
+
+ p0 = g.toDomCoords(data[6][0], data[6][1][1]);
+ p1 = g.toDomCoords(data[7][0], data[7][1][1]);
+ CanvasAssertions.assertLineDrawn(htx, p0, p1, { strokeStyle: '#ff0000' });;
+
+ p0 = g.toDomCoords(data[9][0], data[9][1][1]);
+ p1 = g.toDomCoords(data[10][0], data[10][1][1]);
+ CanvasAssertions.assertLineDrawn(htx, p0, p1, { strokeStyle: '#ff0000' });
+};
+
+MissingPointsTestCase.prototype.testCustomBarsWithMissingPointsConnected = function() {
+ var data = [
+ [1, [1,null,1]],
+ [2, [1,2,3]],
+ [3, null],
+ [4, [4,5,6]],
+ [5, [null,null,null]],
+ [6, [2,3,4]]
+ ];
+ var g = new Dygraph(
+ document.getElementById("graph"),
+ data,
+ {
+ connectSeparatedPoints: true,
+ drawPoints: true,
+ customBars: true,
+ colors: ['red']
+ }
+ );
+
+ var htx = g.hidden_ctx_;
+
+ assertEquals(8, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+
+ var p1 = g.toDomCoords(data[1][0], data[1][1][1]);
+ var p2 = g.toDomCoords(data[3][0], data[3][1][1]);
+ var p3 = g.toDomCoords(data[5][0], data[5][1][1]);
+ CanvasAssertions.assertConsecutiveLinesDrawn(htx,
+ [p1, p2, p3],
+ { strokeStyle: '#ff0000' });
+};
};
var graph = document.getElementById("graph");
var data = "X,A,B,C,D,E\n0,1,2,3,4,5\n";
+ var threw = false;
try {
new Dygraph(graph, data, opts);
} catch(e) {
- assertEquals(
- "Using objects for axis specification is not supported inside the 'series' option.",
- e);
+ threw = true;
}
+
+ assertTrue(threw);
}
assert(newXRange[1]+'<'+xRange[1], newXRange[1] < xRange[1]);
};
+
+RangeSelectorTestCase.prototype.testRangeSelectorPositionIfXAxisNotDrawn = function() {
+ var opts = {
+ width: 480,
+ height: 100,
+ xAxisHeight: 30,
+ drawXAxis: false,
+ showRangeSelector: true,
+ rangeSelectorHeight: 30
+ };
+ var data = [
+ [0, 1],
+ [10, 1]
+ ];
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data, opts);
+
+ //assert, that the range selector is at top position 70 since the 30px of the
+ // xAxis shouldn't be reserved since it isn't drawn.
+ this.assertGraphExistence(g, graph);
+ var bgcanvas = graph.getElementsByClassName('dygraph-rangesel-bgcanvas')[0];
+ assertEquals("Range selector is not at the expected position.","70px", bgcanvas.style.top);
+ var fgcanvas = graph.getElementsByClassName('dygraph-rangesel-fgcanvas')[0];
+ assertEquals("Range selector is not at the expected position.","70px", fgcanvas.style.top);
+};
+
RangeSelectorTestCase.prototype.assertGraphExistence = function(g, graph) {
assertNotNull(g);
var zoomhandles = graph.getElementsByClassName('dygraph-rangesel-zoomhandle');
*
* @author julian.eichstaedt@ch.sauter-bc.com (Fr. Sauter AG)
*/
-var StepTestCase = TestCase("step_plot_per_series");
+var StepTestCase = TestCase("step-plot-per-series");
StepTestCase.prototype.setUp = function() {
document.body.innerHTML = "<div id='graph'></div>";
- dygraph-canvas.js
- dygraph-interaction-model.js
- dygraph-layout.js
-- dygraph-options.js
+x dygraph-options.js
- dygraph-range-selector.js
- dygraph.js
x dygraph-gviz.js
Here's a command that can be used to build dygraphs using the closure
compiler:
-java -jar ../../closure-compiler-read-only/build/compiler.jar --js=dygraph-utils.js --js=dashed-canvas.js --js=dygraph-options-reference.js --js=dygraph-tickers.js --js=dygraph-gviz.js --js_output_file=/tmp/out.js --compilation_level ADVANCED_OPTIMIZATIONS --warning_level VERBOSE --externs dygraph-externs.js
+java -jar ../../closure-compiler-read-only/build/compiler.jar --js=dygraph-utils.js --js=dashed-canvas.js --js=dygraph-options-reference.js --js=dygraph-tickers.js --js=dygraph-gviz.js --js=dygraph-options.js --js_output_file=/tmp/out.js --compilation_level ADVANCED_OPTIMIZATIONS --warning_level VERBOSE --externs dygraph-externs.js
As each file is closurized, it can be added as a "--js" parameter.
<h1>dygraphs JavaScript Visualization Library</h1>
<p><a href="http://github.com/danvk/dygraphs">http://github.com/danvk/dygraphs</a></p>
- <p>See <a href="http://blog.dygraphs.com/">blog</a>, <a href="http://groups.google.com/group/dygraphs-users">mailing list</a>, <a href="http://github.com/danvk/dygraphs/downloads/">downloads</a>, <a href="tests/">demos</a> and <a href="http://code.google.com/p/dygraphs/issues/">open issues</a></p>
+ <p>For help, <a
+ href="http://stackoverflow.com/questions/ask?tags=dygraphs+javascript">ask
+ a question</a> on StackOverflow. You may also be interested in the <a
+ href="http://blog.dygraphs.com/">blog</a>, <a
+ href="http://groups.google.com/group/dygraphs-users">mailing list</a>, <a
+ href="tests/">demos</a> and <a
+ href="http://code.google.com/p/dygraphs/issues/">open issues</a>.</p>
<p>dygraphs is an open source JavaScript library that produces produces interactive, zoomable charts of time series. It is designed to display dense data sets and enable users to explore and interpret them.</p>
* The chart canvas has already been created by the Dygraph object. The
* renderer simply gets a drawing context.
*
- * @param {Dyraph} dygraph The chart to which this renderer belongs.
- * @param {Canvas} element The <canvas> DOM element on which to draw.
+ * @param {Dygraph} dygraph The chart to which this renderer belongs.
+ * @param {HTMLCanvasElement} element The <canvas> DOM element on which to draw.
* @param {CanvasRenderingContext2D} elementContext The drawing context.
* @param {DygraphLayout} layout The chart's DygraphLayout object.
*
this.width = this.element.width;
// --- check whether everything is ok before we return
+ // NOTE(konigsberg): isIE is never defined in this object. Bug of some sort.
if (!this.isIE && !(DygraphCanvasRenderer.isSupported(this.element)))
throw "Canvas is not supported.";
ctx.moveTo(point.canvasx, point.canvasy);
}
if (drawPoints || isIsolated) {
- pointsOnLine.push([point.canvasx, point.canvasy]);
+ pointsOnLine.push([point.canvasx, point.canvasy, point.idx]);
}
prevCanvasX = point.canvasx;
prevCanvasY = point.canvasy;
var cb = pointsOnLine[idx];
ctx.save();
drawPointCallback(
- e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize);
+ e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]);
ctx.restore();
}
};
/**
* Add canvas Actually draw the lines chart, including error bars.
- * If opt_seriesName is specified, only that series will be drawn.
- * (This is used for expedited redrawing with highlightSeriesOpts)
- * Lines are typically drawn in the non-interactive dygraph canvas. If opt_ctx
- * is specified, they can be drawn elsewhere.
*
* This function can only be called if DygraphLayout's points array has been
* updated with canvas{x,y} attributes, i.e. by
* DygraphCanvasRenderer._updatePoints.
+ *
+ * @param {string=} opt_seriesName when specified, only that series will
+ * be drawn. (This is used for expedited redrawing with highlightSeriesOpts)
+ * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing
+ * context. However, lines are typically drawn on the object's
+ * elementContext.
* @private
*/
DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ctx) {
*/
DygraphLayout.prototype.datasets;
+// TODO: DygraphOptions should not reach inside Dygraph private data like this.
+/** @type {Object} */
+Dygraph.prototype.attrs_;
+/** @type {Object} */
+Dygraph.prototype.user_attrs_;
+
/**
* @type {DygraphLayout}
*/
Dygraph.prototype.layout_;
+/** @type {function(): string} */
+Dygraph.prototype.getHighlightSeries;
+
/** @type {Array.<{elem:Element,type:string,fn:function(!Event):(boolean|undefined|null)}>} */
Dygraph.prototype.registeredEvents_;
-/** @type {Object} */
+/** @type {{axes: Object}} */
Dygraph.DEFAULT_ATTRS;
this.setNames.push(setname);
};
+/**
+ * Returns the box which the chart should be drawn in. This is the canvas's
+ * box, less space needed for the axis and chart labels.
+ *
+ * @return {{x: number, y: number, w: number, h: number}}
+ */
DygraphLayout.prototype.getPlotArea = function() {
return this.area_;
};
// on chrome+linux, they are 6 times more expensive than iterating through the
// points and drawing the lines. The brunt of the cost comes from allocating
// the |point| structures.
+ var boundaryIdStart = this.dygraph_.getLeftBoundary_();
for (var setIdx = 0; setIdx < this.datasets.length; setIdx++) {
var dataset = this.datasets[setIdx];
var setName = this.setNames[setIdx];
y: yNormal,
xval: xValue,
yval: yValue,
- name: setName // TODO(danvk): is this really necessary?
+ name: setName, // TODO(danvk): is this really necessary?
+ idx: j + boundaryIdStart
};
}
[ "cx" , "center x coordinate" ],
[ "cy" , "center y coordinate" ],
[ "color" , "series color" ],
- [ "pointSize" , "the radius of the image." ]
+ [ "pointSize" , "the radius of the image." ],
+ [ "idx" , "the row-index of the point in the data."]
],
"description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy). Also see <a href='#drawHighlightPointCallback'>drawHighlightPointCallback</a>"
},
[ "cx" , "center x coordinate" ],
[ "cy" , "center y coordinate" ],
[ "color" , "series color" ],
- [ "pointSize" , "the radius of the image." ]
+ [ "pointSize" , "the radius of the image." ],
+ [ "idx" , "the row-index of the point in the data."]
],
"description": "Draw a custom item when a point is highlighted. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see <a href='#drawPointCallback'>drawPointCallback</a>"
},
"default": "rgb(128,128,128)",
"labels": ["Grid"],
"type": "red, blue",
- "description": "The color of the gridlines."
+ "description": "The color of the gridlines. This may be set on a per-axis basis to define each axis' grid separately."
},
"visibility": {
"default": "[true, true, ...]",
},
"drawXGrid": {
"default": "true",
- "labels": ["Grid"],
+ "labels": ["Grid","Deprecated"],
"type": "boolean",
- "description" : "Whether to display vertical gridlines under the chart."
+ "description" : "Use the per-axis option drawGrid instead. Whether to display vertical gridlines under the chart."
},
"drawYGrid": {
"default": "true",
+ "labels": ["Grid","Deprecated"],
+ "type": "boolean",
+ "description" : "Use the per-axis option drawGrid instead. Whether to display horizontal gridlines under the chart."
+ },
+ "drawGrid": {
+ "default": "true for x and y, false for y2",
"labels": ["Grid"],
"type": "boolean",
- "description" : "Whether to display horizontal gridlines under the chart."
+ "description" : "Whether to display gridlines in the chart. This may be set on a per-axis basis to define the visibility of each axis' grid separately."
+ },
+ "independentTicks": {
+ "default": "true for y, false for y2",
+ "labels": ["Axis display", "Grid"],
+ "type": "boolean",
+ "description" : "Only valid for y and y2, has no effect on x: This option defines whether the y axes should align their ticks or if they should be independent. Possible combinations: 1.) y=true, y2=false (default): y is the primary axis and the y2 ticks are aligned to the the ones of y. (only 1 grid) 2.) y=false, y2=true: y2 is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 grid) 3.) y=true, y2=true: Both axis are independent and have their own ticks. (2 grids) 4.) y=false, y2=false: Invalid configuration causes an error."
},
"drawXAxis": {
"default": "true",
"default": "0.3",
"labels": ["Grid"],
"type": "float",
- "description" : "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawXGrid and drawYGrid options."
+ "description" : "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawXGrid and drawYGrid options. This may be set on a per-axis basis to define each axis' grid separately."
},
"axisLineWidth": {
"default": "0.3",
"use strict";
/*
- * Interesting member variables:
- * dygraph_ - the graph.
+ * Interesting member variables: (REMOVING THIS LIST AS I CLOSURIZE)
* global_ - global attributes (common among all graphs, AIUI)
* user - attributes set by the user
- * yAxes_ - array of axis index to { series : [ series names ] , options : { axis-specific options. }
- * xAxis_ - { options : { axis-specific options. }
* series_ - { seriesName -> { idx, yAxis, options }}
- * labels_ - used as mapping from index to series name.
*/
/**
* if labels are not yet available, since those drive details of the per-series
* and per-axis options.
*
- * @param {Dyraph} dygraph The chart to which these options belong.
+ * @param {Dygraph} dygraph The chart to which these options belong.
* @constructor
*/
var DygraphOptions = function(dygraph) {
+ /**
+ * The dygraph.
+ * @type {!Dygraph}
+ */
this.dygraph_ = dygraph;
+
+ /**
+ * Array of axis index to { series : [ series names ] , options : { axis-specific options. }
+ * @type {Array.<{series : Array.<string>, options : Object}>} @private
+ */
this.yAxes_ = [];
+
+ /**
+ * Contains x-axis specific options, which are stored in the options key.
+ * This matches the yAxes_ object structure (by being a dictionary with an
+ * options element) allowing for shared code.
+ * @type {options: Object} @private
+ */
this.xAxis_ = {};
this.series_ = {};
this.global_ = this.dygraph_.attrs_;
this.user_ = this.dygraph_.user_attrs_ || {};
+ /**
+ * A list of series in columnar order.
+ * @type {Array.<string>}
+ */
+ this.labels_ = [];
+
this.highlightSeries_ = this.get("highlightSeriesOpts") || {};
this.reparseSeries();
};
-/*
+/**
* Not optimal, but does the trick when you're only using two axes.
* If we move to more axes, this can just become a function.
+ *
+ * @type {Object.<number>}
+ * @private
*/
DygraphOptions.AXIS_STRING_MAPPINGS_ = {
'y' : 0,
'Y2' : 1
};
+/**
+ * @param {string|number} axis
+ * @private
+ */
DygraphOptions.axisToIndex_ = function(axis) {
if (typeof(axis) == "string") {
if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) {
}
throw "Dygraphs only supports two y-axes, indexed from 0-1.";
}
- if (typeof(axis) == "object") {
- throw "Using objects for axis specification " +
- "is not supported inside the 'series' option.";
- }
if (axis) {
throw "Unknown axis : " + axis;
}
return; // -- can't do more for now, will parse after getting the labels.
}
- this.labels = labels.slice(1);
+ this.labels_ = labels.slice(1);
this.yAxes_ = [ { series : [], options : {}} ]; // Always one axis at least.
this.xAxis_ = { options : {} };
if (oldStyleSeries) {
var axisId = 0; // 0-offset; there's always one.
// Go through once, add all the series, and for those with {} axis options, add a new axis.
- for (var idx = 0; idx < this.labels.length; idx++) {
- var seriesName = this.labels[idx];
+ for (var idx = 0; idx < this.labels_.length; idx++) {
+ var seriesName = this.labels_[idx];
var optionsForSeries = this.user_[seriesName] || {};
// Go through one more time and assign series to an axis defined by another
// series, e.g. { 'Y1: { axis: {} }, 'Y2': { axis: 'Y1' } }
- for (var idx = 0; idx < this.labels.length; idx++) {
- var seriesName = this.labels[idx];
+ for (var idx = 0; idx < this.labels_.length; idx++) {
+ var seriesName = this.labels_[idx];
var optionsForSeries = this.series_[seriesName]["options"];
var axis = optionsForSeries["axis"];
if (typeof(axis) == 'string') {
if (!this.series_.hasOwnProperty(axis)) {
- this.dygraph_.error("Series " + seriesName + " wants to share a y-axis with " +
+ Dygraph.error("Series " + seriesName + " wants to share a y-axis with " +
"series " + axis + ", which does not define its own axis.");
- return null;
+ return;
}
var yAxis = this.series_[axis].yAxis;
this.series_[seriesName].yAxis = yAxis;
}
}
} else {
- for (var idx = 0; idx < this.labels.length; idx++) {
- var seriesName = this.labels[idx];
+ for (var idx = 0; idx < this.labels_.length; idx++) {
+ var seriesName = this.labels_[idx];
var optionsForSeries = this.user_.series[seriesName] || {};
var yAxis = DygraphOptions.axisToIndex_(optionsForSeries["axis"]);
*/
DygraphOptions.prototype.getForSeries = function(name, series) {
// Honors indexes as series.
- if (series === this.dygraph_.highlightSet_) {
+ if (series === this.dygraph_.getHighlightSeries()) {
if (this.highlightSeries_.hasOwnProperty(name)) {
return this.highlightSeries_[name];
}
/**
* Returns the number of y-axes on the chart.
- * @return {Number} the number of axes.
+ * @return {number} the number of axes.
*/
DygraphOptions.prototype.numAxes = function() {
return this.yAxes_.length;
Dygraph.SHORT_SPACINGS[Dygraph.DAILY] = 1000 * 86400;
Dygraph.SHORT_SPACINGS[Dygraph.WEEKLY] = 1000 * 604800;
+/**
+ * A collection of objects specifying where it is acceptable to place tick
+ * marks for granularities larger than WEEKLY.
+ * 'months' is an array of month indexes on which to place tick marks.
+ * 'year_mod' ticks are placed when year % year_mod = 0.
+ * @type {Array.<Object>}
+ */
+Dygraph.LONG_TICK_PLACEMENTS = [];
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.MONTHLY] = {
+ months : [0,1,2,3,4,5,6,7,8,9,10,11],
+ year_mod : 1
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.QUARTERLY] = {
+ months: [0,3,6,9],
+ year_mod: 1
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.BIANNUAL] = {
+ months: [0,6],
+ year_mod: 1
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.ANNUAL] = {
+ months: [0],
+ year_mod: 1
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.DECADAL] = {
+ months: [0],
+ year_mod: 10
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.CENTENNIAL] = {
+ months: [0],
+ year_mod: 100
+};
+
/**
* This is a list of human-friendly values at which to show tick marks on a log
* scale. It is k * 10^n, where k=1..9 and n=-39..+39, so:
var spacing = Dygraph.SHORT_SPACINGS[granularity];
return Math.floor(0.5 + 1.0 * (end_time - start_time) / spacing);
} else {
- var year_mod = 1; // e.g. to only print one point every 10 years.
- var num_months = 12;
- if (granularity == Dygraph.QUARTERLY) num_months = 3;
- if (granularity == Dygraph.BIANNUAL) num_months = 2;
- if (granularity == Dygraph.ANNUAL) num_months = 1;
- if (granularity == Dygraph.DECADAL) { num_months = 1; year_mod = 10; }
- if (granularity == Dygraph.CENTENNIAL) { num_months = 1; year_mod = 100; }
+ var tickPlacement = Dygraph.LONG_TICK_PLACEMENTS[granularity];
var msInYear = 365.2524 * 24 * 3600 * 1000;
var num_years = 1.0 * (end_time - start_time) / msInYear;
- return Math.floor(0.5 + 1.0 * num_years * num_months / year_mod);
+ return Math.floor(0.5 + 1.0 * num_years * tickPlacement.months.length / tickPlacement.year_mod);
}
};
var check_dst = (spacing >= Dygraph.SHORT_SPACINGS[Dygraph.TWO_HOURLY]);
for (t = start_time; t <= end_time; t += spacing) {
- var d = new Date(t);
+ d = new Date(t);
// This ensures that we stay on the same hourly "rhythm" across
// daylight savings transitions. Without this, the ticks could get off
var months;
var year_mod = 1; // e.g. to only print one point every 10 years.
- if (granularity == Dygraph.MONTHLY) {
- months = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
- } else if (granularity == Dygraph.QUARTERLY) {
- months = [ 0, 3, 6, 9 ];
- } else if (granularity == Dygraph.BIANNUAL) {
- months = [ 0, 6 ];
- } else if (granularity == Dygraph.ANNUAL) {
- months = [ 0 ];
- } else if (granularity == Dygraph.DECADAL) {
- months = [ 0 ];
- year_mod = 10;
- } else if (granularity == Dygraph.CENTENNIAL) {
- months = [ 0 ];
- year_mod = 100;
+ if (granularity < Dygraph.NUM_GRANULARITIES) {
+ months = Dygraph.LONG_TICK_PLACEMENTS[granularity].months;
+ year_mod = Dygraph.LONG_TICK_PLACEMENTS[granularity].year_mod;
} else {
Dygraph.warn("Span of dates is too long");
}
/**
* @param {string} message
- * @private
*/
Dygraph.error = function(message) {
Dygraph.log(Dygraph.ERROR, message);
* @param {!Object} self
* @param {!Object} o
* @return {!Object}
- * @private
*/
Dygraph.update = function(self, o) {
if (typeof(o) != 'undefined' && o !== null) {
};
/**
- * Returns a new iterator over array, between indexes start and
+ * Returns a new iterator over array, between indexes start and
* start + length, and only returns entries that pass the accept function
*
* @param {!Array} array the array to iterate over.
* This function will scan the option list and determine if they
* require us to recalculate the pixel positions of each point.
* @param {!Array.<string>} labels a list of options to check.
- * @param {!Object} attrs
+ * @param {!Object} attrs
* @return {boolean} true if the graph needs new points else false.
* @private
*/
/**
* Compares two arrays to see if they are equal. If either parameter is not an
- * array it will return false. Does a shallow compare
+ * array it will return false. Does a shallow compare
* Dygraph.compareArrays([[1,2], [3, 4]], [[1,2], [3,4]]) === false.
* @param {!Array.<T>} array1 first array
* @param {!Array.<T>} array2 second array
var computeCoordinates = function() {
var x = cx + (Math.sin(angle) * radius);
var y = cy + (-Math.cos(angle) * radius);
- return [x, y];
+ return [x, y];
};
var initialCoordinates = computeCoordinates();
* };
* window.addEventListener('mouseup', mouseUpHandler);
* };
- *
+ *
* @constructor
*/
Dygraph.IFrameTarp = function() {
};
/**
- * Is one element contained by another?
- * @param {Element} containee The contained element.
- * @param {Element} container The container element.
+ * Is one node contained by another?
+ * @param {Node} containee The contained node.
+ * @param {Node} container The container node.
* @return {boolean} Whether containee is inside (or equal to) container.
* @private
*/
-Dygraph.isElementContainedBy = function(containee, container) {
+Dygraph.isNodeContainedBy = function(containee, container) {
if (container === null || containee === null) {
return false;
}
- while (containee && containee !== container) {
- containee = containee.parentNode;
+ var containeeNode = /** @type {Node} */ (containee);
+ while (containeeNode && containeeNode !== container) {
+ containeeNode = containeeNode.parentNode;
}
- return (containee === container);
+ return (containeeNode === container);
};
pixelsPerLabel: 60,
axisLabelFormatter: Dygraph.dateAxisFormatter,
valueFormatter: Dygraph.dateString_,
+ drawGrid: true,
+ independentTicks: true,
ticker: null // will be set in dygraph-tickers.js
},
y: {
pixelsPerLabel: 30,
valueFormatter: Dygraph.numberValueFormatter,
axisLabelFormatter: Dygraph.numberAxisLabelFormatter,
+ drawGrid: true,
+ independentTicks: true,
ticker: null // will be set in dygraph-tickers.js
},
y2: {
pixelsPerLabel: 30,
valueFormatter: Dygraph.numberValueFormatter,
axisLabelFormatter: Dygraph.numberAxisLabelFormatter,
+ drawGrid: false,
+ independentTicks: false,
ticker: null // will be set in dygraph-tickers.js
}
}
// 2. e.relatedTarget is outside the chart
var target = e.target || e.fromElement;
var relatedTarget = e.relatedTarget || e.toElement;
- if (Dygraph.isElementContainedBy(target, dygraph.graphDiv) &&
- !Dygraph.isElementContainedBy(relatedTarget, dygraph.graphDiv)) {
+ if (Dygraph.isNodeContainedBy(target, dygraph.graphDiv) &&
+ !Dygraph.isNodeContainedBy(relatedTarget, dygraph.graphDiv)) {
dygraph.mouseOut_(e);
}
};
ctx.strokeStyle = color;
ctx.fillStyle = color;
callback(this.g, pt.name, ctx, canvasx, pt.canvasy,
- color, circleSize);
+ color, circleSize, pt.idx);
}
ctx.restore();
var datasets = [];
var extremes = {}; // series name -> [low, high]
var i, j, k;
+ var errorBars = this.attr_("errorBars");
+ var customBars = this.attr_("customBars");
+ var bars = errorBars || customBars;
+ var isValueNull = function(sample) {
+ if (!bars) {
+ return sample[1] === null;
+ } else {
+ return customBars ? sample[1][1] === null :
+ errorBars ? sample[1][0] === null : false;
+ }
+ };
// Loop over the fields (series). Go from the last to the first,
// because if they're stacked that's how we accumulate the values.
// Prune down to the desired range, if necessary (for zooming)
// Because there can be lines going to points outside of the visible area,
// we actually prune to visible points, plus one on either side.
- var bars = this.attr_("errorBars") || this.attr_("customBars");
if (dateWindow) {
var low = dateWindow[0];
var high = dateWindow[1];
var pruned = [];
+
// TODO(danvk): do binary search instead of linear search.
// TODO(danvk): pass firstIdx and lastIdx directly to the renderer.
var firstIdx = null, lastIdx = null;
lastIdx = k;
}
}
+
if (firstIdx === null) firstIdx = 0;
- if (firstIdx > 0) firstIdx--;
+ var correctedFirstIdx = firstIdx;
+ var isInvalidValue = true;
+ while (isInvalidValue && correctedFirstIdx > 0) {
+ correctedFirstIdx--;
+ isInvalidValue = isValueNull(series[correctedFirstIdx]);
+ }
+
if (lastIdx === null) lastIdx = series.length - 1;
- if (lastIdx < series.length - 1) lastIdx++;
- boundaryIds[i-1] = [firstIdx, lastIdx];
+ var correctedLastIdx = lastIdx;
+ isInvalidValue = true;
+ while (isInvalidValue && correctedLastIdx < series.length - 1) {
+ correctedLastIdx++;
+ isInvalidValue = isValueNull(series[correctedLastIdx]);
+ }
+
+ boundaryIds[i-1] = [(firstIdx > 0) ? firstIdx - 1 : firstIdx,
+ (lastIdx < series.length - 1) ? lastIdx + 1 : lastIdx];
+
+ if (correctedFirstIdx!==firstIdx) {
+ pruned.push(series[correctedFirstIdx]);
+ }
for (k = firstIdx; k <= lastIdx; k++) {
pruned.push(series[k]);
}
+ if (correctedLastIdx !== lastIdx) {
+ pruned.push(series[correctedLastIdx]);
+ }
+
series = pruned;
} else {
boundaryIds[i-1] = [0, series.length-1];
};
var numAxes = this.attributes_.numAxes();
var ypadCompat, span, series, ypad;
+
+ var p_axis;
// Compute extreme values, a span and tick marks for each axis.
for (var i = 0; i < numAxes; i++) {
var axis = this.axes_[i];
var logscale = this.attributes_.getForAxis("logscale", i);
var includeZero = this.attributes_.getForAxis("includeZero", i);
+ var independentTicks = this.attributes_.getForAxis("independentTicks", i);
series = this.attributes_.seriesForAxis(i);
// Add some padding. This supports two Y padding operation modes:
} else {
axis.computedValueRange = axis.extremeRange;
}
-
- // Add ticks. By default, all axes inherit the tick positions of the
- // primary axis. However, if an axis is specifically marked as having
- // independent ticks, then that is permissible as well.
- var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));
- var ticker = opts('ticker');
- if (i === 0 || axis.independentTicks) {
+
+
+ if(independentTicks) {
+ axis.independentTicks = independentTicks;
+ var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));
+ var ticker = opts('ticker');
axis.ticks = ticker(axis.computedValueRange[0],
- axis.computedValueRange[1],
- this.height_, // TODO(danvk): should be area.height
- opts,
- this);
- } else {
- var p_axis = this.axes_[0];
+ axis.computedValueRange[1],
+ this.height_, // TODO(danvk): should be area.height
+ opts,
+ this);
+ // Define the first independent axis as primary axis.
+ if (!p_axis) p_axis = axis;
+ }
+ }
+ if (p_axis === undefined) {
+ throw ("Configuration Error: At least one axis has to have the \"independentTicks\" option activated.");
+ }
+ // Add ticks. By default, all axes inherit the tick positions of the
+ // primary axis. However, if an axis is specifically marked as having
+ // independent ticks, then that is permissible as well.
+ for (var i = 0; i < numAxes; i++) {
+ var axis = this.axes_[i];
+
+ if (!axis.independentTicks) {
+ var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));
+ var ticker = opts('ticker');
var p_ticks = p_axis.ticks;
var p_scale = p_axis.computedValueRange[1] - p_axis.computedValueRange[0];
var scale = axis.computedValueRange[1] - axis.computedValueRange[0];
Dygraph.prototype.extractSeries_ = function(rawData, i, logScale) {
// TODO(danvk): pre-allocate series here.
var series = [];
+ var errorBars = this.attr_("errorBars");
+ var customBars = this.attr_("customBars");
for (var j = 0; j < rawData.length; j++) {
var x = rawData[j][0];
var point = rawData[j][i];
point = null;
}
}
- series.push([x, point]);
+ // Fix null points to fit the display type standard.
+ if(point !== null) {
+ series.push([x, point]);
+ } else {
+ series.push([x, errorBars ? [null, null] : customBars ? [null, null, null] : point]);
+ }
}
return series;
};
}
if (g.numAxes() == 2) {
- // TODO(danvk): per-axis setting.
- var w = g.getOption('yAxisLabelWidth') + 2 * g.getOption('axisTickSize');
- e.reserveSpaceRight(w);
+ // TODO(danvk): introduce a 'drawAxis' per-axis property.
+ if (g.getOption('drawYAxis')) {
+ // TODO(danvk): per-axis setting.
+ var w = g.getOption('yAxisLabelWidth') + 2 * g.getOption('axisTickSize');
+ e.reserveSpaceRight(w);
+ }
} else if (g.numAxes() > 2) {
g.error("Only two y-axes are supported at this time. (Trying " +
"to use " + g.numAxes() + ")");
var x, y, i, ticks;
if (g.getOption('drawYGrid')) {
+ var axes = ["y", "y2"];
+ var strokeStyles = [], lineWidths = [], drawGrid = [], stroking = [], strokePattern = [];
+ for (var i = 0; i < axes.length; i++) {
+ drawGrid[i] = g.getOptionForAxis("drawGrid", axes[i]);
+ if (drawGrid[i]) {
+ strokeStyles[i] = g.getOptionForAxis('gridLineColor', axes[i]);
+ lineWidths[i] = g.getOptionForAxis('gridLineWidth', axes[i]);
+ strokePattern[i] = g.getOptionForAxis('gridLinePattern', axes[i]);
+ stroking[i] = strokePattern[i] && (strokePattern[i].length >= 2);
+ }
+ }
ticks = layout.yticks;
ctx.save();
- ctx.strokeStyle = g.getOption('gridLineColor');
- ctx.lineWidth = g.getOption('gridLineWidth');
+ // draw grids for the different y axes
for (i = 0; i < ticks.length; i++) {
- // TODO(danvk): allow secondary axes to draw a grid, too.
- if (ticks[i][0] !== 0) continue;
- x = halfUp(area.x);
- y = halfDown(area.y + ticks[i][1] * area.h);
- ctx.beginPath();
- ctx.moveTo(x, y);
- ctx.lineTo(x + area.w, y);
- ctx.closePath();
- ctx.stroke();
+ var axis = ticks[i][0];
+ if(drawGrid[axis]) {
+ if (stroking[axis]) {
+ ctx.installPattern(strokePattern[axis]);
+ }
+ ctx.strokeStyle = strokeStyles[axis];
+ ctx.lineWidth = lineWidths[axis];
+
+ x = halfUp(area.x);
+ y = halfDown(area.y + ticks[i][1] * area.h);
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + area.w, y);
+ ctx.closePath();
+ ctx.stroke();
+
+ if (stroking[axis]) {
+ ctx.uninstallPattern();
+ }
+ }
}
ctx.restore();
}
- if (g.getOption('drawXGrid')) {
+ // draw grid for x axis
+ if (g.getOption('drawXGrid') && g.getOptionForAxis("drawGrid", 'x')) {
ticks = layout.xticks;
ctx.save();
- ctx.strokeStyle = g.getOption('gridLineColor');
- ctx.lineWidth = g.getOption('gridLineWidth');
+ var strokePattern = g.getOptionForAxis('gridLinePattern', 'x');
+ var stroking = strokePattern && (strokePattern.length >= 2);
+ if (stroking) {
+ ctx.installPattern(strokePattern);
+ }
+ ctx.strokeStyle = g.getOptionForAxis('gridLineColor', 'x');
+ ctx.lineWidth = g.getOptionForAxis('gridLineWidth', 'x');
for (i = 0; i < ticks.length; i++) {
x = halfUp(area.x + ticks[i][0] * area.w);
y = halfDown(area.y + area.h);
ctx.closePath();
ctx.stroke();
}
+ if (stroking) {
+ ctx.uninstallPattern();
+ }
ctx.restore();
}
};
}
var plotArea = this.dygraph_.layout_.getPlotArea();
- var xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'));
+
+ var xAxisLabelHeight = 0;
+ if(this.getOption_('drawXAxis')){
+ xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'));
+ }
this.canvasRect_ = {
x: plotArea.x,
y: plotArea.y + plotArea.h + xAxisLabelHeight + 4,
exit 1
fi
+# Don't run tests if the documentation doesn't parse.
+./generate-documentation.py > /dev/null
+if [ $? != 0 ]; then
+ echo Failed to generate documentation. Fix this before running tests.
+ exit 1
+fi
+
phantomjs phantom-driver.js
}
);
</script>
+
+ <p>Use axis specific grid options. (independentTicks, gridLinePattern, ...)</p>
+ <div id="div_g15" style="width:600px; height:300px;"></div>
+
+ <script type="text/javascript">
+ var data = [];
+ for (var i = 1; i <= 100; i++) {
+ var m = "01", d = i;
+ if (d > 31) { m = "02"; d -= 31; }
+ if (m == "02" && d > 28) { m = "03"; d -= 28; }
+ if (m == "03" && d > 31) { m = "04"; d -= 31; }
+ if (d < 10) d = "0" + d;
+ // two series, one with range 1-100, one with range 1-2M
+ data.push([new Date("2010/" + m + "/" + d),
+ i,
+ 100 - i,
+ 1e6 * (1 + i * (100 - i) / (50 * 50)),
+ 1e6 * (2 - i * (100 - i) / (50 * 50))]);
+ }
+ g15 = new Dygraph(
+ document.getElementById("div_g15"),
+ data,
+ {
+ labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
+ series : {
+ 'Y3': {
+ axis: 'y2'
+ },
+ 'Y4': {
+ axis: 'y2'
+ }
+ },
+ axes: {
+ x: {
+ gridLineWidth: 2,
+ drawGrid: true,
+ independentTicks: true
+ },
+ y: {
+ valueRange: [40, 80],
+ drawGrid: true,
+ independentTicks: true
+ },
+ y2: {
+ // set axis-related properties here
+ labelsKMB: true,
+ ticker: Dygraph.numericLinearTicks,
+ drawGrid: true,
+ independentTicks: true,
+ gridLineColor: "#ff0000",
+ gridLinePattern: [4,4]
+ }
+ }
+ }
+ );
+ </script>
</body>
-</html>
+</html>
\ No newline at end of file
</td>
</tr></table>
- <div id="graph2" style="float: right; margin-right: 50px; width: 400px; height: 300px;"></div>
+ <div id="graph3" style="float: right; margin-right: 50px; width: 400px; height: 300px;"></div>
<p>The gap would normally be encoded as a null, or missing value. But when you use <code>connectSeparatedPoints</code>, that has a special meaning. Instead, you have to use <code>NaN</code>. This is a bit of a hack, but it gets the job done.</p>
<script type="text/javascript">
g2 = new Dygraph(
- document.getElementById("graph2"),
+ document.getElementById("graph3"),
"x,A,B \n" +
"1,,3 \n" +
"2,2, \n" +
</td>
</tr></table>
+ <h3>Behavior at the edges of the panel for independent series</h3>
+ <p> In case only a part of the whole data is visible (e.g. after zooming in) the lines are
+ drawn to the respective next valid point outside the visible area. </p>
+
+ <table><tr>
+ <td valign=top>
+ <table>
+ <table class=thinborder>
+ <tr><th>x</th><th>A</th></tr>
+ <tr><td>0</td><td>2</td></tr>
+ <tr><td>1</td><td>3</td></tr>
+ <tr><td>2</td><td>3</td></tr>
+ <tr><td>4</td><td>4</td></tr>
+ <tr><td>5</td><td>3</td></tr>
+ <tr><td>6</td><td>3</td></tr>
+ <tr><td>7</td><td>3</td></tr>
+ <tr><td>8</td><td>4</td></tr>
+ </table>
+ </td>
+ <td valign=top style="padding-left:25px;">
+ <table class=thinborder>
+ <tr><th>x</th><th>B</th></tr>
+ <tr><td>0</td><td>1</td></tr>
+ <tr><td>1</td><td>2</td></tr>
+ <tr><td>2</td><td>1</td></tr>
+ <tr><td>8</td><td>2</td></tr>
+ </table>
+ </td>
+ </tr></table>
+
+ <div id="graph2" style="float: right; margin-right: 50px; width: 400px; height: 300px;"></div>
+ <p>Both graphs have no value at the right edge of the panel (x=3). The lines that are drawn to the right edge are determined by their respective next valid value outside the visible area.
+ Therefore it is neither necessary that the next valid values are on the same point nor that they have the same index (index 4 for the green line and index 8 for the blue line).</p>
+ <p>Use double click to unzoom and see the currently invisible points</p>
+
+ <script type="text/javascript">
+ g2 = new Dygraph(
+ document.getElementById("graph2"),
+"x,A,B \n" +
+"0,2,1 \n" +
+"1,3,2 \n" +
+"2,3,1 \n" +
+"3,, \n" +
+"4,4, \n" +
+"5,3, \n" +
+"6,3, \n" +
+"7,3, \n" +
+"8,4,2 \n"
+
+ , {
+ connectSeparatedPoints: true,
+ drawPoints: true,
+ pointSize: 3,
+ highlightCircleSize: 5,
+ dateWindow: [0,3]
+ }
+ );
+ </script>
+
+ <table><tr>
+ <td valign=top>
+ Index
+ <pre>
+0
+1
+2
+3
+4
+5
+6
+7
+8</pre>
+ </td>
+ <td valign=top>
+ (CSV)
+ <pre>x,A,B
+0,2,1
+1,3,2
+2,3,1
+3,,
+4,4,
+5,3,
+6,3,
+7,3,
+8,4,2</pre>
+ </td>
+ <td valign=top style="padding-left: 25px;">
+ (native)
+ <pre>[
+ [0, 2, 1],
+ [1, 3, 2],
+ [2, 3, 1],
+ [3, null, null],
+ [4, 4, null],
+ [5, 3, null],
+ [6, 3, null],
+ [7, 3, null],
+ [8, 4, 2] ]</pre>
+ </td>
+ </tr></table>
+
</body>
</html>
</td><td valign=top>
<div id="graph3"></div>
<div id="graph4"></div>
+ </td><td valign=top>
+ <div id="graph5"></div>
+ <div id="graph6"></div>
</td></tr>
</table>
[2, [12, 2], [20, 3]],
[3, [ 8, 2], [20, 3]],
[4, [null, 2], [20, 3]],
- [5, [null, 2], [null, 3]],
+ [5, [null, 2], null],
[6, [ 9, 2], [20, 3]],
[7, [10, 2], [20, 3]]
],
[1, [10, 2], [20, 3]],
[2, [12, 2], [20, 3]],
[3, [ 8, 2], [20, 3]],
- [4, [null, 2], [20, 3]],
- [5, [null, 2], [null, 3]],
+ [4, null, [20, 3]],
+ [5, null, [null, 3]],
[6, [ 9, 2], [20, 3]],
[7, [10, 2], [20, 3]]
],
labels: [ "X", "Series1", "Series2" ]
}
);
+
+ g5 = new Dygraph(
+ document.getElementById("graph5"),
+ [
+ [1, [10, 2], [20, 3]],
+ [2, [12, 2], [20, 3]],
+ [3, [ 8, 2], [20, 3]],
+ [4, [2, null], null],
+ [5, null, [null, 3]],
+ [6, [ 9, 2], [20, 3]],
+ [7, [10, 2], [20, 3]]
+ ],
+ {
+ errorBars: true,
+ connectSeparatedPoints: false,
+ labels: [ "X", "Series1", "Series2" ]
+ }
+ );
+
+ g6 = new Dygraph(
+ document.getElementById("graph6"),
+ [
+ [1, [8, 10,12],null],
+ [2, [3, 5,7],[4,6,7]],
+ [3, null,[1,2,4]],
+ [4, [ 9,null, 2],[3,4,8]],
+ [5, [null,2, null],[6,8,9]],
+ [6, [2,3, 6],[2,3,5]]
+ ],
+ {
+ customBars: true,
+ connectSeparatedPoints: false,
+ labels: [ "X", "Series1", "Series2"]
+ }
+ );
</script>
</body>
</html>
Roll period of 14 timesteps, custom range selector height and plot color.
</p>
<div id="roll14" style="width:800px; height:320px;"></div>
+ <p>
+ Demo of range selecor without the chart. (interesting if multiple charts should be synced with one range selector).
+ </p>
+ <div id="nochart" style="width:800px; height:30px;"></div>
<script type="text/javascript">
g1 = new Dygraph(
document.getElementById("noroll"),
rangeSelectorPlotFillColor: 'lightyellow'
}
);
+ g3 = new Dygraph(
+ document.getElementById("nochart"),
+ [[0,1],[10,1]],
+ {
+ xAxisHeight: 30,
+ drawXAxis: false,
+ showRangeSelector: true,
+ rangeSelectorHeight: 30,
+ }
+ );
</script>
</body>
</html>
<body>
<h2>Multiple y-axes</h2>
<p>The same data with both one and two y-axes. Two y-axes:</p>
+ <p>Two y-axes with y as primary axis (default):</p>
<div id="demodiv" style="width: 640; height: 350; border: 1px solid black"></div>
- <p>A single y-axis:</p>
+ <p>Two y-axes with y2 as primary axis:</p>
+ <div id="demodiv_y2_primary" style="width: 640; height: 350; border: 1px solid black"></div>
+ <p>Two y-axes using different grids:</p>
+ <div id="demodiv_two_grids" style="width: 640; height: 350; border: 1px solid black"></div>
+ <p>A single y-axis (left):</p>
<div id="demodiv_one" style="width: 640; height: 350; border: 1px solid black"></div>
+ <p>A single y-axis (right):</p>
+ <div id="demodiv_one_right" style="width: 640; height: 350; border: 1px solid black"></div>
<script type="text/javascript">
var data = [];
yAxisLabelWidth: 60
}
);
-
+
g2 = new Dygraph(
- document.getElementById("demodiv_one"),
+ document.getElementById("demodiv_y2_primary"),
+ data,
+ {
+ labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
+ ylabel: 'Primary y-axis',
+ y2label: 'Secondary y-axis',
+ series : {
+ 'Y3': {
+ axis: 'y2'
+ },
+ 'Y4': {
+ axis: 'y2'
+ }
+ },
+ axes: {
+ y: {
+ // set axis-related properties here
+ drawGrid: false,
+ independentTicks: false
+ },
+ y2: {
+ // set axis-related properties here
+ labelsKMB: true,
+ drawGrid: true,
+ independentTicks: true
+ }
+ }
+ }
+ );
+
+ g3 = new Dygraph(
+ document.getElementById("demodiv_two_grids"),
data,
{
labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
- labelsKMB: true,
ylabel: 'Primary y-axis',
y2label: 'Secondary y-axis',
+ series : {
+ 'Y3': {
+ axis: 'y2'
+ },
+ 'Y4': {
+ axis: 'y2'
+ }
+ },
+ axes: {
+ y2: {
+ // set axis-related properties here
+ labelsKMB: true,
+ drawGrid: true,
+ independentTicks: true,
+ gridLinePattern: [2,2]
+ }
+ }
}
);
+ g4 = new Dygraph(
+ document.getElementById("demodiv_one"),
+ data,
+ {
+ labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
+ labelsKMB: true,
+ ylabel: 'Primary y-axis',
+ y2label: 'Secondary y-axis',
+ }
+ );
+
+ g5 = new Dygraph(
+ document.getElementById("demodiv_one_right"),
+ data,
+ {
+ labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
+ ylabel: 'Primary y-axis',
+ y2label: 'Secondary y-axis',
+ series : {
+ 'Y1': {
+ axis: 'y2'
+ },
+ 'Y2': {
+ axis: 'y2'
+ },
+ 'Y3': {
+ axis: 'y2'
+ },
+ 'Y4': {
+ axis: 'y2'
+ }
+ },
+ axes: {
+ y: {
+ // set axis-related properties here
+ drawGrid: false,
+ independentTicks: false
+ },
+ y2: {
+ // set axis-related properties here
+ labelsKMB: true,
+ drawGrid: true,
+ independentTicks: true
+ }
+ }
+ }
+ );
+
function update(el) {
g.updateOptions( { fillGraph: el.checked } );
g2.updateOptions( { fillGraph: el.checked } );
+ g3.updateOptions( { fillGraph: el.checked } );
+ g4.updateOptions( { fillGraph: el.checked } );
+ g5.updateOptions( { fillGraph: el.checked } );
}
</script>