12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis// Use of this source code is governed by a BSD-style license that can be
3010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis// found in the LICENSE file.
4010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis'use strict';
666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
7010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis/**
8010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @fileoverview This contains an implementation of the EventTarget interface
9010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * as defined by DOM Level 2 Events.
10010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.exportTo('base', function() {
12010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
13010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  /**
14010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * Creates a new EventTarget. This class implements the DOM level 2
15010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * EventTarget interface and can be used wherever those are used.
16010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * @constructor
17010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   */
18010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  function EventTarget() {
19010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  }
20010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
21010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  EventTarget.prototype = {
22010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
23010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
24010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Adds an event listener to the target.
25010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * @param {string} type The name of the event.
26010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * @param {!Function|{handleEvent:Function}} handler The handler for the
27010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     *     event. This is called when the event is dispatched.
28010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
29010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    addEventListener: function(type, handler) {
30010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (!this.listeners_)
31010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        this.listeners_ = Object.create(null);
32010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (!(type in this.listeners_)) {
33010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        this.listeners_[type] = [handler];
34010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      } else {
35010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        var handlers = this.listeners_[type];
36010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        if (handlers.indexOf(handler) < 0)
37010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          handlers.push(handler);
38010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
39010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
40010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
41010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
42010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Removes an event listener from the target.
43010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * @param {string} type The name of the event.
44010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * @param {!Function|{handleEvent:Function}} handler The handler for the
45010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     *     event.
46010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
47010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    removeEventListener: function(type, handler) {
48010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (!this.listeners_)
49010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        return;
50010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (type in this.listeners_) {
51010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        var handlers = this.listeners_[type];
52010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        var index = handlers.indexOf(handler);
53010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        if (index >= 0) {
54010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          // Clean up if this was the last listener.
55010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          if (handlers.length == 1)
56010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis            delete this.listeners_[type];
57010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          else
58010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis            handlers.splice(index, 1);
59010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        }
60010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
61010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
62010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
63010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
64010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Dispatches an event and calls all the listeners that are listening to
65010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * the type of the event.
66010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * @param {!cr.event.Event} event The event to dispatch.
67010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * @return {boolean} Whether the default action was prevented. If someone
68010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     *     calls preventDefault on the event object then this returns false.
69010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
70010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    dispatchEvent: function(event) {
71010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (!this.listeners_)
72010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        return true;
73010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
74010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // Since we are using DOM Event objects we need to override some of the
75010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // properties and methods so that we can emulate this correctly.
76010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      var self = this;
77010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      event.__defineGetter__('target', function() {
78010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        return self;
79010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      });
80010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      event.preventDefault = function() {
81010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        this.returnValue = false;
82010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      };
83010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
84010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      var type = event.type;
85010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      var prevented = 0;
86010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (type in this.listeners_) {
87010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        // Clone to prevent removal during dispatch
88010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        var handlers = this.listeners_[type].concat();
89010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        for (var i = 0, handler; handler = handlers[i]; i++) {
90010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          if (handler.handleEvent)
91010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis            prevented |= handler.handleEvent.call(handler, event) === false;
92010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          else
93010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis            prevented |= handler.call(this, event) === false;
94010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        }
95010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
96010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
97010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      return !prevented && event.returnValue;
9866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
9966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
10066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    hasEventListener: function(type) {
10166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      return this.listeners_[type] !== undefined;
10266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    }
10366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis  };
10466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
10566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis  var EventTargetHelper = {
10666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    decorate: function(target) {
10766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      for (var k in EventTargetHelper) {
10866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        if (k == 'decorate')
10966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          continue;
11066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        var v = EventTargetHelper[k];
11166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        if (typeof v !== 'function')
11266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          continue;
11366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        target[k] = v;
11466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      }
11566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      target.listenerCounts_ = {};
11666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
11766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
11866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    addEventListener: function(type, listener, useCapture) {
11966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.__proto__.addEventListener.call(
12066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          this, type, listener, useCapture);
12166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      if (this.listenerCounts_[type] === undefined)
12266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        this.listenerCounts_[type] = 0;
12366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.listenerCounts_[type]++;
12466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
12566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
12666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    removeEventListener: function(type, listener, useCapture) {
12766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.__proto__.removeEventListener.call(
12866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          this, type, listener, useCapture);
12966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.listenerCounts_[type]--;
13066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
13166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
13266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    hasEventListener: function(type) {
13366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      return this.listenerCounts_[type] > 0;
134010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    }
135010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  };
136010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
137010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  // Export
138010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  return {
13966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    EventTarget: EventTarget,
14066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    EventTargetHelper: EventTargetHelper
141010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  };
142010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis});
143