OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
13 * distribution. | 13 * distribution. |
14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
17 * | 17 * |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | |
31 /** | 30 /** |
32 * @constructor | 31 * @unrestricted |
33 * @extends {WebInspector.Widget} | |
34 */ | 32 */ |
35 WebInspector.Dialog = function() | 33 WebInspector.Dialog = class extends WebInspector.Widget { |
36 { | 34 constructor() { |
37 WebInspector.Widget.call(this, true); | 35 super(true); |
38 this.markAsRoot(); | 36 this.markAsRoot(); |
39 this.registerRequiredCSS("ui/dialog.css"); | 37 this.registerRequiredCSS('ui/dialog.css'); |
40 | 38 |
41 this.contentElement.createChild("content"); | 39 this.contentElement.createChild('content'); |
42 this.contentElement.tabIndex = 0; | 40 this.contentElement.tabIndex = 0; |
43 this.contentElement.addEventListener("focus", this._onFocus.bind(this), fals
e); | 41 this.contentElement.addEventListener('focus', this._onFocus.bind(this), fals
e); |
44 this._keyDownBound = this._onKeyDown.bind(this); | 42 this._keyDownBound = this._onKeyDown.bind(this); |
45 | 43 |
46 this._wrapsContent = false; | 44 this._wrapsContent = false; |
47 this._dimmed = false; | 45 this._dimmed = false; |
48 /** @type {!Map<!HTMLElement, number>} */ | 46 /** @type {!Map<!HTMLElement, number>} */ |
49 this._tabIndexMap = new Map(); | 47 this._tabIndexMap = new Map(); |
| 48 } |
| 49 |
| 50 /** |
| 51 * @return {boolean} |
| 52 */ |
| 53 static hasInstance() { |
| 54 return !!WebInspector.Dialog._instance; |
| 55 } |
| 56 |
| 57 /** |
| 58 * @param {!WebInspector.Widget} view |
| 59 */ |
| 60 static setModalHostView(view) { |
| 61 WebInspector.Dialog._modalHostView = view; |
| 62 } |
| 63 |
| 64 /** |
| 65 * FIXME: make utility method in Dialog, so clients use it instead of this get
ter. |
| 66 * Method should be like Dialog.showModalElement(position params, reposition c
allback). |
| 67 * @return {?WebInspector.Widget} |
| 68 */ |
| 69 static modalHostView() { |
| 70 return WebInspector.Dialog._modalHostView; |
| 71 } |
| 72 |
| 73 static modalHostRepositioned() { |
| 74 if (WebInspector.Dialog._instance) |
| 75 WebInspector.Dialog._instance._position(); |
| 76 } |
| 77 |
| 78 /** |
| 79 * @override |
| 80 */ |
| 81 show() { |
| 82 if (WebInspector.Dialog._instance) |
| 83 WebInspector.Dialog._instance.detach(); |
| 84 WebInspector.Dialog._instance = this; |
| 85 |
| 86 var document = /** @type {!Document} */ (WebInspector.Dialog._modalHostView.
element.ownerDocument); |
| 87 this._disableTabIndexOnElements(document); |
| 88 |
| 89 this._glassPane = new WebInspector.GlassPane(document, this._dimmed); |
| 90 this._glassPane.element.addEventListener('click', this._onGlassPaneClick.bin
d(this), false); |
| 91 this.element.ownerDocument.body.addEventListener('keydown', this._keyDownBou
nd, false); |
| 92 |
| 93 super.show(this._glassPane.element); |
| 94 |
| 95 this._position(); |
| 96 this._focusRestorer = new WebInspector.WidgetFocusRestorer(this); |
| 97 } |
| 98 |
| 99 /** |
| 100 * @override |
| 101 */ |
| 102 detach() { |
| 103 this._focusRestorer.restore(); |
| 104 |
| 105 this.element.ownerDocument.body.removeEventListener('keydown', this._keyDown
Bound, false); |
| 106 super.detach(); |
| 107 |
| 108 this._glassPane.dispose(); |
| 109 delete this._glassPane; |
| 110 |
| 111 this._restoreTabIndexOnElements(); |
| 112 |
| 113 delete WebInspector.Dialog._instance; |
| 114 } |
| 115 |
| 116 addCloseButton() { |
| 117 var closeButton = this.contentElement.createChild('div', 'dialog-close-butto
n', 'dt-close-button'); |
| 118 closeButton.gray = true; |
| 119 closeButton.addEventListener('click', this.detach.bind(this), false); |
| 120 } |
| 121 |
| 122 /** |
| 123 * @param {number=} positionX |
| 124 * @param {number=} positionY |
| 125 */ |
| 126 setPosition(positionX, positionY) { |
| 127 this._defaultPositionX = positionX; |
| 128 this._defaultPositionY = positionY; |
| 129 } |
| 130 |
| 131 /** |
| 132 * @param {!Size} size |
| 133 */ |
| 134 setMaxSize(size) { |
| 135 this._maxSize = size; |
| 136 } |
| 137 |
| 138 /** |
| 139 * @param {boolean} wraps |
| 140 */ |
| 141 setWrapsContent(wraps) { |
| 142 this.element.classList.toggle('wraps-content', wraps); |
| 143 this._wrapsContent = wraps; |
| 144 } |
| 145 |
| 146 /** |
| 147 * @param {boolean} dimmed |
| 148 */ |
| 149 setDimmed(dimmed) { |
| 150 this._dimmed = dimmed; |
| 151 } |
| 152 |
| 153 contentResized() { |
| 154 if (this._wrapsContent) |
| 155 this._position(); |
| 156 } |
| 157 |
| 158 /** |
| 159 * @param {!Document} document |
| 160 */ |
| 161 _disableTabIndexOnElements(document) { |
| 162 this._tabIndexMap.clear(); |
| 163 for (var node = document; node; node = node.traverseNextNode(document)) { |
| 164 if (node instanceof HTMLElement) { |
| 165 var element = /** @type {!HTMLElement} */ (node); |
| 166 var tabIndex = element.tabIndex; |
| 167 if (tabIndex >= 0) { |
| 168 this._tabIndexMap.set(element, tabIndex); |
| 169 element.tabIndex = -1; |
| 170 } |
| 171 } |
| 172 } |
| 173 } |
| 174 |
| 175 _restoreTabIndexOnElements() { |
| 176 for (var element of this._tabIndexMap.keys()) |
| 177 element.tabIndex = /** @type {number} */ (this._tabIndexMap.get(element)); |
| 178 this._tabIndexMap.clear(); |
| 179 } |
| 180 |
| 181 /** |
| 182 * @param {!Event} event |
| 183 */ |
| 184 _onFocus(event) { |
| 185 this.focus(); |
| 186 } |
| 187 |
| 188 /** |
| 189 * @param {!Event} event |
| 190 */ |
| 191 _onGlassPaneClick(event) { |
| 192 if (!this.element.isSelfOrAncestor(/** @type {?Node} */ (event.target))) |
| 193 this.detach(); |
| 194 } |
| 195 |
| 196 _position() { |
| 197 var container = WebInspector.Dialog._modalHostView.element; |
| 198 |
| 199 var width = container.offsetWidth - 10; |
| 200 var height = container.offsetHeight - 10; |
| 201 |
| 202 if (this._wrapsContent) { |
| 203 width = Math.min(width, this.contentElement.offsetWidth); |
| 204 height = Math.min(height, this.contentElement.offsetHeight); |
| 205 } |
| 206 |
| 207 if (this._maxSize) { |
| 208 width = Math.min(width, this._maxSize.width); |
| 209 height = Math.min(height, this._maxSize.height); |
| 210 } |
| 211 |
| 212 var positionX; |
| 213 if (typeof this._defaultPositionX === 'number') { |
| 214 positionX = this._defaultPositionX; |
| 215 } else { |
| 216 positionX = (container.offsetWidth - width) / 2; |
| 217 positionX = Number.constrain(positionX, 0, container.offsetWidth - width); |
| 218 } |
| 219 |
| 220 var positionY; |
| 221 if (typeof this._defaultPositionY === 'number') { |
| 222 positionY = this._defaultPositionY; |
| 223 } else { |
| 224 positionY = (container.offsetHeight - height) / 2; |
| 225 positionY = Number.constrain(positionY, 0, container.offsetHeight - height
); |
| 226 } |
| 227 |
| 228 this.element.style.width = width + 'px'; |
| 229 this.element.style.height = height + 'px'; |
| 230 this.element.positionAt(positionX, positionY, container); |
| 231 } |
| 232 |
| 233 /** |
| 234 * @param {!Event} event |
| 235 */ |
| 236 _onKeyDown(event) { |
| 237 if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) { |
| 238 event.consume(true); |
| 239 this.detach(); |
| 240 } |
| 241 } |
50 }; | 242 }; |
51 | 243 |
52 /** | |
53 * @return {boolean} | |
54 */ | |
55 WebInspector.Dialog.hasInstance = function() | |
56 { | |
57 return !!WebInspector.Dialog._instance; | |
58 }; | |
59 | |
60 WebInspector.Dialog.prototype = { | |
61 /** | |
62 * @override | |
63 */ | |
64 show: function() | |
65 { | |
66 if (WebInspector.Dialog._instance) | |
67 WebInspector.Dialog._instance.detach(); | |
68 WebInspector.Dialog._instance = this; | |
69 | |
70 var document = /** @type {!Document} */ (WebInspector.Dialog._modalHostV
iew.element.ownerDocument); | |
71 this._disableTabIndexOnElements(document); | |
72 | |
73 this._glassPane = new WebInspector.GlassPane(document, this._dimmed); | |
74 this._glassPane.element.addEventListener("click", this._onGlassPaneClick
.bind(this), false); | |
75 this.element.ownerDocument.body.addEventListener("keydown", this._keyDow
nBound, false); | |
76 | |
77 WebInspector.Widget.prototype.show.call(this, this._glassPane.element); | |
78 | |
79 this._position(); | |
80 this._focusRestorer = new WebInspector.WidgetFocusRestorer(this); | |
81 }, | |
82 | |
83 /** | |
84 * @override | |
85 */ | |
86 detach: function() | |
87 { | |
88 this._focusRestorer.restore(); | |
89 | |
90 this.element.ownerDocument.body.removeEventListener("keydown", this._key
DownBound, false); | |
91 WebInspector.Widget.prototype.detach.call(this); | |
92 | |
93 this._glassPane.dispose(); | |
94 delete this._glassPane; | |
95 | |
96 this._restoreTabIndexOnElements(); | |
97 | |
98 delete WebInspector.Dialog._instance; | |
99 }, | |
100 | |
101 addCloseButton: function() | |
102 { | |
103 var closeButton = this.contentElement.createChild("div", "dialog-close-b
utton", "dt-close-button"); | |
104 closeButton.gray = true; | |
105 closeButton.addEventListener("click", this.detach.bind(this), false); | |
106 }, | |
107 | |
108 /** | |
109 * @param {number=} positionX | |
110 * @param {number=} positionY | |
111 */ | |
112 setPosition: function(positionX, positionY) | |
113 { | |
114 this._defaultPositionX = positionX; | |
115 this._defaultPositionY = positionY; | |
116 }, | |
117 | |
118 /** | |
119 * @param {!Size} size | |
120 */ | |
121 setMaxSize: function(size) | |
122 { | |
123 this._maxSize = size; | |
124 }, | |
125 | |
126 /** | |
127 * @param {boolean} wraps | |
128 */ | |
129 setWrapsContent: function(wraps) | |
130 { | |
131 this.element.classList.toggle("wraps-content", wraps); | |
132 this._wrapsContent = wraps; | |
133 }, | |
134 | |
135 /** | |
136 * @param {boolean} dimmed | |
137 */ | |
138 setDimmed: function(dimmed) | |
139 { | |
140 this._dimmed = dimmed; | |
141 }, | |
142 | |
143 contentResized: function() | |
144 { | |
145 if (this._wrapsContent) | |
146 this._position(); | |
147 }, | |
148 | |
149 /** | |
150 * @param {!Document} document | |
151 */ | |
152 _disableTabIndexOnElements: function(document) | |
153 { | |
154 this._tabIndexMap.clear(); | |
155 for (var node = document; node; node = node.traverseNextNode(document))
{ | |
156 if (node instanceof HTMLElement) { | |
157 var element = /** @type {!HTMLElement} */ (node); | |
158 var tabIndex = element.tabIndex; | |
159 if (tabIndex >= 0) { | |
160 this._tabIndexMap.set(element, tabIndex); | |
161 element.tabIndex = -1; | |
162 } | |
163 } | |
164 } | |
165 }, | |
166 | |
167 _restoreTabIndexOnElements: function() | |
168 { | |
169 for (var element of this._tabIndexMap.keys()) | |
170 element.tabIndex = /** @type {number} */(this._tabIndexMap.get(eleme
nt)); | |
171 this._tabIndexMap.clear(); | |
172 }, | |
173 | |
174 /** | |
175 * @param {!Event} event | |
176 */ | |
177 _onFocus: function(event) | |
178 { | |
179 this.focus(); | |
180 }, | |
181 | |
182 /** | |
183 * @param {!Event} event | |
184 */ | |
185 _onGlassPaneClick: function(event) | |
186 { | |
187 if (!this.element.isSelfOrAncestor(/** @type {?Node} */ (event.target))) | |
188 this.detach(); | |
189 }, | |
190 | |
191 _position: function() | |
192 { | |
193 var container = WebInspector.Dialog._modalHostView.element; | |
194 | |
195 var width = container.offsetWidth - 10; | |
196 var height = container.offsetHeight - 10; | |
197 | |
198 if (this._wrapsContent) { | |
199 width = Math.min(width, this.contentElement.offsetWidth); | |
200 height = Math.min(height, this.contentElement.offsetHeight); | |
201 } | |
202 | |
203 if (this._maxSize) { | |
204 width = Math.min(width, this._maxSize.width); | |
205 height = Math.min(height, this._maxSize.height); | |
206 } | |
207 | |
208 var positionX; | |
209 if (typeof this._defaultPositionX === "number") { | |
210 positionX = this._defaultPositionX; | |
211 } else { | |
212 positionX = (container.offsetWidth - width) / 2; | |
213 positionX = Number.constrain(positionX, 0, container.offsetWidth - w
idth); | |
214 } | |
215 | |
216 var positionY; | |
217 if (typeof this._defaultPositionY === "number") { | |
218 positionY = this._defaultPositionY; | |
219 } else { | |
220 positionY = (container.offsetHeight - height) / 2; | |
221 positionY = Number.constrain(positionY, 0, container.offsetHeight -
height); | |
222 } | |
223 | |
224 this.element.style.width = width + "px"; | |
225 this.element.style.height = height + "px"; | |
226 this.element.positionAt(positionX, positionY, container); | |
227 }, | |
228 | |
229 /** | |
230 * @param {!Event} event | |
231 */ | |
232 _onKeyDown: function(event) | |
233 { | |
234 if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) { | |
235 event.consume(true); | |
236 this.detach(); | |
237 } | |
238 }, | |
239 | |
240 __proto__: WebInspector.Widget.prototype | |
241 }; | |
242 | 244 |
243 /** @type {?Element} */ | 245 /** @type {?Element} */ |
244 WebInspector.Dialog._previousFocusedElement = null; | 246 WebInspector.Dialog._previousFocusedElement = null; |
245 | 247 |
246 /** @type {?WebInspector.Widget} */ | 248 /** @type {?WebInspector.Widget} */ |
247 WebInspector.Dialog._modalHostView = null; | 249 WebInspector.Dialog._modalHostView = null; |
248 | 250 |
249 /** | 251 |
250 * @param {!WebInspector.Widget} view | |
251 */ | |
252 WebInspector.Dialog.setModalHostView = function(view) | |
253 { | |
254 WebInspector.Dialog._modalHostView = view; | |
255 }; | |
256 | |
257 /** | |
258 * FIXME: make utility method in Dialog, so clients use it instead of this gette
r. | |
259 * Method should be like Dialog.showModalElement(position params, reposition cal
lback). | |
260 * @return {?WebInspector.Widget} | |
261 */ | |
262 WebInspector.Dialog.modalHostView = function() | |
263 { | |
264 return WebInspector.Dialog._modalHostView; | |
265 }; | |
266 | |
267 WebInspector.Dialog.modalHostRepositioned = function() | |
268 { | |
269 if (WebInspector.Dialog._instance) | |
270 WebInspector.Dialog._instance._position(); | |
271 }; | |
272 | |
OLD | NEW |