X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph.js;h=758d26db7a7421405fbb7de748eecc5b7402930b;hb=82c6fe4d4a5e55a9d5f7696b23a203905006d1ab;hp=66432f7f2a7773e9082d21daa3ecc30603c9fa5e;hpb=795b16307db2a673ba7aa3452f6f6b0e93baeb3a;p=dygraphs.git diff --git a/dygraph.js b/dygraph.js index 66432f7..758d26d 100644 --- a/dygraph.js +++ b/dygraph.js @@ -397,6 +397,7 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { Dygraph.updateDeep(this.attrs_, Dygraph.DEFAULT_ATTRS); this.boundaryIds_ = []; + this.setIndexByName_ = {}; // Create the containing DIV and other interactive elements this.createInterface_(); @@ -840,11 +841,13 @@ Dygraph.prototype.createInterface_ = function() { this.createStatusMessage_(); this.createDragInterface_(); + this.resizeHandler = function(e) { + dygraph.resize(); + } + // Update when the window is resized. // TODO(danvk): drop frames depending on complexity of the chart. - Dygraph.addEvent(window, 'resize', function(e) { - dygraph.resize(); - }); + Dygraph.addEvent(window, 'resize', this.resizeHandler); }; /** @@ -868,7 +871,9 @@ Dygraph.prototype.destroy = function() { } } }; - + // remove event handlers + Dygraph.removeEvent(window,'resize',this.resizeHandler); + this.resizeHandler = null; // These may not all be necessary, but it can't hurt... nullOut(this.layout_); nullOut(this.plotter_); @@ -1526,29 +1531,113 @@ Dygraph.prototype.mouseMove_ = function(event) { Dygraph.prototype.idxToRow_ = function(idx) { if (idx < 0) return -1; - for (var i in this.layout_.datasets) { - if (idx < this.layout_.datasets[i].length) { - return this.boundaryIds_[0][0]+idx; + // make sure that you get the boundaryIds record which is also defined (see bug #236) + var boundaryIdx = -1; + for (var i = 0; i < this.boundaryIds_.length; i++) { + if (this.boundaryIds_[i] !== undefined) { + boundaryIdx = i; + break; + } + } + if (boundaryIdx < 0) return -1; + for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) { + var set = this.layout_.datasets[setIdx]; + if (idx < set.length) { + return this.boundaryIds_[boundaryIdx][0] + idx; } - idx -= this.layout_.datasets[i].length; + idx -= set.length; } return -1; }; /** * @private + * Generates legend html dash for any stroke pattern. It will try to scale the + * pattern to fit in 1em width. Or if small enough repeat the partern for 1em + * width. + * @param strokePattern The pattern + * @param color The color of the series. + * @param oneEmWidth The width in pixels of 1em in the legend. + */ +Dygraph.prototype.generateLegendDashHTML_ = function(strokePattern, color, oneEmWidth) { + var dash = ""; + var i, j, paddingLeft, marginRight; + var strokePixelLength = 0, segmentLoop = 0; + var normalizedPattern = []; + var loop; + // IE 7,8 fail at these divs, so they get boring legend, have not tested 9. + var isIE = (/MSIE/.test(navigator.userAgent) && !window.opera); + if(isIE) { + return "—"; + } + if (!strokePattern || strokePattern.length <= 1) { + // Solid line + dash = "
"; + } else { + // Compute the length of the pixels including the first segment twice, + // since we repeat it. + for (i = 0; i <= strokePattern.length; i++) { + strokePixelLength += strokePattern[i%strokePattern.length]; + } + + // See if we can loop the pattern by itself at least twice. + loop = Math.floor(oneEmWidth/(strokePixelLength-strokePattern[0])); + if (loop > 1) { + // This pattern fits at least two times, no scaling just convert to em; + for (i = 0; i < strokePattern.length; i++) { + normalizedPattern[i] = strokePattern[i]/oneEmWidth; + } + // Since we are repeating the pattern, we don't worry about repeating the + // first segment in one draw. + segmentLoop = normalizedPattern.length; + } else { + // If the pattern doesn't fit in the legend we scale it to fit. + loop = 1; + for (i = 0; i < strokePattern.length; i++) { + normalizedPattern[i] = strokePattern[i]/strokePixelLength; + } + // For the scaled patterns we do redraw the first segment. + segmentLoop = normalizedPattern.length+1; + } + // Now make the pattern. + for (j = 0; j < loop; j++) { + for (i = 0; i < segmentLoop; i+=2) { + // The padding is the drawn segment. + paddingLeft = normalizedPattern[i%normalizedPattern.length]; + if (i < strokePattern.length) { + // The margin is the space segment. + marginRight = normalizedPattern[(i+1)%normalizedPattern.length]; + } else { + // The repeated first segment has no right margin. + marginRight = 0; + } + dash += "
"; + } + } + } + return dash; +}; + +/** + * @private * Generates HTML for the legend which is displayed when hovering over the * chart. If no selected points are specified, a default legend is returned * (this may just be the empty string). * @param { Number } [x] The x-value of the selected points. * @param { [Object] } [sel_points] List of selected points for the given * x-value. Should have properties like 'name', 'yval' and 'canvasy'. + * @param { Number } [oneEmWidth] The pixel width for 1em in the legend. */ -Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) { +Dygraph.prototype.generateLegendHTML_ = function(x, sel_points, oneEmWidth) { // If no points are selected, we display a default legend. Traditionally, // this has been blank. But a better default would be a conventional legend, // which provides essential information for a non-interactive chart. - var html, sepLines, i, c; + var html, sepLines, i, c, dash, strokePattern; if (typeof(x) === 'undefined') { if (this.attr_('legend') != 'always') return ''; @@ -1559,8 +1648,10 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) { if (!this.visibility()[i - 1]) continue; c = this.plotter_.colors[labels[i]]; if (html !== '') html += (sepLines ? '
' : ' '); - html += "—" + labels[i] + - ""; + strokePattern = this.attr_("strokePattern", labels[i]); + dash = this.generateLegendDashHTML_(strokePattern, c, oneEmWidth); + html += "" + dash + + " " + labels[i] + ""; } return html; } @@ -1603,8 +1694,14 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) { * x-value. Should have properties like 'name', 'yval' and 'canvasy'. */ Dygraph.prototype.setLegendHTML_ = function(x, sel_points) { - var html = this.generateLegendHTML_(x, sel_points); var labelsDiv = this.attr_("labelsDiv"); + var sizeSpan = document.createElement('span'); + // Calculates the width of 1em in pixels for the legend. + sizeSpan.setAttribute('style', 'margin: 0; padding: 0 0 0 1em; border: 0;'); + labelsDiv.appendChild(sizeSpan); + var oneEmWidth=sizeSpan.offsetWidth; + + var html = this.generateLegendHTML_(x, sel_points, oneEmWidth); if (labelsDiv !== null) { labelsDiv.innerHTML = html; } else { @@ -1683,8 +1780,9 @@ Dygraph.prototype.setSelection = function(row) { } if (row !== false && row >= 0) { - for (var i in this.layout_.datasets) { - if (row < this.layout_.datasets[i].length) { + for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) { + var set = this.layout_.datasets[setIdx]; + if (row < set.length) { var point = this.layout_.points[pos+row]; if (this.attr_("stackedGraph")) { @@ -1693,7 +1791,7 @@ Dygraph.prototype.setSelection = function(row) { this.selPoints_.push(point); } - pos += this.layout_.datasets[i].length; + pos += set.length; } } @@ -2019,9 +2117,15 @@ Dygraph.prototype.drawGraph_ = function(clearSelection) { var extremes = packed[1]; this.boundaryIds_ = packed[2]; + this.setIndexByName_ = {}; + var labels = this.attr_("labels"); + if (labels.length > 0) { + this.setIndexByName_[labels[0]] = 0; + } for (var i = 1; i < datasets.length; i++) { + this.setIndexByName_[labels[i]] = i; if (!this.visibility()[i - 1]) continue; - this.layout_.addDataset(this.attr_("labels")[i], datasets[i]); + this.layout_.addDataset(labels[i], datasets[i]); } this.computeYAxisRanges_(extremes); @@ -2090,7 +2194,7 @@ Dygraph.prototype.renderGraph_ = function(is_initial_draw, clearSelection) { Dygraph.prototype.computeYAxes_ = function() { // Preserve valueWindow settings if they exist, and if the user hasn't // specified a new valueRange. - var i, valueWindows, seriesName, axis, index; + var i, valueWindows, seriesName, axis, index, opts, v; if (this.axes_ !== undefined && this.user_attrs_.hasOwnProperty("valueRange") === false) { valueWindows = []; for (index = 0; index < this.axes_.length; index++) { @@ -2122,7 +2226,7 @@ Dygraph.prototype.computeYAxes_ = function() { // Copy global axis options over to the first axis. for (i = 0; i < axisOptions.length; i++) { var k = axisOptions[i]; - var v = this.attr_(k); + v = this.attr_(k); if (v) this.axes_[0][k] = v; } @@ -2136,7 +2240,7 @@ Dygraph.prototype.computeYAxes_ = function() { } if (typeof(axis) == 'object') { // Add a new axis, making a copy of its per-axis options. - var opts = {}; + opts = {}; Dygraph.update(opts, this.axes_[0]); Dygraph.update(opts, { valueRange: null }); // shouldn't inherit this. var yAxisId = this.axes_.length; @@ -2170,6 +2274,22 @@ Dygraph.prototype.computeYAxes_ = function() { this.axes_[index].valueWindow = valueWindows[index]; } } + + // New axes options + for (axis = 0; axis < this.axes_.length; axis++) { + if (axis === 0) { + opts = this.optionsViewForAxis_('y' + (axis ? '2' : '')); + v = opts("valueRange"); + if (v) this.axes_[axis].valueRange = v; + } else { // To keep old behavior + var axes = this.user_attrs_.axes; + if (axes && axes.y2) { + v = axes.y2.valueRange; + if (v) this.axes_[axis].valueRange = v; + } + } + } + }; /** @@ -2774,6 +2894,19 @@ Dygraph.prototype.parseArray_ = function(data) { * @private */ Dygraph.prototype.parseDataTable_ = function(data) { + var shortTextForAnnotationNum = function(num) { + // converts [0-9]+ [A-Z][a-z]* + // example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab + // and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz + var shortText = String.fromCharCode(65 /* A */ + num % 26); + num = Math.floor(num / 26); + while ( num > 0 ) { + shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26 ) + shortText.toLowerCase(); + num = Math.floor((num - 1) / 26); + } + return shortText; + } + var cols = data.getNumberOfColumns(); var rows = data.getNumberOfRows(); @@ -2855,7 +2988,7 @@ Dygraph.prototype.parseDataTable_ = function(data) { var ann = {}; ann.series = data.getColumnLabel(col); ann.xval = row[0]; - ann.shortText = String.fromCharCode(65 /* A */ + annotations.length); + ann.shortText = shortTextForAnnotationNum(annotations.length); ann.text = ''; for (var k = 0; k < annotationCols[col].length; k++) { if (k) ann.text += "\n"; @@ -3162,15 +3295,19 @@ Dygraph.prototype.annotations = function() { }; /** + * Get the list of label names for this graph. The first column is the + * x-axis, so the data series names start at index 1. + */ +Dygraph.prototype.getLabels = function(name) { + return this.attr_("labels").slice(); +}; + +/** * Get the index of a series (column) given its name. The first column is the * x-axis, so the data series start with index 1. */ Dygraph.prototype.indexFromSetName = function(name) { - var labels = this.attr_("labels"); - for (var i = 0; i < labels.length; i++) { - if (labels[i] == name) return i; - } - return null; + return this.setIndexByName_[name]; }; /**