bea3d34f5c367daa7a975d2e4e7d20a9dde4b45b
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Base / Object.js
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
26 WebInspector.Object = class Object
27 {
28     constructor()
29     {
30         // FIXME: Remove once <https://webkit.org/b/142862> is fixed.
31     }
32
33     // Static
34
35     static addEventListener(eventType, listener, thisObject)
36     {
37         thisObject = thisObject || null;
38
39         console.assert(eventType, "Object.addEventListener: invalid event type ", eventType, "(listener: ", listener, "thisObject: ", thisObject, ")");
40         if (!eventType)
41             return;
42
43         console.assert(listener, "Object.addEventListener: invalid listener ", listener, "(event type: ", eventType, "thisObject: ", thisObject, ")");
44         if (!listener)
45             return;
46
47         if (!this._listeners)
48             this._listeners = {};
49
50         var listeners = this._listeners[eventType];
51         if (!listeners)
52             listeners = this._listeners[eventType] = [];
53
54         // Prevent registering multiple times.
55         for (var i = 0; i < listeners.length; ++i) {
56             if (listeners[i].listener === listener && listeners[i].thisObject === thisObject)
57                 return;
58         }
59
60         listeners.push({thisObject, listener});
61     }
62
63     static removeEventListener(eventType, listener, thisObject)
64     {
65         eventType = eventType || null;
66         listener = listener || null;
67         thisObject = thisObject || null;
68
69         if (!this._listeners)
70             return;
71
72         if (!eventType) {
73             for (eventType in this._listeners)
74                 this.removeEventListener(eventType, listener, thisObject);
75             return;
76         }
77
78         var listeners = this._listeners[eventType];
79         if (!listeners)
80             return;
81
82         for (var i = listeners.length - 1; i >= 0; --i) {
83             if (listener && listeners[i].listener === listener && listeners[i].thisObject === thisObject)
84                 listeners.splice(i, 1);
85             else if (!listener && thisObject && listeners[i].thisObject === thisObject)
86                 listeners.splice(i, 1);
87         }
88
89         if (!listeners.length)
90             delete this._listeners[eventType];
91
92         if (!Object.keys(this._listeners).length)
93             delete this._listeners;
94     }
95
96     static removeAllListeners()
97     {
98         delete this._listeners;
99     }
100
101     static hasEventListeners(eventType)
102     {
103         if (!this._listeners || !this._listeners[eventType])
104             return false;
105         return true;
106     }
107
108     // Public
109
110     addEventListener() { return WebInspector.Object.addEventListener.apply(this, arguments); }
111     removeEventListener() { return WebInspector.Object.removeEventListener.apply(this, arguments); }
112     removeAllListeners() { return WebInspector.Object.removeAllListeners.apply(this, arguments); }
113     hasEventListeners() { return WebInspector.Object.hasEventListeners.apply(this, arguments); }
114
115     dispatchEventToListeners(eventType, eventData)
116     {
117         var event = new WebInspector.Event(this, eventType, eventData);
118
119         function dispatch(object)
120         {
121             if (!object || !object._listeners || !object._listeners[eventType] || event._stoppedPropagation)
122                 return;
123
124             // Make a copy with slice so mutations during the loop doesn't affect us.
125             var listenersForThisEvent = object._listeners[eventType].slice(0);
126
127             // Iterate over the listeners and call them. Stop if stopPropagation is called.
128             for (var i = 0; i < listenersForThisEvent.length; ++i) {
129                 listenersForThisEvent[i].listener.call(listenersForThisEvent[i].thisObject, event);
130                 if (event._stoppedPropagation)
131                     break;
132             }
133         }
134
135         // Dispatch to listeners of this specific object.
136         dispatch(this);
137
138         // Allow propagation again so listeners on the constructor always have a crack at the event.
139         event._stoppedPropagation = false;
140
141         // Dispatch to listeners on all constructors up the prototype chain, including the immediate constructor.
142         var constructor = this.constructor;
143         while (constructor) {
144             dispatch(constructor);
145
146             if (!constructor.prototype.__proto__)
147                 break;
148
149             constructor = constructor.prototype.__proto__.constructor;
150         }
151
152         return event.defaultPrevented;
153     }
154 };
155
156 // FIXME: Uses arguments.callee, so it cannot be in the class.
157 WebInspector.Object.deprecatedAddConstructorFunctions = function(subclassConstructor)
158 {
159     // Copies the relevant functions to the subclass constructor.
160     var list = ["addEventListener", "removeEventListener", "removeAllListeners", "hasEventListeners"];
161     for (var property of list) {
162         var value = WebInspector.Object[property];
163         if (typeof value !== "function")
164             continue;
165         if (value === arguments.callee)
166             continue;
167         subclassConstructor[property] = value;
168     }
169 };
170
171 WebInspector.Event = class Event
172 {
173     constructor(target, type, data)
174     {
175         this.target = target;
176         this.type = type;
177         this.data = data;
178         this.defaultPrevented = false;
179         this._stoppedPropagation = false;
180     }
181
182     stopPropagation()
183     {
184         this._stoppedPropagation = true;
185     }
186
187     preventDefault()
188     {
189         this.defaultPrevented = true;
190     }
191 };
192
193 WebInspector.notifications = new WebInspector.Object;
194
195 WebInspector.Notification = {
196     GlobalModifierKeysDidChange: "global-modifiers-did-change",
197     PageArchiveStarted: "page-archive-started",
198     PageArchiveEnded: "page-archive-ended",
199     ExtraDomainsActivated: "extra-domains-activated",
200 };