X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph-utils.js;h=98128e317f53a6da1b1a11903d457d863e32df11;hb=5108eb2029afe739872ebfb58f9fb826cef9ea13;hp=b5521c4912d2270f9ce074d84c7e994770110c27;hpb=004b5c90b03ee12be0ac0e653ec4006970ee6740;p=dygraphs.git diff --git a/dygraph-utils.js b/dygraph-utils.js index b5521c4..98128e3 100644 --- a/dygraph-utils.js +++ b/dygraph-utils.js @@ -1,5 +1,5 @@ // Copyright 2011 Dan Vanderkam (danvdk@gmail.com) -// All Rights Reserved. +// MIT-licensed (http://opensource.org/licenses/MIT) /** * @fileoverview This file contains utility functions used by dygraphs. These @@ -172,36 +172,59 @@ Dygraph.hsvToRGB = function (hue, saturation, value) { // The following functions are from quirksmode.org with a modification for Safari from // http://blog.firetree.net/2005/07/04/javascript-find-position/ // http://www.quirksmode.org/js/findpos.html +// ... and modifications to support scrolling divs. -/** @private */ +/** + * Find the x-coordinate of the supplied object relative to the left side + * of the page. + * @private + */ Dygraph.findPosX = function(obj) { var curleft = 0; - if(obj.offsetParent) - while(1) - { - curleft += obj.offsetLeft; - if(!obj.offsetParent) + if(obj.offsetParent) { + var copyObj = obj; + while(1) { + curleft += copyObj.offsetLeft; + if(!copyObj.offsetParent) { break; - obj = obj.offsetParent; + } + copyObj = copyObj.offsetParent; } - else if(obj.x) + } else if(obj.x) { curleft += obj.x; + } + // This handles the case where the object is inside a scrolled div. + while(obj && obj != document.body) { + curleft -= obj.scrollLeft; + obj = obj.parentNode; + } return curleft; }; -/** @private */ +/** + * Find the y-coordinate of the supplied object relative to the top of the + * page. + * @private + */ Dygraph.findPosY = function(obj) { var curtop = 0; - if(obj.offsetParent) - while(1) - { - curtop += obj.offsetTop; - if(!obj.offsetParent) + if(obj.offsetParent) { + var copyObj = obj; + while(1) { + curtop += copyObj.offsetTop; + if(!copyObj.offsetParent) { break; - obj = obj.offsetParent; + } + copyObj = copyObj.offsetParent; } - else if(obj.y) + } else if(obj.y) { curtop += obj.y; + } + // This handles the case where the object is inside a scrolled div. + while(obj && obj != document.body) { + curtop -= obj.scrollTop; + obj = obj.parentNode; + } return curtop; }; @@ -320,30 +343,6 @@ Dygraph.hmsString_ = function(date) { }; /** - * Convert a JS date (millis since epoch) to YYYY/MM/DD - * @param {Number} date The JavaScript date (ms since epoch) - * @return {String} A date of the form "YYYY/MM/DD" - * @private - */ -Dygraph.dateString_ = function(date) { - var zeropad = Dygraph.zeropad; - var d = new Date(date); - - // Get the year: - var year = "" + d.getFullYear(); - // Get a 0 padded month string - var month = zeropad(d.getMonth() + 1); //months are 0-offset, sigh - // Get a 0 padded day string - var day = zeropad(d.getDate()); - - var ret = ""; - var frac = d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds(); - if (frac) ret = " " + Dygraph.hmsString_(date); - - return year + "/" + month + "/" + day + ret; -}; - -/** * Round a number to the specified number of digits past the decimal point. * @param {Number} num The number to round * @param {Number} places The number of decimals to which to round @@ -472,6 +471,33 @@ Dygraph.update = function (self, o) { }; /** + * Copies all the properties from o to self. + * + * @private + */ +Dygraph.updateDeep = function (self, o) { + if (typeof(o) != 'undefined' && o !== null) { + for (var k in o) { + if (o.hasOwnProperty(k)) { + if (o[k] == null) { + self[k] = null; + } else if (Dygraph.isArrayLike(o[k])) { + self[k] = o[k].slice(); + } else if (typeof(o[k]) == 'object') { + if (typeof(self[k]) != 'object') { + self[k] = {}; + } + Dygraph.updateDeep(self[k], o[k]); + } else { + self[k] = o[k]; + } + } + } + } + return self; +}; + +/** * @private */ Dygraph.isArrayLike = function (o) { @@ -500,6 +526,7 @@ Dygraph.isDateLike = function (o) { }; /** + * Note: this only seems to work for arrays. * @private */ Dygraph.clone = function(o) { @@ -530,3 +557,108 @@ Dygraph.createCanvas = function() { return canvas; }; + +/** + * @private + * This function will scan the option list and determine if they + * require us to recalculate the pixel positions of each point. + * @param { List } a list of options to check. + * @return { Boolean } true if the graph needs new points else false. + */ +Dygraph.isPixelChangingOptionList = function(labels, attrs) { + // A whitelist of options that do not change pixel positions. + var pixelSafeOptions = { + 'annotationClickHandler': true, + 'annotationDblClickHandler': true, + 'annotationMouseOutHandler': true, + 'annotationMouseOverHandler': true, + 'axisLabelColor': true, + 'axisLineColor': true, + 'axisLineWidth': true, + 'clickCallback': true, + 'colorSaturation': true, + 'colorValue': true, + 'colors': true, + 'connectSeparatedPoints': true, + 'digitsAfterDecimal': true, + 'drawCallback': true, + 'drawPoints': true, + 'drawXGrid': true, + 'drawYGrid': true, + 'fillAlpha': true, + 'gridLineColor': true, + 'gridLineWidth': true, + 'hideOverlayOnMouseOut': true, + 'highlightCallback': true, + 'highlightCircleSize': true, + 'interactionModel': true, + 'isZoomedIgnoreProgrammaticZoom': true, + 'labelsDiv': true, + 'labelsDivStyles': true, + 'labelsDivWidth': true, + 'labelsKMB': true, + 'labelsKMG2': true, + 'labelsSeparateLines': true, + 'labelsShowZeroValues': true, + 'legend': true, + 'maxNumberWidth': true, + 'panEdgeFraction': true, + 'pixelsPerYLabel': true, + 'pointClickCallback': true, + 'pointSize': true, + 'showLabelsOnHighlight': true, + 'showRoller': true, + 'sigFigs': true, + 'strokeWidth': true, + 'underlayCallback': true, + 'unhighlightCallback': true, + 'xAxisLabelFormatter': true, + 'xTicker': true, + 'xValueFormatter': true, + 'yAxisLabelFormatter': true, + 'yValueFormatter': true, + 'zoomCallback': true + }; + + // Assume that we do not require new points. + // This will change to true if we actually do need new points. + var requiresNewPoints = false; + + // Create a dictionary of series names for faster lookup. + // If there are no labels, then the dictionary stays empty. + var seriesNamesDictionary = { }; + if (labels) { + for (var i = 1; i < labels.length; i++) { + seriesNamesDictionary[labels[i]] = true; + } + } + + // Iterate through the list of updated options. + for (property in attrs) { + // Break early if we already know we need new points from a previous option. + if (requiresNewPoints) { + break; + } + if (attrs.hasOwnProperty(property)) { + // Find out of this field is actually a series specific options list. + if (seriesNamesDictionary[property]) { + // This property value is a list of options for this series. + // If any of these sub properties are not pixel safe, set the flag. + for (subProperty in attrs[property]) { + // Break early if we already know we need new points from a previous option. + if (requiresNewPoints) { + break; + } + if (attrs[property].hasOwnProperty(subProperty) && !pixelSafeOptions[subProperty]) { + requiresNewPoints = true; + } + } + // If this was not a series specific option list, check if its a pixel changing property. + } else if (!pixelSafeOptions[property]) { + requiresNewPoints = true; + } + } + } + + return requiresNewPoints; +};