1/*
2 * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26WebInspector.Object = function()
27{
28}
29
30WebInspector.Object.addConstructorFunctions = function(subclassConstructor)
31{
32    // Copies the relevant functions the subclass constructor.
33    for (var property in WebInspector.Object) {
34        var value = WebInspector.Object[property];
35        if (typeof value !== "function")
36            continue;
37        if (value === arguments.callee)
38            continue;
39        subclassConstructor[property] = value;
40    }
41}
42
43WebInspector.Object.addEventListener = function(eventType, listener, thisObject)
44{
45    thisObject = thisObject || null;
46
47    console.assert(eventType, "Object.addEventListener: invalid event type ", eventType, "(listener: ", listener, "thisObject: ", thisObject, ")");
48    if (!eventType)
49        return;
50
51    console.assert(listener, "Object.addEventListener: invalid listener ", listener, "(event type: ", eventType, "thisObject: ", thisObject, ")");
52    if (!listener)
53        return;
54
55    if (!this._listeners)
56        this._listeners = {};
57
58    var listeners = this._listeners[eventType];
59    if (!listeners)
60        listeners = this._listeners[eventType] = [];
61
62    // Prevent registering multiple times.
63    for (var i = 0; i < listeners.length; ++i) {
64        if (listeners[i].listener === listener && listeners[i].thisObject === thisObject)
65            return;
66    }
67
68    listeners.push({thisObject: thisObject, listener: listener});
69};
70
71WebInspector.Object.removeEventListener = function(eventType, listener, thisObject)
72{
73    eventType = eventType || null;
74    listener = listener || null;
75    thisObject = thisObject || null;
76
77    if (!this._listeners)
78        return;
79
80    if (!eventType) {
81        for (eventType in this._listeners)
82            this.removeEventListener(eventType, listener, thisObject);
83        return;
84    }
85
86    var listeners = this._listeners[eventType];
87    if (!listeners)
88        return;
89
90    for (var i = listeners.length - 1; i >= 0; --i) {
91        if (listener && listeners[i].listener === listener && listeners[i].thisObject === thisObject)
92            listeners.splice(i, 1);
93        else if (!listener && thisObject && listeners[i].thisObject === thisObject)
94            listeners.splice(i, 1);
95    }
96
97    if (!listeners.length)
98        delete this._listeners[eventType];
99
100    if (!Object.keys(this._listeners).length)
101        delete this._listeners;
102};
103
104WebInspector.Object.removeAllListeners = function()
105{
106    delete this._listeners;
107};
108
109WebInspector.Object.hasEventListeners = function(eventType)
110{
111    if (!this._listeners || !this._listeners[eventType])
112        return false;
113    return true;
114};
115
116WebInspector.Object.prototype = {
117    constructor: WebInspector.Object,
118
119    addEventListener: WebInspector.Object.addEventListener,
120
121    removeEventListener: WebInspector.Object.removeEventListener,
122
123    removeAllListeners: WebInspector.Object.removeAllListeners,
124
125    hasEventListeners: WebInspector.Object.hasEventListeners,
126
127    dispatchEventToListeners: function(eventType, eventData)
128    {
129        var event = new WebInspector.Event(this, eventType, eventData);
130
131        function dispatch(object)
132        {
133            if (!object || !object._listeners || !object._listeners[eventType] || event._stoppedPropagation)
134                return;
135
136            // Make a copy with slice so mutations during the loop doesn't affect us.
137            var listenersForThisEvent = object._listeners[eventType].slice(0);
138
139            // Iterate over the listeners and call them. Stop if stopPropagation is called.
140            for (var i = 0; i < listenersForThisEvent.length; ++i) {
141                listenersForThisEvent[i].listener.call(listenersForThisEvent[i].thisObject, event);
142                if (event._stoppedPropagation)
143                    break;
144            }
145        }
146
147        // Dispatch to listeners of this specific object.
148        dispatch(this);
149
150        // Allow propagation again so listeners on the constructor always have a crack at the event.
151        event._stoppedPropagation = false;
152
153        // Dispatch to listeners on all constructors up the prototype chain, including the immediate constructor.
154        var constructor = this.constructor;
155        while (constructor) {
156            dispatch(constructor);
157
158            if (!constructor.prototype.__proto__)
159                break;
160
161            constructor = constructor.prototype.__proto__.constructor;
162        }
163
164        return event.defaultPrevented;
165    }
166}
167
168WebInspector.Event = function(target, type, data)
169{
170    this.target = target;
171    this.type = type;
172    this.data = data;
173    this.defaultPrevented = false;
174    this._stoppedPropagation = false;
175}
176
177WebInspector.Event.prototype = {
178    constructor: WebInspector.Event,
179
180    stopPropagation: function()
181    {
182        this._stoppedPropagation = true;
183    },
184
185    preventDefault: function()
186    {
187        this.defaultPrevented = true;
188    }
189}
190
191WebInspector.notifications = new WebInspector.Object;
192