1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * @fileoverview This contains an implementation of the EventTarget interface
7 * as defined by DOM Level 2 Events.
8 */
9base.exportTo('base', function() {
10
11  /**
12   * Creates a new EventTarget. This class implements the DOM level 2
13   * EventTarget interface and can be used wherever those are used.
14   * @constructor
15   */
16  function EventTarget() {
17  }
18
19  EventTarget.prototype = {
20
21    /**
22     * Adds an event listener to the target.
23     * @param {string} type The name of the event.
24     * @param {!Function|{handleEvent:Function}} handler The handler for the
25     *     event. This is called when the event is dispatched.
26     */
27    addEventListener: function(type, handler) {
28      if (!this.listeners_)
29        this.listeners_ = Object.create(null);
30      if (!(type in this.listeners_)) {
31        this.listeners_[type] = [handler];
32      } else {
33        var handlers = this.listeners_[type];
34        if (handlers.indexOf(handler) < 0)
35          handlers.push(handler);
36      }
37    },
38
39    /**
40     * Removes an event listener from the target.
41     * @param {string} type The name of the event.
42     * @param {!Function|{handleEvent:Function}} handler The handler for the
43     *     event.
44     */
45    removeEventListener: function(type, handler) {
46      if (!this.listeners_)
47        return;
48      if (type in this.listeners_) {
49        var handlers = this.listeners_[type];
50        var index = handlers.indexOf(handler);
51        if (index >= 0) {
52          // Clean up if this was the last listener.
53          if (handlers.length == 1)
54            delete this.listeners_[type];
55          else
56            handlers.splice(index, 1);
57        }
58      }
59    },
60
61    /**
62     * Dispatches an event and calls all the listeners that are listening to
63     * the type of the event.
64     * @param {!cr.event.Event} event The event to dispatch.
65     * @return {boolean} Whether the default action was prevented. If someone
66     *     calls preventDefault on the event object then this returns false.
67     */
68    dispatchEvent: function(event) {
69      if (!this.listeners_)
70        return true;
71
72      // Since we are using DOM Event objects we need to override some of the
73      // properties and methods so that we can emulate this correctly.
74      var self = this;
75      event.__defineGetter__('target', function() {
76        return self;
77      });
78      event.preventDefault = function() {
79        this.returnValue = false;
80      };
81
82      var type = event.type;
83      var prevented = 0;
84      if (type in this.listeners_) {
85        // Clone to prevent removal during dispatch
86        var handlers = this.listeners_[type].concat();
87        for (var i = 0, handler; handler = handlers[i]; i++) {
88          if (handler.handleEvent)
89            prevented |= handler.handleEvent.call(handler, event) === false;
90          else
91            prevented |= handler.call(this, event) === false;
92        }
93      }
94
95      return !prevented && event.returnValue;
96    }
97  };
98
99  // Export
100  return {
101    EventTarget: EventTarget
102  };
103});
104