Remove the vestigial DygraphLayout.setDateWindow.
[dygraphs.git] / dygraph-utils.js
index 10a393c..223360b 100644 (file)
@@ -76,18 +76,28 @@ Dygraph.log = function(severity, message) {
   }
 
   if (typeof(window.console) != 'undefined') {
+    // In older versions of Firefox, only console.log is defined.
+    var console = window.console;
+    var log = function(console, method, msg) {
+      if (method && typeof(method) == 'function') {
+        method.call(console, msg);
+      } else {
+        console.log(msg);
+      }
+    };
+
     switch (severity) {
       case Dygraph.DEBUG:
-        window.console.debug('dygraphs: ' + message);
+        log(console, console.debug, 'dygraphs: ' + message);
         break;
       case Dygraph.INFO:
-        window.console.info('dygraphs: ' + message);
+        log(console, console.info, 'dygraphs: ' + message);
         break;
       case Dygraph.WARNING:
-        window.console.warn('dygraphs: ' + message);
+        log(console, console.warn, 'dygraphs: ' + message);
         break;
       case Dygraph.ERROR:
-        window.console.error('dygraphs: ' + message);
+        log(console, console.error, 'dygraphs: ' + message);
         break;
     }
   }
@@ -125,7 +135,6 @@ Dygraph.prototype.warn = Dygraph.warn;
 
 /**
  * @param {string} message
- * @private
  */
 Dygraph.error = function(message) {
   Dygraph.log(Dygraph.ERROR, message);
@@ -183,7 +192,7 @@ Dygraph.addEvent = function addEvent(elem, type, fn) {
  *     on the event. The function takes one parameter: the event object.
  * @private
  */
-Dygraph.prototype.addEvent = function addEvent(elem, type, fn) {
+Dygraph.prototype.addEvent = function(elem, type, fn) {
   Dygraph.addEvent(elem, type, fn);
   this.registeredEvents_.push({ elem : elem, type : type, fn : fn });
 };
@@ -197,7 +206,7 @@ Dygraph.prototype.addEvent = function addEvent(elem, type, fn) {
  *     on the event. The function takes one parameter: the event object.
  * @private
  */
-Dygraph.removeEvent = function addEvent(elem, type, fn) {
+Dygraph.removeEvent = function(elem, type, fn) {
   if (elem.removeEventListener) {
     elem.removeEventListener(type, fn, false);
   } else {
@@ -291,6 +300,12 @@ Dygraph.findPosX = function(obj) {
   if(obj.offsetParent) {
     var copyObj = obj;
     while(1) {
+      // NOTE: the if statement here is for IE8.
+      var borderLeft = "0";
+      if (window.getComputedStyle) {
+        borderLeft = window.getComputedStyle(copyObj, null).borderLeft || "0";
+      }
+      curleft += parseInt(borderLeft, 10) ;
       curleft += copyObj.offsetLeft;
       if(!copyObj.offsetParent) {
         break;
@@ -322,6 +337,12 @@ Dygraph.findPosY = function(obj) {
   if(obj.offsetParent) {
     var copyObj = obj;
     while(1) {
+      // NOTE: the if statement here is for IE8.
+      var borderTop = "0";
+      if (window.getComputedStyle) {
+        borderTop = window.getComputedStyle(copyObj, null).borderTop || "0";
+      }
+      curtop += parseInt(borderTop, 10) ;
       curtop += copyObj.offsetTop;
       if(!copyObj.offsetParent) {
         break;
@@ -612,7 +633,6 @@ Dygraph.dateStrToMillis = function(str) {
  * @param {!Object} self
  * @param {!Object} o
  * @return {!Object}
- * @private
  */
 Dygraph.update = function(self, o) {
   if (typeof(o) != 'undefined' && o !== null) {
@@ -796,7 +816,7 @@ Dygraph.Iterator.prototype.next = function() {
 };
 
 /**
- * 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.
@@ -816,7 +836,9 @@ Dygraph.createIterator = function(array, start, length, opt_predicate) {
 
 // Shim layer with setTimeout fallback.
 // From: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
-Dygraph.requestAnimFrame = (function(){
+// Should be called with the window context:
+//   Dygraph.requestAnimFrame.call(window, function() {})
+Dygraph.requestAnimFrame = (function() {
   return window.requestAnimationFrame       ||
           window.webkitRequestAnimationFrame ||
           window.mozRequestAnimationFrame    ||
@@ -841,7 +863,7 @@ Dygraph.requestAnimFrame = (function(){
  * @private
  */
 Dygraph.repeatAndCleanup = function(repeatFn, maxFrames, framePeriodInMillis,
-  cleanupFn) {
+    cleanupFn) {
   var frameNumber = 0;
   var previousFrameNumber;
   var startTime = new Date().getTime();
@@ -854,7 +876,7 @@ Dygraph.repeatAndCleanup = function(repeatFn, maxFrames, framePeriodInMillis,
 
   (function loop() {
     if (frameNumber >= maxFrames) return;
-    Dygraph.requestAnimFrame(function() {
+    Dygraph.requestAnimFrame.call(window, function() {
       // Determine which frame to draw based on the delay so far.  Will skip
       // frames if necessary.
       var currentTime = new Date().getTime();
@@ -871,7 +893,7 @@ Dygraph.repeatAndCleanup = function(repeatFn, maxFrames, framePeriodInMillis,
         repeatFn(maxFrameArg);  // Ensure final call with maxFrameArg.
         cleanupFn();
       } else {
-        if (frameDelta != 0) {  // Don't call repeatFn with duplicate frames.
+        if (frameDelta !== 0) {  // Don't call repeatFn with duplicate frames.
           repeatFn(frameNumber);
         }
         loop();
@@ -884,7 +906,7 @@ Dygraph.repeatAndCleanup = function(repeatFn, maxFrames, framePeriodInMillis,
  * 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
  */
@@ -988,7 +1010,7 @@ Dygraph.isPixelChangingOptionList = function(labels, attrs) {
 
 /**
  * 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
@@ -1027,14 +1049,13 @@ Dygraph.regularShape_ = function(
   delta = delta || Math.PI * 2 / sides;
 
   ctx.beginPath();
-  var first = true;
   var initialAngle = rotationRadians;
   var angle = initialAngle;
 
   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();
@@ -1142,7 +1163,7 @@ Dygraph.Circles = {
  *   };
  *   window.addEventListener('mouseup', mouseUpHandler);
  * };
- * 
+ *
  * @constructor
  */
 Dygraph.IFrameTarp = function() {
@@ -1211,3 +1232,63 @@ Dygraph.detectLineDelimiter = function(data) {
 
   return null;
 };
+
+/**
+ * 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.isNodeContainedBy = function(containee, container) {
+  if (container === null || containee === null) {
+    return false;
+  }
+  var containeeNode = /** @type {Node} */ (containee);
+  while (containeeNode && containeeNode !== container) {
+    containeeNode = containeeNode.parentNode;
+  }
+  return (containeeNode === container);
+};
+
+
+// This masks some numeric issues in older versions of Firefox,
+// where 1.0/Math.pow(10,2) != Math.pow(10,-2).
+/** @type {function(number,number):number} */
+Dygraph.pow = function(base, exp) {
+  if (exp < 0) {
+    return 1.0 / Math.pow(base, -exp);
+  }
+  return Math.pow(base, exp);
+};
+
+// For Dygraph.setDateSameTZ, below.
+Dygraph.dateSetters = {
+  ms: Date.prototype.setMilliseconds,
+  s: Date.prototype.setSeconds,
+  m: Date.prototype.setMinutes,
+  h: Date.prototype.setHours
+};
+
+/**
+ * This is like calling d.setSeconds(), d.setMinutes(), etc, except that it
+ * adjusts for time zone changes to keep the date/time parts consistent.
+ *
+ * For example, d.getSeconds(), d.getMinutes() and d.getHours() will all be
+ * the same before/after you call setDateSameTZ(d, {ms: 0}). The same is not
+ * true if you call d.setMilliseconds(0).
+ *
+ * @type {function(!Date, Object.<number>)}
+ */
+Dygraph.setDateSameTZ = function(d, parts) {
+  var tz = d.getTimezoneOffset();
+  for (var k in parts) {
+    if (!parts.hasOwnProperty(k)) continue;
+    var setter = Dygraph.dateSetters[k];
+    if (!setter) throw "Invalid setter: " + k;
+    setter.call(d, parts[k]);
+    if (d.getTimezoneOffset() != tz) {
+      d.setTime(d.getTime() + (tz - d.getTimezoneOffset()) * 60 * 1000);
+    }
+  }
+};