Tracking all event registrations, and deallocating them en masse makes
authorRobert Konigsberg <konigsberg@google.com>
Tue, 17 Apr 2012 18:56:48 +0000 (14:56 -0400)
committerRobert Konigsberg <konigsberg@google.com>
Tue, 17 Apr 2012 19:04:40 +0000 (15:04 -0400)
destroy() retain much less memory. e.g. in a simple case creating and
destoying the canonical demo, retains 10K less data upon destroy.

auto_tests/misc/local.html
dygraph-canvas.js
dygraph-range-selector.js
dygraph-utils.js
dygraph.js

index 6db0207..0af3cff 100644 (file)
   <script type="text/javascript" src="../tests/Proxy.js"></script>
   <script type="text/javascript" src="../tests/CanvasAssertions.js"></script>
   <script type="text/javascript" src="../tests/DygraphOps.js"></script>
-  <script type="text/javascript" src="../tests/sanity.js"></script>
-  <script type="text/javascript" src="../tests/simple_drawing.js"></script>
-  <script type="text/javascript" src="../tests/range_tests.js"></script>
+  <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/multi_csv.js"></script>
-  <script type="text/javascript" src="../tests/to_dom_coords.js"></script>
+  <script type="text/javascript" src="../tests/callback.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/error_bars.js"></script>
+  <script type="text/javascript" src="../tests/formats.js"></script>
   <script type="text/javascript" src="../tests/interaction_model.js"></script>
+  <script type="text/javascript" src="../tests/multiple_axes.js"></script>
+  <script type="text/javascript" src="../tests/multi_csv.js"></script>
+  <script type="text/javascript" src="../tests/no_hours.js"></script>
+  <script type="text/javascript" src="../tests/pathological_cases.js"></script>
+
+  <script type="text/javascript" src="../tests/range_tests.js"></script>
+  <script type="text/javascript" src="../tests/rolling_average.js"></script>
+  <script type="text/javascript" src="../tests/sanity.js"></script>
+  <script type="text/javascript" src="../tests/selection.js"></script>
+  <script type="text/javascript" src="../tests/scientific_notation.js"></script>
+  <script type="text/javascript" src="../tests/scrolling_div.js"></script>
+  <script type="text/javascript" src="../tests/simple_drawing.js"></script>
   <!--
   <script type="text/javascript" src="../tests/tickers.js"></script>
   -->
-  <script type="text/javascript" src="../tests/scrolling_div.js"></script>
-  <script type="text/javascript" src="../tests/custom_bars.js"></script>
-  <script type="text/javascript" src="../tests/css.js"></script>
-  <script type="text/javascript" src="../tests/selection.js"></script>
-  <script type="text/javascript" src="../tests/rolling_average.js"></script>
-  <script type="text/javascript" src="../tests/error_bars.js"></script>
-  <script type="text/javascript" src="../tests/annotations.js"></script>
-  <script type="text/javascript" src="../tests/scientific_notation.js"></script>
-  <script type="text/javascript" src="../tests/pathological_cases.js"></script>
-  <script type="text/javascript" src="../tests/date_formats.js"></script>
-  <script type="text/javascript" src="../tests/formats.js"></script>
+  <script type="text/javascript" src="../tests/to_dom_coords.js"></script>
   <script type="text/javascript" src="../tests/update_while_panning.js"></script>
-  <script type="text/javascript" src="../tests/no_hours.js"></script>
   <script type="text/javascript" src="../tests/update_options.js"></script>
   <script type="text/javascript" src="../tests/utils_test.js"></script>
-  <script type="text/javascript" src="../tests/multiple_axes.js"></script>
-  <script type="text/javascript" src="../tests/callback.js"></script>
 
 
   <script type="text/javascript">
