From: Dan Vanderkam Date: Tue, 5 Apr 2011 14:35:42 +0000 (-0400) Subject: merge master X-Git-Tag: v1.0.0~536 X-Git-Url: https://adrianiainlam.tk/git/?a=commitdiff_plain;h=5bc3e265be640a29bc57d7d58c059322ab361d63;hp=36dfa9583c9b6134a63654c73aded8710aff2779;p=dygraphs.git merge master --- diff --git a/docs/index.html b/docs/index.html index 060f31a..1d0569d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -99,8 +99,6 @@

(Mouse over to highlight individual values. Click and drag to zoom. Double-click to zoom back out. Change the number and hit enter to adjust the averaging period.)

-
Temperatures in New York vs. San Francisco
-
@@ -136,7 +137,7 @@
  • Plots time series without using an external server or Flash
  • Works in Internet Explorer (using excanvas)
  • Lightweight (69kb) and responsive
  • -
  • Displays values on mouseover (this makes it easily discoverable)
  • +
  • Displays values on mouseover, making interaction easily discoverable
  • Supports error bands around data series
  • Interactive zoom
  • Displays Annotations on the chart
  • diff --git a/dygraph-canvas.js b/dygraph-canvas.js index 2f8821e..522629c 100644 --- a/dygraph-canvas.js +++ b/dygraph-canvas.js @@ -7,6 +7,24 @@ * - grid overlays * - error bars * - dygraphs attribute system + * + * High level overview of classes: + * + * - DygraphLayout + * This contains all the data to be charted. + * It uses data coordinates, but also records the chart range (in data + * coordinates) and hence is able to calculate percentage positions ('In + * this view, Point A lies 25% down the x-axis.') + * Two things that it does not do are: + * 1. Record pixel coordinates for anything. + * 2. (oddly) determine anything about the layout of chart elements. + * The naming is a vestige of Dygraph's original PlotKit roots. + * + * - DygraphCanvasRenderer + * This class determines the charting area (in pixel coordinates), maps the + * percentage coordinates in the DygraphLayout to pixels and draws them. + * It's also responsible for creating chart DOM elements, i.e. annotations, + * tick mark labels, the title and the x/y-axis labels. */ /** @@ -301,6 +319,7 @@ DygraphCanvasRenderer = function(dygraph, element, layout, options) { this.xlabels = new Array(); this.ylabels = new Array(); this.annotations = new Array(); + this.chartLabels = {}; // TODO(danvk): consider all axes in this computation. this.area = { @@ -321,6 +340,21 @@ DygraphCanvasRenderer = function(dygraph, element, layout, options) { "to use " + this.dygraph_.numAxes() + ")"); } + // Add space for chart labels: title, xlabel and ylabel. + if (this.attr_('title')) { + this.area.h -= this.attr_('titleHeight'); + this.area.y += this.attr_('titleHeight'); + } + if (this.attr_('xlabel')) { + this.area.h -= this.attr_('xLabelHeight'); + } + if (this.attr_('ylabel')) { + // It would make sense to shift the chart here to make room for the y-axis + // label, but the default yAxisLabelWidth is large enough that this results + // in overly-padded charts. The y-axis label should fit fine. If it + // doesn't, the yAxisLabelWidth option can be increased. + } + this.container.style.position = "relative"; this.container.style.width = this.width + "px"; @@ -374,9 +408,15 @@ DygraphCanvasRenderer.prototype.clear = function() { var el = this.annotations[i]; if (el.parentNode) el.parentNode.removeChild(el); } + for (var k in this.chartLabels) { + if (!this.chartLabels.hasOwnProperty(k)) continue; + var el = this.chartLabels[k]; + if (el.parentNode) el.parentNode.removeChild(el); + } this.xlabels = new Array(); this.ylabels = new Array(); this.annotations = new Array(); + this.chartLabels = {}; }; @@ -452,6 +492,7 @@ DygraphCanvasRenderer.prototype.render = function() { // Do the ordinary rendering, as before this._renderLineChart(); this._renderAxis(); + this._renderChartLabels(); this._renderAnnotations(); }; @@ -518,7 +559,7 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { label.style.top = top + "px"; } if (tick[0] == 0) { - label.style.left = "0px"; + label.style.left = (this.area.x - this.options.yAxisLabelWidth - this.options.axisTickSize) + "px"; label.style.textAlign = "right"; } else if (tick[0] == 1) { label.style.left = (this.area.x + this.area.w + @@ -575,7 +616,7 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { var label = makeDiv(tick[1]); label.style.textAlign = "center"; - label.style.bottom = "0px"; + label.style.top = (y + this.options.axisTickSize) + 'px'; var left = (x - this.options.axisLabelWidth/2); if (left + this.options.axisLabelWidth > this.width) { @@ -605,6 +646,101 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { }; +DygraphCanvasRenderer.prototype._renderChartLabels = function() { + // Generate divs for the chart title, xlabel and ylabel. + // Space for these divs has already been taken away from the charting area in + // the DygraphCanvasRenderer constructor. + if (this.attr_('title')) { + var div = document.createElement("div"); + div.style.position = 'absolute'; + div.style.top = '0px'; + div.style.left = this.area.x + 'px'; + div.style.width = this.area.w + 'px'; + div.style.height = this.attr_('titleHeight') + 'px'; + div.style.textAlign = 'center'; + div.style.fontSize = (this.attr_('titleHeight') - 8) + 'px'; + div.style.fontWeight = 'bold'; + var class_div = document.createElement("div"); + class_div.className = 'dygraph-label dygraph-title'; + class_div.innerHTML = this.attr_('title'); + div.appendChild(class_div); + this.container.appendChild(div); + this.chartLabels.title = div; + } + + if (this.attr_('xlabel')) { + var div = document.createElement("div"); + div.style.position = 'absolute'; + div.style.bottom = 0; // TODO(danvk): this is lazy. Calculate style.top. + div.style.left = this.area.x + 'px'; + div.style.width = this.area.w + 'px'; + div.style.height = this.attr_('xLabelHeight') + 'px'; + div.style.textAlign = 'center'; + div.style.fontSize = (this.attr_('xLabelHeight') - 2) + 'px'; + + var class_div = document.createElement("div"); + class_div.className = 'dygraph-label dygraph-xlabel'; + class_div.innerHTML = this.attr_('xlabel'); + div.appendChild(class_div); + this.container.appendChild(div); + this.chartLabels.xlabel = div; + } + + if (this.attr_('ylabel')) { + var box = { + left: 0, + top: this.area.y, + width: this.attr_('yLabelWidth'), + height: this.area.h + }; + // TODO(danvk): is this outer div actually necessary? + var div = document.createElement("div"); + div.style.position = 'absolute'; + div.style.left = box.left; + div.style.top = box.top + 'px'; + div.style.width = box.width + 'px'; + div.style.height = box.height + 'px'; + div.style.fontSize = (this.attr_('yLabelWidth') - 2) + 'px'; + + var inner_div = document.createElement("div"); + inner_div.style.position = 'absolute'; + inner_div.style.width = box.height + 'px'; + inner_div.style.height = box.width + 'px'; + inner_div.style.top = (box.height / 2 - box.width / 2) + 'px'; + inner_div.style.left = (box.width / 2 - box.height / 2) + 'px'; + inner_div.style.textAlign = 'center'; + + // CSS rotation is an HTML5 feature which is not standardized. Hence every + // browser has its own name for the CSS style. + inner_div.style.transform = 'rotate(-90deg)'; // HTML5 + inner_div.style.WebkitTransform = 'rotate(-90deg)'; // Safari/Chrome + inner_div.style.MozTransform = 'rotate(-90deg)'; // Firefox + inner_div.style.OTransform = 'rotate(-90deg)'; // Opera + inner_div.style.msTransform = 'rotate(-90deg)'; // IE9 + + if (typeof(document.documentMode) !== 'undefined' && + document.documentMode < 9) { + // We're dealing w/ an old version of IE, so we have to rotate the text + // using a BasicImage transform. This uses a different origin of rotation + // than HTML5 rotation (top left of div vs. its center). + inner_div.style.filter = + 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)'; + inner_div.style.left = '0px'; + inner_div.style.top = '0px'; + } + + var class_div = document.createElement("div"); + class_div.className = 'dygraph-label dygraph-ylabel'; + class_div.innerHTML = this.attr_('ylabel'); + + inner_div.appendChild(class_div); + div.appendChild(inner_div); + this.container.appendChild(div); + this.chartLabels.ylabel = div; + } +}; + + DygraphCanvasRenderer.prototype._renderAnnotations = function() { var annotationStyle = { "position": "absolute", diff --git a/dygraph.js b/dygraph.js index 00af051..fe677f2 100644 --- a/dygraph.js +++ b/dygraph.js @@ -193,6 +193,11 @@ Dygraph.DEFAULT_ATTRS = { stepPlot: false, avoidMinZero: false, + // Sizes of the various chart labels. + titleHeight: 28, + xLabelHeight: 18, + yLabelWidth: 18, + interactionModel: null // will be set to Dygraph.defaultInteractionModel. }; @@ -959,8 +964,9 @@ Dygraph.prototype.createStatusMessage_ = function() { }; /** - * Position the labels div so that its right edge is flush with the right edge - * of the charting area. + * Position the labels div so that: + * - its right edge is flush with the right edge of the charting area + * - its top edge is flush with the top edge of the charting area */ Dygraph.prototype.positionLabelsDiv_ = function() { // Don't touch a user-specified labelsDiv. @@ -969,6 +975,7 @@ Dygraph.prototype.positionLabelsDiv_ = function() { var area = this.plotter_.area; var div = this.attr_("labelsDiv"); div.style.left = area.x + area.w - this.attr_("labelsDivWidth") - 1 + "px"; + div.style.top = area.y + "px"; }; /** @@ -986,10 +993,11 @@ Dygraph.prototype.createRollInterface_ = function() { var display = this.attr_('showRoller') ? 'block' : 'none'; + var area = this.plotter_.area; var textAttr = { "position": "absolute", "zIndex": 10, - "top": (this.plotter_.area.h - 25) + "px", - "left": (this.plotter_.area.x + 1) + "px", + "top": (area.y + area.h - 25) + "px", + "left": (area.x + 1) + "px", "display": display }; this.roller_.size = "2"; @@ -4164,7 +4172,7 @@ Dygraph.OPTIONS_REFERENCE = // "sigma": { "default": "2.0", "labels": ["Error Bars"], - "type": "integer", + "type": "float", "description": "When errorBars is set, shade this many standard deviations above/below each point." }, "customBars": { @@ -4198,6 +4206,42 @@ Dygraph.OPTIONS_REFERENCE = // "default": "null", "description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% pased the edges of the displayed values. null means no bounds." }, + "title": { + "labels": ["Chart labels"], + "type": "string", + "default": "null", + "description": "Text to display above the chart. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-title' classes." + }, + "titleHeight": { + "default": "18", + "labels": ["Chart labels"], + "type": "integer", + "description": "Height of the chart title, in pixels. This also controls the default font size of the title. If you style the title on your own, this controls how much space is set aside above the chart for the title's div." + }, + "xlabel": { + "labels": ["Chart labels"], + "type": "string", + "default": "null", + "description": "Text to display below the chart's x-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-xlabel' classes." + }, + "xLabelHeight": { + "labels": ["Chart labels"], + "type": "integer", + "default": "18", + "description": "Height of the x-axis label, in pixels. This also controls the default font size of the x-axis label. If you style the label on your own, this controls how much space is set aside below the chart for the x-axis label's div." + }, + "ylabel": { + "labels": ["Chart labels"], + "type": "string", + "default": "null", + "description": "Text to display to the left of the chart's y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-ylabel' classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the yAxisLabelWidth option. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it's rotated, so width is controlled by the 'height' property) or set the yLabelWidth option." + }, + "yLabelWidth": { + "labels": ["Chart labels"], + "type": "integer", + "default": "18", + "description": "Width of the div which contains the y-axis label. Since the y-axis label appears rotated 90 degrees, this actually affects the height of its div." + }, "isZoomedIgnoreProgrammaticZoom" : { "default": "false", "labels": ["Zooming"], @@ -4208,7 +4252,8 @@ Dygraph.OPTIONS_REFERENCE = // ; // // NOTE: in addition to parsing as JS, this snippet is expected to be valid // JSON. This assumption cannot be checked in JS, but it will be checked when -// documentation is generated by the generate-documentation.py script. +// documentation is generated by the generate-documentation.py script. For the +// most part, this just means that you should always use double quotes. // Do a quick sanity check on the options reference. (function() { @@ -4217,6 +4262,7 @@ Dygraph.OPTIONS_REFERENCE = // var valid_cats = [ 'Annotations', 'Axis display', + 'Chart labels', 'CSV parsing', 'Callbacks', 'Data Line display', diff --git a/tests/border.html b/tests/border.html index 9079448..cbf5f9e 100644 --- a/tests/border.html +++ b/tests/border.html @@ -23,7 +23,10 @@ diff --git a/tests/century-scale.html b/tests/century-scale.html index ae4eaef..271a652 100644 --- a/tests/century-scale.html +++ b/tests/century-scale.html @@ -36,7 +36,6 @@ "rgb(255,100,100)", "#00DD55", "rgba(50,50,200,0.4)"], - padding: {left: 40, right: 30, top: 15, bottom: 15}, width: 480, height: 320 } diff --git a/tests/demo.html b/tests/demo.html index 2a2ea20..5859a23 100644 --- a/tests/demo.html +++ b/tests/demo.html @@ -36,9 +36,7 @@ } return r; }, - null, { - rollPeriod: 1, labelsDiv: document.getElementById('status'), labelsSeparateLines: true, labelsKMB: true, @@ -46,9 +44,11 @@ "rgb(255,100,100)", "#00DD55", "rgba(50,50,200,0.4)"], - padding: {left: 40, right: 30, top: 15, bottom: 15}, - width: 480, - height: 320 + width: 640, + height: 480, + title: 'Interesting Shapes', + xlabel: 'Date', + ylabel: 'Count' } ); diff --git a/tests/noise.html b/tests/noise.html index 902de67..6c5b566 100644 --- a/tests/noise.html +++ b/tests/noise.html @@ -13,9 +13,8 @@ -

    7-day rollup:

    -

    14-Day Rollup:

    +
    diff --git a/tests/reverse-y-axis.html b/tests/reverse-y-axis.html index 2328a41..5d0869e 100644 --- a/tests/reverse-y-axis.html +++ b/tests/reverse-y-axis.html @@ -37,7 +37,6 @@ } return r; }, - null, { rollPeriod: 1, labelsDiv: document.getElementById('status'), @@ -47,7 +46,6 @@ "rgb(255,100,100)", "#00DD55", "rgba(50,50,200,0.4)"], - padding: {left: 40, right: 30, top: 15, bottom: 15}, width: 480, height: 320, valueRange: [3000, 0] // Reverse y-axis diff --git a/tests/styled-chart-labels.html b/tests/styled-chart-labels.html new file mode 100644 index 0000000..b7af95e --- /dev/null +++ b/tests/styled-chart-labels.html @@ -0,0 +1,80 @@ + + + + + two series + + + + + + + + + +

    In this chart, each chart label is styled independently. View source to + see how it works.

    + +
    + +

    This version of the chart uses the default styles:

    +
    + + + +