Javascript  |  143行  |  4.16 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

/**
 * @fileoverview This contains an implementation of the EventTarget interface
 * as defined by DOM Level 2 Events.
 */
base.exportTo('base', function() {

  /**
   * Creates a new EventTarget. This class implements the DOM level 2
   * EventTarget interface and can be used wherever those are used.
   * @constructor
   */
  function EventTarget() {
  }

  EventTarget.prototype = {

    /**
     * Adds an event listener to the target.
     * @param {string} type The name of the event.
     * @param {!Function|{handleEvent:Function}} handler The handler for the
     *     event. This is called when the event is dispatched.
     */
    addEventListener: function(type, handler) {
      if (!this.listeners_)
        this.listeners_ = Object.create(null);
      if (!(type in this.listeners_)) {
        this.listeners_[type] = [handler];
      } else {
        var handlers = this.listeners_[type];
        if (handlers.indexOf(handler) < 0)
          handlers.push(handler);
      }
    },

    /**
     * Removes an event listener from the target.
     * @param {string} type The name of the event.
     * @param {!Function|{handleEvent:Function}} handler The handler for the
     *     event.
     */
    removeEventListener: function(type, handler) {
      if (!this.listeners_)
        return;
      if (type in this.listeners_) {
        var handlers = this.listeners_[type];
        var index = handlers.indexOf(handler);
        if (index >= 0) {
          // Clean up if this was the last listener.
          if (handlers.length == 1)
            delete this.listeners_[type];
          else
            handlers.splice(index, 1);
        }
      }
    },

    /**
     * Dispatches an event and calls all the listeners that are listening to
     * the type of the event.
     * @param {!cr.event.Event} event The event to dispatch.
     * @return {boolean} Whether the default action was prevented. If someone
     *     calls preventDefault on the event object then this returns false.
     */
    dispatchEvent: function(event) {
      if (!this.listeners_)
        return true;

      // Since we are using DOM Event objects we need to override some of the
      // properties and methods so that we can emulate this correctly.
      var self = this;
      event.__defineGetter__('target', function() {
        return self;
      });
      event.preventDefault = function() {
        this.returnValue = false;
      };

      var type = event.type;
      var prevented = 0;
      if (type in this.listeners_) {
        // Clone to prevent removal during dispatch
        var handlers = this.listeners_[type].concat();
        for (var i = 0, handler; handler = handlers[i]; i++) {
          if (handler.handleEvent)
            prevented |= handler.handleEvent.call(handler, event) === false;
          else
            prevented |= handler.call(this, event) === false;
        }
      }

      return !prevented && event.returnValue;
    },

    hasEventListener: function(type) {
      return this.listeners_[type] !== undefined;
    }
  };

  var EventTargetHelper = {
    decorate: function(target) {
      for (var k in EventTargetHelper) {
        if (k == 'decorate')
          continue;
        var v = EventTargetHelper[k];
        if (typeof v !== 'function')
          continue;
        target[k] = v;
      }
      target.listenerCounts_ = {};
    },

    addEventListener: function(type, listener, useCapture) {
      this.__proto__.addEventListener.call(
          this, type, listener, useCapture);
      if (this.listenerCounts_[type] === undefined)
        this.listenerCounts_[type] = 0;
      this.listenerCounts_[type]++;
    },

    removeEventListener: function(type, listener, useCapture) {
      this.__proto__.removeEventListener.call(
          this, type, listener, useCapture);
      this.listenerCounts_[type]--;
    },

    hasEventListener: function(type) {
      return this.listenerCounts_[type] > 0;
    }
  };

  // Export
  return {
    EventTarget: EventTarget,
    EventTargetHelper: EventTargetHelper
  };
});