index 3c1d40c..97dca1e 100644 (file)
@@ -634,13 +634,13 @@ DygraphCanvasRenderer.prototype._renderAnnotations = function() {
     div.style.borderColor = this.colors[p.name];
     a.div = div;
 
-    Dygraph.addEvent(div, 'click',
+    this.dygraph_.addEvent(div, 'click',
         bindEvt('clickHandler', 'annotationClickHandler', p, this));
-    Dygraph.addEvent(div, 'mouseover',
+    this.dygraph_.addEvent(div, 'mouseover',
         bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this));
-    Dygraph.addEvent(div, 'mouseout',
+    this.dygraph_.addEvent(div, 'mouseout',
         bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this));
-    Dygraph.addEvent(div, 'dblclick',
+    this.dygraph_.addEvent(div, 'dblclick',
         bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this));
 
     this.container.appendChild(div);
index 6cc184e..fd50bd0 100644 (file)
@@ -201,8 +201,8 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     isZooming = true;
     xLast = e.screenX;
     handle = e.target ? e.target : e.srcElement;
-    Dygraph.addEvent(topElem, 'mousemove', onZoom);
-    Dygraph.addEvent(topElem, 'mouseup', onZoomEnd);
+    self.dygraph_.addEvent(topElem, 'mousemove', onZoom);
+    self.dygraph_.addEvent(topElem, 'mouseup', onZoomEnd);
     self.fgcanvas_.style.cursor = 'col-resize';
   };
 
@@ -283,8 +283,8 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
       Dygraph.cancelEvent(e);
       isPanning = true;
       xLast = e.screenX;
-      Dygraph.addEvent(topElem, 'mousemove', onPan);
-      Dygraph.addEvent(topElem, 'mouseup', onPanEnd);
+      self.dygraph_.addEvent(topElem, 'mousemove', onPan);
+      self.dygraph_.addEvent(topElem, 'mouseup', onPanEnd);
     }
   };
 
@@ -382,10 +382,10 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
   };
 
   addTouchEvents = function(elem, fn) {
-      var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel'];
-      for (var i = 0; i < types.length; i++) {
-        Dygraph.addEvent(elem, types[i], fn);
-      }
+    var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel'];
+    for (var i = 0; i < types.length; i++) {
+      self.dygraph_.addEvent(elem, types[i], fn);
+    }
   };
 
   this.dygraph_.attrs_.interactionModel =
@@ -393,14 +393,14 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
   this.dygraph_.attrs_.panEdgeFraction = 0.0001;
 
   var dragStartEvent = window.opera ? 'mousedown' : 'dragstart';
-  Dygraph.addEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart);
-  Dygraph.addEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart);
+  this.dygraph_.addEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart);
+  this.dygraph_.addEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart);
 
   if (this.isUsingExcanvas_) {
-    Dygraph.addEvent(this.iePanOverlay_, 'mousedown', onPanStart);
+    this.dygraph_.addEvent(this.iePanOverlay_, 'mousedown', onPanStart);
   } else {
-    Dygraph.addEvent(this.fgcanvas_, 'mousedown', onPanStart);
-    Dygraph.addEvent(this.fgcanvas_, 'mousemove', onCanvasMouseMove);
+    this.dygraph_.addEvent(this.fgcanvas_, 'mousedown', onPanStart);
+    this.dygraph_.addEvent(this.fgcanvas_, 'mousemove', onCanvasMouseMove);
   }
 
   // Touch events
