1<!DOCTYPE html>
2<!--
3Copyright (c) 2014 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/base/base.html">
9
10<script>
11'use strict';
12
13/**
14 * @fileoverview This contains an implementation of the EventTarget interface
15 * as defined by DOM Level 2 Events.
16 */
17tr.exportTo('tr.b', function() {
18
19  /**
20   * Creates a new EventTarget. This class implements the DOM level 2
21   * EventTarget interface and can be used wherever those are used.
22   * @constructor
23   */
24  function EventTarget() {
25  }
26  EventTarget.decorate = function(target) {
27    for (var k in EventTarget.prototype) {
28      if (k == 'decorate')
29        continue;
30      var v = EventTarget.prototype[k];
31      if (typeof v !== 'function')
32        continue;
33      target[k] = v;
34    }
35  };
36
37  EventTarget.prototype = {
38
39    /**
40     * Adds an event listener to the target.
41     * @param {string} type The name of the event.
42     * @param {!Function|{handleEvent:Function}} handler The handler for the
43     *     event. This is called when the event is dispatched.
44     */
45    addEventListener: function(type, handler) {
46      if (!this.listeners_)
47        this.listeners_ = Object.create(null);
48      if (!(type in this.listeners_)) {
49        this.listeners_[type] = [handler];
50      } else {
51        var handlers = this.listeners_[type];
52        if (handlers.indexOf(handler) < 0)
53          handlers.push(handler);
54      }
55    },
56
57    /**
58     * Removes an event listener from the target.
59     * @param {string} type The name of the event.
60     * @param {!Function|{handleEvent:Function}} handler The handler for the
61     *     event.
62     */
63    removeEventListener: function(type, handler) {
64      if (!this.listeners_)
65        return;
66      if (type in this.listeners_) {
67        var handlers = this.listeners_[type];
68        var index = handlers.indexOf(handler);
69        if (index >= 0) {
70          // Clean up if this was the last listener.
71          if (handlers.length == 1)
72            delete this.listeners_[type];
73          else
74            handlers.splice(index, 1);
75        }
76      }
77    },
78
79    /**
80     * Dispatches an event and calls all the listeners that are listening to
81     * the type of the event.
82     * @param {!cr.event.Event} event The event to dispatch.
83     * @return {boolean} Whether the default action was prevented. If someone
84     *     calls preventDefault on the event object then this returns false.
85     */
86    dispatchEvent: function(event) {
87      if (!this.listeners_)
88        return true;
89
90      // Since we are using DOM Event objects we need to override some of the
91      // properties and methods so that we can emulate this correctly.
92      var self = this;
93      event.__defineGetter__('target', function() {
94        return self;
95      });
96      var realPreventDefault = event.preventDefault;
97      event.preventDefault = function() {
98        realPreventDefault.call(this);
99        this.rawReturnValue = false;
100      };
101
102      var type = event.type;
103      var prevented = 0;
104      if (type in this.listeners_) {
105        // Clone to prevent removal during dispatch
106        var handlers = this.listeners_[type].concat();
107        for (var i = 0, handler; handler = handlers[i]; i++) {
108          if (handler.handleEvent)
109            prevented |= handler.handleEvent.call(handler, event) === false;
110          else
111            prevented |= handler.call(this, event) === false;
112        }
113      }
114
115      return !prevented && event.rawReturnValue;
116    },
117
118    hasEventListener: function(type) {
119      return this.listeners_[type] !== undefined;
120    }
121  };
122
123  var EventTargetHelper = {
124    decorate: function(target) {
125      for (var k in EventTargetHelper) {
126        if (k == 'decorate')
127          continue;
128        var v = EventTargetHelper[k];
129        if (typeof v !== 'function')
130          continue;
131        target[k] = v;
132      }
133      target.listenerCounts_ = {};
134    },
135
136    addEventListener: function(type, listener, useCapture) {
137      this.__proto__.addEventListener.call(
138          this, type, listener, useCapture);
139      if (this.listenerCounts_[type] === undefined)
140        this.listenerCounts_[type] = 0;
141      this.listenerCounts_[type]++;
142    },
143
144    removeEventListener: function(type, listener, useCapture) {
145      this.__proto__.removeEventListener.call(
146          this, type, listener, useCapture);
147      this.listenerCounts_[type]--;
148    },
149
150    hasEventListener: function(type) {
151      return this.listenerCounts_[type] > 0;
152    }
153  };
154
155  // Export
156  return {
157    EventTarget: EventTarget,
158    EventTargetHelper: EventTargetHelper
159  };
160});
161</script>
162