15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/**
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @fileoverview EventTracker is a simple class that manages the addition and
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * removal of DOM event listeners. In particular, it keeps track of all
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * listeners that have been added and makes it easy to remove some or all of
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * them without requiring all the information again. This is particularly handy
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * when the listener is a generated function such as a lambda or the result of
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * calling Function.bind.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/**
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * The type of the internal tracking entry. TODO(dbeam): move this back to
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * EventTracker.Entry when https://github.com/google/closure-compiler/issues/544
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * is fixed.
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @typedef {{node: !Node,
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) *            eventType: string,
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) *            listener: Function,
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) *            capture: boolean}}
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)var EventTrackerEntry;
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/**
2603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Create an EventTracker to track a set of events.
2703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * EventTracker instances are typically tied 1:1 with other objects or
2803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * DOM elements whose listeners should be removed when the object is disposed
2903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * or the corresponding elements are removed from the DOM.
3003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @constructor
3103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */
3203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)function EventTracker() {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
3403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * @type {Array.<EventTrackerEntry>}
3503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * @private
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
3703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  this.listeners_ = [];
3803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)EventTracker.prototype = {
4103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  /**
4203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * Add an event listener - replacement for Node.addEventListener.
4303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * @param {!Node} node The DOM node to add a listener to.
4403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * @param {string} eventType The type of event to subscribe to.
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @param {EventListener|Function} listener The listener to add.
4603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * @param {boolean=} opt_capture Whether to invoke during the capture phase.
4703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   */
4803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  add: function(node, eventType, listener, opt_capture) {
4903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    var capture = !!opt_capture;
5003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    var h = {
5103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      node: node,
5203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      eventType: eventType,
5303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      listener: listener,
5403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      capture: capture,
5503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    };
5603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    this.listeners_.push(h);
5703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    node.addEventListener(eventType, listener, capture);
5803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  },
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
6103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * Remove any specified event listeners added with this EventTracker.
6203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * @param {!Node} node The DOM node to remove a listener from.
6303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * @param {?string} eventType The type of event to remove.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
6503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  remove: function(node, eventType) {
6603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    this.listeners_ = this.listeners_.filter(function(h) {
6703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (h.node == node && (!eventType || (h.eventType == eventType))) {
6803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        EventTracker.removeEventListener_(h);
6903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        return false;
7003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      }
7103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      return true;
7203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    });
7303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  },
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  /**
7603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   * Remove all event listeners added with this EventTracker.
7703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)   */
7803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  removeAll: function() {
7903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    this.listeners_.forEach(EventTracker.removeEventListener_);
8003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    this.listeners_ = [];
8103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
8203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)};
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/**
8503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Remove a single event listener given it's tracking entry. It's up to the
8603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * caller to ensure the entry is removed from listeners_.
8703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {EventTrackerEntry} h The entry describing the listener to remove.
8803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @private
8903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */
9003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)EventTracker.removeEventListener_ = function(h) {
9103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  h.node.removeEventListener(h.eventType, h.listener, h.capture);
9203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)};
93