index c6c3a60..e2af0b7 100644 (file)
@@ -136,13 +136,14 @@ Dygraph.getContext = function(canvas) {
  * @param { Function } fn The function to call on the event. The function takes
  * one parameter: the event object.
  */
-Dygraph.addEvent = function addEvent(elem, type, fn) {
+Dygraph.prototype.addEvent = function addEvent(elem, type, fn) {
   if (elem.addEventListener) {
     elem.addEventListener(type, fn, false);
   } else {
     elem[type+fn] = function(){fn(window.event);};
     elem.attachEvent('on'+type, elem[type+fn]);
   }
+  this.registeredEvents_.push({ elem : elem, type : type, fn : fn });
 };
 
 /**
@@ -154,7 +155,7 @@ Dygraph.addEvent = function addEvent(elem, type, fn) {
  * @param { Function } fn The function to call on the event. The function takes
  * one parameter: the event object.
  */
-Dygraph.removeEvent = function addEvent(elem, type, fn) {
+Dygraph.prototype.removeEvent = function addEvent(elem, type, fn) {
   if (elem.removeEventListener) {
     elem.removeEventListener(type, fn, false);
   } else {
index e1cdd4e..47c6d2c 100644 (file)
@@ -407,6 +407,8 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
   this.setIndexByName_ = {};
   this.datasetIndex_ = [];
 
+  this.registeredEvents_ = [];
+
   // Create the containing DIV and other interactive elements
   this.createInterface_();
 
@@ -953,12 +955,12 @@ Dygraph.prototype.createInterface_ = function() {
   this.mouseMoveHandler = function(e) {
     dygraph.mouseMove_(e);
   };
-  Dygraph.addEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler);
+  this.addEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler);
   
   this.mouseOutHandler = function(e) {
     dygraph.mouseOut_(e);
   };
-  Dygraph.addEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler);
+  this.addEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler);
 
   this.createDragInterface_();
 
@@ -968,7 +970,7 @@ Dygraph.prototype.createInterface_ = function() {
 
   // Update when the window is resized.
   // TODO(danvk): drop frames depending on complexity of the chart.
-  Dygraph.addEvent(window, 'resize', this.resizeHandler);
+  this.addEvent(window, 'resize', this.resizeHandler);
 };
 
 /**
@@ -983,11 +985,17 @@ Dygraph.prototype.destroy = function() {
       node.removeChild(node.firstChild);
     }
   };
-  
-  // remove mouse event handlers
-  Dygraph.removeEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler);
-  Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler);
-  Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseUpHandler_);
+  for (var idx = 0; idx < this.registeredEvents_.length; idx++) {
+    var reg = this.registeredEvents_[idx];
+    this.removeEvent(reg.elem, reg.type, reg.fn);
+  }
+  this.registeredEvents_ = [];
+
+  // remove mouse event handlers (This may not be necessary anymore)
+  this.removeEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler);
+  this.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler);
+  this.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseUpHandler_);
   removeRecursive(this.maindiv_);
 
   var nullOut = function(obj) {
@@ -998,7 +1006,7 @@ Dygraph.prototype.destroy = function() {
     }
   };
   // remove event handlers
-  Dygraph.removeEvent(window,'resize',this.resizeHandler);
+  this.removeEvent(window,'resize',this.resizeHandler);
   this.resizeHandler = null;
   // These may not all be necessary, but it can't hurt...
   nullOut(this.layout_);
@@ -1217,7 +1225,8 @@ Dygraph.prototype.createDragInterface_ = function() {
     boundedDates: null, // [minDate, maxDate]
     boundedValues: null, // [[minValue, maxValue] ...]
 
-    initializeMouseDown: function(event, g, context) {
+    // contextB is the same thing as this context object but renamed.
+    initializeMouseDown: function(event, g, contextB) {
       // prevents mouse drags from selecting page text.
       if (event.preventDefault) {
         event.preventDefault();  // Firefox, Chrome, etc.
@@ -1226,11 +1235,11 @@ Dygraph.prototype.createDragInterface_ = function() {
         event.cancelBubble = true;
       }
 
-      context.px = Dygraph.findPosX(g.canvas_);
-      context.py = Dygraph.findPosY(g.canvas_);
-      context.dragStartX = g.dragGetX_(event, context);
-      context.dragStartY = g.dragGetY_(event, context);
-      context.cancelNextDblclick = false;
+      contextB.px = Dygraph.findPosX(g.canvas_);
+      contextB.py = Dygraph.findPosY(g.canvas_);
+      contextB.dragStartX = g.dragGetX_(event, contextB);
+      contextB.dragStartY = g.dragGetY_(event, contextB);
+      contextB.cancelNextDblclick = false;
     }
   };
 
@@ -1248,7 +1257,7 @@ Dygraph.prototype.createDragInterface_ = function() {
 
   for (var eventName in interactionModel) {
     if (!interactionModel.hasOwnProperty(eventName)) continue;
-    Dygraph.addEvent(this.mouseEventElement_, eventName,
+    this.addEvent(this.mouseEventElement_, eventName,
         bindHandler(interactionModel[eventName]));
   }
 
@@ -1272,7 +1281,7 @@ Dygraph.prototype.createDragInterface_ = function() {
     }
   };
 
-  Dygraph.addEvent(document, 'mouseup', this.mouseUpHandler_);
+  this.addEvent(document, 'mouseup', this.mouseUpHandler_);
 };
 
 /**