Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(118)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2007 Apple Inc. All rights reserved. 2 * Copyright (C) 2007 Apple 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 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. 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 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the 11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution. 12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived 14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission. 15 * from this software without specific prior written permission.
16 * 16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28
29 /** 28 /**
30 * @constructor 29 * @unrestricted
31 * @extends {WebInspector.Object}
32 * @param {boolean=} nonFocusable
33 */ 30 */
34 function TreeOutline(nonFocusable) 31 var TreeOutline = class extends WebInspector.Object {
35 { 32 /**
33 * @param {boolean=} nonFocusable
34 */
35 constructor(nonFocusable) {
36 super();
36 this._createRootElement(); 37 this._createRootElement();
37 38
38 this.selectedTreeElement = null; 39 this.selectedTreeElement = null;
39 this.expandTreeElementsWhenArrowing = false; 40 this.expandTreeElementsWhenArrowing = false;
40 /** @type {?function(!TreeElement, !TreeElement):number} */ 41 /** @type {?function(!TreeElement, !TreeElement):number} */
41 this._comparator = null; 42 this._comparator = null;
42 43
43 this.contentElement = this._rootElement._childrenListNode; 44 this.contentElement = this._rootElement._childrenListNode;
44 this.contentElement.addEventListener("keydown", this._treeKeyDown.bind(this) , true); 45 this.contentElement.addEventListener('keydown', this._treeKeyDown.bind(this) , true);
45 46
46 this.setFocusable(!nonFocusable); 47 this.setFocusable(!nonFocusable);
47 48
48 this.element = this.contentElement; 49 this.element = this.contentElement;
49 } 50 }
51
52 _createRootElement() {
53 this._rootElement = new TreeElement();
54 this._rootElement.treeOutline = this;
55 this._rootElement.root = true;
56 this._rootElement.selectable = false;
57 this._rootElement.expanded = true;
58 this._rootElement._childrenListNode.classList.remove('children');
59 }
60
61 /**
62 * @return {!TreeElement}
63 */
64 rootElement() {
65 return this._rootElement;
66 }
67
68 /**
69 * @return {?TreeElement}
70 */
71 firstChild() {
72 return this._rootElement.firstChild();
73 }
74
75 /**
76 * @param {!TreeElement} child
77 */
78 appendChild(child) {
79 this._rootElement.appendChild(child);
80 }
81
82 /**
83 * @param {!TreeElement} child
84 * @param {number} index
85 */
86 insertChild(child, index) {
87 this._rootElement.insertChild(child, index);
88 }
89
90 /**
91 * @param {!TreeElement} child
92 */
93 removeChild(child) {
94 this._rootElement.removeChild(child);
95 }
96
97 removeChildren() {
98 this._rootElement.removeChildren();
99 }
100
101 /**
102 * @param {number} x
103 * @param {number} y
104 * @return {?TreeElement}
105 */
106 treeElementFromPoint(x, y) {
107 var node = this.contentElement.ownerDocument.deepElementFromPoint(x, y);
108 if (!node)
109 return null;
110
111 var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(['ol', 'li']);
112 if (listNode)
113 return listNode.parentTreeElement || listNode.treeElement;
114 return null;
115 }
116
117 /**
118 * @param {?Event} event
119 * @return {?TreeElement}
120 */
121 treeElementFromEvent(event) {
122 return event ? this.treeElementFromPoint(event.pageX, event.pageY) : null;
123 }
124
125 /**
126 * @param {?function(!TreeElement, !TreeElement):number} comparator
127 */
128 setComparator(comparator) {
129 this._comparator = comparator;
130 }
131
132 /**
133 * @param {boolean} focusable
134 */
135 setFocusable(focusable) {
136 if (focusable)
137 this.contentElement.setAttribute('tabIndex', 0);
138 else
139 this.contentElement.removeAttribute('tabIndex');
140 }
141
142 focus() {
143 this.contentElement.focus();
144 }
145
146 /**
147 * @param {!TreeElement} element
148 */
149 _bindTreeElement(element) {
150 if (element.treeOutline)
151 console.error('Binding element for the second time: ' + new Error().stack) ;
152 element.treeOutline = this;
153 element.onbind();
154 }
155
156 /**
157 * @param {!TreeElement} element
158 */
159 _unbindTreeElement(element) {
160 if (!element.treeOutline)
161 console.error('Unbinding element that was not bound: ' + new Error().stack );
162
163 element.deselect();
164 element.onunbind();
165 element.treeOutline = null;
166 }
167
168 /**
169 * @return {boolean}
170 */
171 selectPrevious() {
172 var nextSelectedElement = this.selectedTreeElement.traversePreviousTreeEleme nt(true);
173 while (nextSelectedElement && !nextSelectedElement.selectable)
174 nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!thi s.expandTreeElementsWhenArrowing);
175 if (nextSelectedElement) {
176 nextSelectedElement.reveal();
177 nextSelectedElement.select(false, true);
178 return true;
179 }
180 return false;
181 }
182
183 /**
184 * @return {boolean}
185 */
186 selectNext() {
187 var nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(t rue);
188 while (nextSelectedElement && !nextSelectedElement.selectable)
189 nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.ex pandTreeElementsWhenArrowing);
190 if (nextSelectedElement) {
191 nextSelectedElement.reveal();
192 nextSelectedElement.select(false, true);
193 return true;
194 }
195 return false;
196 }
197
198 /**
199 * @param {!Event} event
200 */
201 _treeKeyDown(event) {
202 if (event.target !== this.contentElement)
203 return;
204
205 if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ct rlKey)
206 return;
207
208 var handled = false;
209 var nextSelectedElement;
210 if (event.key === 'ArrowUp' && !event.altKey) {
211 handled = this.selectPrevious();
212 } else if (event.key === 'ArrowDown' && !event.altKey) {
213 handled = this.selectNext();
214 } else if (event.key === 'ArrowLeft') {
215 if (this.selectedTreeElement.expanded) {
216 if (event.altKey)
217 this.selectedTreeElement.collapseRecursively();
218 else
219 this.selectedTreeElement.collapse();
220 handled = true;
221 } else if (this.selectedTreeElement.parent && !this.selectedTreeElement.pa rent.root) {
222 handled = true;
223 if (this.selectedTreeElement.parent.selectable) {
224 nextSelectedElement = this.selectedTreeElement.parent;
225 while (nextSelectedElement && !nextSelectedElement.selectable)
226 nextSelectedElement = nextSelectedElement.parent;
227 handled = nextSelectedElement ? true : false;
228 } else if (this.selectedTreeElement.parent)
229 this.selectedTreeElement.parent.collapse();
230 }
231 } else if (event.key === 'ArrowRight') {
232 if (!this.selectedTreeElement.revealed()) {
233 this.selectedTreeElement.reveal();
234 handled = true;
235 } else if (this.selectedTreeElement._expandable) {
236 handled = true;
237 if (this.selectedTreeElement.expanded) {
238 nextSelectedElement = this.selectedTreeElement.firstChild();
239 while (nextSelectedElement && !nextSelectedElement.selectable)
240 nextSelectedElement = nextSelectedElement.nextSibling;
241 handled = nextSelectedElement ? true : false;
242 } else {
243 if (event.altKey)
244 this.selectedTreeElement.expandRecursively();
245 else
246 this.selectedTreeElement.expand();
247 }
248 }
249 } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /* De lete */)
250 handled = this.selectedTreeElement.ondelete();
251 else if (isEnterKey(event))
252 handled = this.selectedTreeElement.onenter();
253 else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code)
254 handled = this.selectedTreeElement.onspace();
255
256 if (nextSelectedElement) {
257 nextSelectedElement.reveal();
258 nextSelectedElement.select(false, true);
259 }
260
261 if (handled)
262 event.consume(true);
263 }
264
265 /**
266 * @param {!TreeElement} treeElement
267 * @param {boolean} center
268 */
269 _deferredScrollIntoView(treeElement, center) {
270 if (!this._treeElementToScrollIntoView)
271 this.element.window().requestAnimationFrame(deferredScrollIntoView.bind(th is));
272 this._treeElementToScrollIntoView = treeElement;
273 this._centerUponScrollIntoView = center;
274 /**
275 * @this {TreeOutline}
276 */
277 function deferredScrollIntoView() {
278 this._treeElementToScrollIntoView.listItemElement.scrollIntoViewIfNeeded(t his._centerUponScrollIntoView);
279 delete this._treeElementToScrollIntoView;
280 delete this._centerUponScrollIntoView;
281 }
282 }
283 };
50 284
51 /** @enum {symbol} */ 285 /** @enum {symbol} */
52 TreeOutline.Events = { 286 TreeOutline.Events = {
53 ElementAttached: Symbol("ElementAttached"), 287 ElementAttached: Symbol('ElementAttached'),
54 ElementExpanded: Symbol("ElementExpanded"), 288 ElementExpanded: Symbol('ElementExpanded'),
55 ElementCollapsed: Symbol("ElementCollapsed"), 289 ElementCollapsed: Symbol('ElementCollapsed'),
56 ElementSelected: Symbol("ElementSelected") 290 ElementSelected: Symbol('ElementSelected')
57 }; 291 };
58 292
59 TreeOutline.prototype = {
60 _createRootElement: function()
61 {
62 this._rootElement = new TreeElement();
63 this._rootElement.treeOutline = this;
64 this._rootElement.root = true;
65 this._rootElement.selectable = false;
66 this._rootElement.expanded = true;
67 this._rootElement._childrenListNode.classList.remove("children");
68 },
69
70 /**
71 * @return {!TreeElement}
72 */
73 rootElement: function()
74 {
75 return this._rootElement;
76 },
77
78 /**
79 * @return {?TreeElement}
80 */
81 firstChild: function()
82 {
83 return this._rootElement.firstChild();
84 },
85
86 /**
87 * @param {!TreeElement} child
88 */
89 appendChild: function(child)
90 {
91 this._rootElement.appendChild(child);
92 },
93
94 /**
95 * @param {!TreeElement} child
96 * @param {number} index
97 */
98 insertChild: function(child, index)
99 {
100 this._rootElement.insertChild(child, index);
101 },
102
103 /**
104 * @param {!TreeElement} child
105 */
106 removeChild: function(child)
107 {
108 this._rootElement.removeChild(child);
109 },
110
111 removeChildren: function()
112 {
113 this._rootElement.removeChildren();
114 },
115
116 /**
117 * @param {number} x
118 * @param {number} y
119 * @return {?TreeElement}
120 */
121 treeElementFromPoint: function(x, y)
122 {
123 var node = this.contentElement.ownerDocument.deepElementFromPoint(x, y);
124 if (!node)
125 return null;
126
127 var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(["ol", "li"]) ;
128 if (listNode)
129 return listNode.parentTreeElement || listNode.treeElement;
130 return null;
131 },
132
133 /**
134 * @param {?Event} event
135 * @return {?TreeElement}
136 */
137 treeElementFromEvent: function(event)
138 {
139 return event ? this.treeElementFromPoint(event.pageX, event.pageY) : nul l;
140 },
141
142 /**
143 * @param {?function(!TreeElement, !TreeElement):number} comparator
144 */
145 setComparator: function(comparator)
146 {
147 this._comparator = comparator;
148 },
149
150 /**
151 * @param {boolean} focusable
152 */
153 setFocusable: function(focusable)
154 {
155 if (focusable)
156 this.contentElement.setAttribute("tabIndex", 0);
157 else
158 this.contentElement.removeAttribute("tabIndex");
159 },
160
161 focus: function()
162 {
163 this.contentElement.focus();
164 },
165
166 /**
167 * @param {!TreeElement} element
168 */
169 _bindTreeElement: function(element)
170 {
171 if (element.treeOutline)
172 console.error("Binding element for the second time: " + new Error(). stack);
173 element.treeOutline = this;
174 element.onbind();
175 },
176
177 /**
178 * @param {!TreeElement} element
179 */
180 _unbindTreeElement: function(element)
181 {
182 if (!element.treeOutline)
183 console.error("Unbinding element that was not bound: " + new Error() .stack);
184
185 element.deselect();
186 element.onunbind();
187 element.treeOutline = null;
188 },
189
190 /**
191 * @return {boolean}
192 */
193 selectPrevious: function()
194 {
195 var nextSelectedElement = this.selectedTreeElement.traversePreviousTreeE lement(true);
196 while (nextSelectedElement && !nextSelectedElement.selectable)
197 nextSelectedElement = nextSelectedElement.traversePreviousTreeElemen t(!this.expandTreeElementsWhenArrowing);
198 if (nextSelectedElement) {
199 nextSelectedElement.reveal();
200 nextSelectedElement.select(false, true);
201 return true;
202 }
203 return false;
204 },
205
206 /**
207 * @return {boolean}
208 */
209 selectNext: function()
210 {
211 var nextSelectedElement = this.selectedTreeElement.traverseNextTreeEleme nt(true);
212 while (nextSelectedElement && !nextSelectedElement.selectable)
213 nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!t his.expandTreeElementsWhenArrowing);
214 if (nextSelectedElement) {
215 nextSelectedElement.reveal();
216 nextSelectedElement.select(false, true);
217 return true;
218 }
219 return false;
220 },
221
222 /**
223 * @param {!Event} event
224 */
225 _treeKeyDown: function(event)
226 {
227 if (event.target !== this.contentElement)
228 return;
229
230 if (!this.selectedTreeElement || event.shiftKey || event.metaKey || even t.ctrlKey)
231 return;
232
233 var handled = false;
234 var nextSelectedElement;
235 if (event.key === "ArrowUp" && !event.altKey) {
236 handled = this.selectPrevious();
237 } else if (event.key === "ArrowDown" && !event.altKey) {
238 handled = this.selectNext();
239 } else if (event.key === "ArrowLeft") {
240 if (this.selectedTreeElement.expanded) {
241 if (event.altKey)
242 this.selectedTreeElement.collapseRecursively();
243 else
244 this.selectedTreeElement.collapse();
245 handled = true;
246 } else if (this.selectedTreeElement.parent && !this.selectedTreeElem ent.parent.root) {
247 handled = true;
248 if (this.selectedTreeElement.parent.selectable) {
249 nextSelectedElement = this.selectedTreeElement.parent;
250 while (nextSelectedElement && !nextSelectedElement.selectabl e)
251 nextSelectedElement = nextSelectedElement.parent;
252 handled = nextSelectedElement ? true : false;
253 } else if (this.selectedTreeElement.parent)
254 this.selectedTreeElement.parent.collapse();
255 }
256 } else if (event.key === "ArrowRight") {
257 if (!this.selectedTreeElement.revealed()) {
258 this.selectedTreeElement.reveal();
259 handled = true;
260 } else if (this.selectedTreeElement._expandable) {
261 handled = true;
262 if (this.selectedTreeElement.expanded) {
263 nextSelectedElement = this.selectedTreeElement.firstChild();
264 while (nextSelectedElement && !nextSelectedElement.selectabl e)
265 nextSelectedElement = nextSelectedElement.nextSibling;
266 handled = nextSelectedElement ? true : false;
267 } else {
268 if (event.altKey)
269 this.selectedTreeElement.expandRecursively();
270 else
271 this.selectedTreeElement.expand();
272 }
273 }
274 } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 / * Delete */)
275 handled = this.selectedTreeElement.ondelete();
276 else if (isEnterKey(event))
277 handled = this.selectedTreeElement.onenter();
278 else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code )
279 handled = this.selectedTreeElement.onspace();
280
281 if (nextSelectedElement) {
282 nextSelectedElement.reveal();
283 nextSelectedElement.select(false, true);
284 }
285
286 if (handled)
287 event.consume(true);
288 },
289
290 /**
291 * @param {!TreeElement} treeElement
292 * @param {boolean} center
293 */
294 _deferredScrollIntoView: function(treeElement, center)
295 {
296 if (!this._treeElementToScrollIntoView)
297 this.element.window().requestAnimationFrame(deferredScrollIntoView.b ind(this));
298 this._treeElementToScrollIntoView = treeElement;
299 this._centerUponScrollIntoView = center;
300 /**
301 * @this {TreeOutline}
302 */
303 function deferredScrollIntoView()
304 {
305 this._treeElementToScrollIntoView.listItemElement.scrollIntoViewIfNe eded(this._centerUponScrollIntoView);
306 delete this._treeElementToScrollIntoView;
307 delete this._centerUponScrollIntoView;
308 }
309 },
310
311 __proto__: WebInspector.Object.prototype
312 };
313
314 /** 293 /**
315 * @constructor 294 * @unrestricted
316 * @extends {TreeOutline}
317 */ 295 */
318 function TreeOutlineInShadow() 296 var TreeOutlineInShadow = class extends TreeOutline {
319 { 297 constructor() {
320 TreeOutline.call(this); 298 super();
321 this.contentElement.classList.add("tree-outline"); 299 this.contentElement.classList.add('tree-outline');
322 300
323 // Redefine element to the external one. 301 // Redefine element to the external one.
324 this.element = createElement("div"); 302 this.element = createElement('div');
325 this._shadowRoot = WebInspector.createShadowRootWithCoreStyles(this.element, "ui/treeoutline.css"); 303 this._shadowRoot = WebInspector.createShadowRootWithCoreStyles(this.element, 'ui/treeoutline.css');
326 this._disclosureElement = this._shadowRoot.createChild("div", "tree-outline- disclosure"); 304 this._disclosureElement = this._shadowRoot.createChild('div', 'tree-outline- disclosure');
327 this._disclosureElement.appendChild(this.contentElement); 305 this._disclosureElement.appendChild(this.contentElement);
328 this._renderSelection = true; 306 this._renderSelection = true;
329 } 307 }
330 308
331 TreeOutlineInShadow.prototype = { 309 /**
332 /** 310 * @param {string} cssFile
333 * @param {string} cssFile 311 */
334 */ 312 registerRequiredCSS(cssFile) {
335 registerRequiredCSS: function(cssFile) 313 WebInspector.appendStyle(this._shadowRoot, cssFile);
336 { 314 }
337 WebInspector.appendStyle(this._shadowRoot, cssFile); 315
338 }, 316 hideOverflow() {
339 317 this._disclosureElement.classList.add('tree-outline-disclosure-hide-overflow ');
340 hideOverflow: function() 318 }
341 { 319
342 this._disclosureElement.classList.add("tree-outline-disclosure-hide-over flow"); 320 makeDense() {
343 }, 321 this.contentElement.classList.add('tree-outline-dense');
344 322 }
345 makeDense: function()
346 {
347 this.contentElement.classList.add("tree-outline-dense");
348 },
349
350 __proto__: TreeOutline.prototype
351 }; 323 };
352 324
353 /** 325 /**
354 * @constructor 326 * @unrestricted
355 * @param {(string|!Node)=} title
356 * @param {boolean=} expandable
357 */ 327 */
358 function TreeElement(title, expandable) 328 var TreeElement = class {
359 { 329 /**
330 * @param {(string|!Node)=} title
331 * @param {boolean=} expandable
332 */
333 constructor(title, expandable) {
360 /** @type {?TreeOutline} */ 334 /** @type {?TreeOutline} */
361 this.treeOutline = null; 335 this.treeOutline = null;
362 this.parent = null; 336 this.parent = null;
363 this.previousSibling = null; 337 this.previousSibling = null;
364 this.nextSibling = null; 338 this.nextSibling = null;
365 339
366 this._listItemNode = createElement("li"); 340 this._listItemNode = createElement('li');
367 this._listItemNode.treeElement = this; 341 this._listItemNode.treeElement = this;
368 if (title) 342 if (title)
369 this.title = title; 343 this.title = title;
370 this._listItemNode.addEventListener("mousedown", this._handleMouseDown.bind( this), false); 344 this._listItemNode.addEventListener('mousedown', this._handleMouseDown.bind( this), false);
371 this._listItemNode.addEventListener("click", this._treeElementToggled.bind(t his), false); 345 this._listItemNode.addEventListener('click', this._treeElementToggled.bind(t his), false);
372 this._listItemNode.addEventListener("dblclick", this._handleDoubleClick.bind (this), false); 346 this._listItemNode.addEventListener('dblclick', this._handleDoubleClick.bind (this), false);
373 347
374 this._childrenListNode = createElement("ol"); 348 this._childrenListNode = createElement('ol');
375 this._childrenListNode.parentTreeElement = this; 349 this._childrenListNode.parentTreeElement = this;
376 this._childrenListNode.classList.add("children"); 350 this._childrenListNode.classList.add('children');
377 351
378 this._hidden = false; 352 this._hidden = false;
379 this._selectable = true; 353 this._selectable = true;
380 this.expanded = false; 354 this.expanded = false;
381 this.selected = false; 355 this.selected = false;
382 this.setExpandable(expandable || false); 356 this.setExpandable(expandable || false);
383 this._collapsible = true; 357 this._collapsible = true;
384 } 358 }
359
360 /**
361 * @param {?TreeElement} ancestor
362 * @return {boolean}
363 */
364 hasAncestor(ancestor) {
365 if (!ancestor)
366 return false;
367
368 var currentNode = this.parent;
369 while (currentNode) {
370 if (ancestor === currentNode)
371 return true;
372 currentNode = currentNode.parent;
373 }
374
375 return false;
376 }
377
378 /**
379 * @param {?TreeElement} ancestor
380 * @return {boolean}
381 */
382 hasAncestorOrSelf(ancestor) {
383 return this === ancestor || this.hasAncestor(ancestor);
384 }
385
386 /**
387 * @return {!Array.<!TreeElement>}
388 */
389 children() {
390 return this._children || [];
391 }
392
393 /**
394 * @return {number}
395 */
396 childCount() {
397 return this._children ? this._children.length : 0;
398 }
399
400 /**
401 * @return {?TreeElement}
402 */
403 firstChild() {
404 return this._children ? this._children[0] : null;
405 }
406
407 /**
408 * @return {?TreeElement}
409 */
410 lastChild() {
411 return this._children ? this._children[this._children.length - 1] : null;
412 }
413
414 /**
415 * @param {number} index
416 * @return {?TreeElement}
417 */
418 childAt(index) {
419 return this._children ? this._children[index] : null;
420 }
421
422 /**
423 * @param {!TreeElement} child
424 * @return {number}
425 */
426 indexOfChild(child) {
427 return this._children ? this._children.indexOf(child) : -1;
428 }
429
430 /**
431 * @param {!TreeElement} child
432 */
433 appendChild(child) {
434 if (!this._children)
435 this._children = [];
436
437 var insertionIndex;
438 if (this.treeOutline && this.treeOutline._comparator)
439 insertionIndex = this._children.lowerBound(child, this.treeOutline._compar ator);
440 else
441 insertionIndex = this._children.length;
442 this.insertChild(child, insertionIndex);
443 }
444
445 /**
446 * @param {!TreeElement} child
447 * @param {number} index
448 */
449 insertChild(child, index) {
450 if (!this._children)
451 this._children = [];
452
453 if (!child)
454 throw 'child can\'t be undefined or null';
455
456 console.assert(
457 !child.parent, 'Attempting to insert a child that is already in the tree , reparenting is not supported.');
458
459 var previousChild = (index > 0 ? this._children[index - 1] : null);
460 if (previousChild) {
461 previousChild.nextSibling = child;
462 child.previousSibling = previousChild;
463 } else {
464 child.previousSibling = null;
465 }
466
467 var nextChild = this._children[index];
468 if (nextChild) {
469 nextChild.previousSibling = child;
470 child.nextSibling = nextChild;
471 } else {
472 child.nextSibling = null;
473 }
474
475 this._children.splice(index, 0, child);
476
477 this.setExpandable(true);
478 child.parent = this;
479
480 if (this.treeOutline)
481 this.treeOutline._bindTreeElement(child);
482 for (var current = child.firstChild(); this.treeOutline && current;
483 current = current.traverseNextTreeElement(false, child, true))
484 this.treeOutline._bindTreeElement(current);
485 child.onattach();
486 child._ensureSelection();
487 if (this.treeOutline)
488 this.treeOutline.dispatchEventToListeners(TreeOutline.Events.ElementAttach ed, child);
489 var nextSibling = child.nextSibling ? child.nextSibling._listItemNode : null ;
490 this._childrenListNode.insertBefore(child._listItemNode, nextSibling);
491 this._childrenListNode.insertBefore(child._childrenListNode, nextSibling);
492 if (child.selected)
493 child.select();
494 if (child.expanded)
495 child.expand();
496 }
497
498 /**
499 * @param {number} childIndex
500 */
501 removeChildAtIndex(childIndex) {
502 if (childIndex < 0 || childIndex >= this._children.length)
503 throw 'childIndex out of range';
504
505 var child = this._children[childIndex];
506 this._children.splice(childIndex, 1);
507
508 var parent = child.parent;
509 if (this.treeOutline && this.treeOutline.selectedTreeElement &&
510 this.treeOutline.selectedTreeElement.hasAncestorOrSelf(child)) {
511 if (child.nextSibling)
512 child.nextSibling.select(true);
513 else if (child.previousSibling)
514 child.previousSibling.select(true);
515 else if (parent)
516 parent.select(true);
517 }
518
519 if (child.previousSibling)
520 child.previousSibling.nextSibling = child.nextSibling;
521 if (child.nextSibling)
522 child.nextSibling.previousSibling = child.previousSibling;
523 child.parent = null;
524
525 if (this.treeOutline)
526 this.treeOutline._unbindTreeElement(child);
527 for (var current = child.firstChild(); this.treeOutline && current;
528 current = current.traverseNextTreeElement(false, child, true))
529 this.treeOutline._unbindTreeElement(current);
530
531 child._detach();
532 }
533
534 /**
535 * @param {!TreeElement} child
536 */
537 removeChild(child) {
538 if (!child)
539 throw 'child can\'t be undefined or null';
540 if (child.parent !== this)
541 return;
542
543 var childIndex = this._children.indexOf(child);
544 if (childIndex === -1)
545 throw 'child not found in this node\'s children';
546
547 this.removeChildAtIndex(childIndex);
548 }
549
550 removeChildren() {
551 if (!this.root && this.treeOutline && this.treeOutline.selectedTreeElement & &
552 this.treeOutline.selectedTreeElement.hasAncestorOrSelf(this))
553 this.select(true);
554
555 for (var i = 0; this._children && i < this._children.length; ++i) {
556 var child = this._children[i];
557 child.previousSibling = null;
558 child.nextSibling = null;
559 child.parent = null;
560
561 if (this.treeOutline)
562 this.treeOutline._unbindTreeElement(child);
563 for (var current = child.firstChild(); this.treeOutline && current;
564 current = current.traverseNextTreeElement(false, child, true))
565 this.treeOutline._unbindTreeElement(current);
566 child._detach();
567 }
568 this._children = [];
569 }
570
571 get selectable() {
572 if (this._hidden)
573 return false;
574 return this._selectable;
575 }
576
577 set selectable(x) {
578 this._selectable = x;
579 }
580
581 get listItemElement() {
582 return this._listItemNode;
583 }
584
585 get childrenListElement() {
586 return this._childrenListNode;
587 }
588
589 /**
590 * @return {string|!Node}
591 */
592 get title() {
593 return this._title;
594 }
595
596 /**
597 * @param {string|!Node} x
598 */
599 set title(x) {
600 if (this._title === x)
601 return;
602 this._title = x;
603
604 if (typeof x === 'string') {
605 this._titleElement = createElementWithClass('span', 'tree-element-title');
606 this._titleElement.textContent = x;
607 this.tooltip = x;
608 } else {
609 this._titleElement = x;
610 this.tooltip = '';
611 }
612
613 this._listItemNode.removeChildren();
614 if (this._iconElement)
615 this._listItemNode.appendChild(this._iconElement);
616
617 this._listItemNode.appendChild(this._titleElement);
618 this._ensureSelection();
619 }
620
621 /**
622 * @return {string}
623 */
624 titleAsText() {
625 if (!this._title)
626 return '';
627 if (typeof this._title === 'string')
628 return this._title;
629 return this._title.textContent;
630 }
631
632 /**
633 * @param {!WebInspector.InplaceEditor.Config} editingConfig
634 */
635 startEditingTitle(editingConfig) {
636 WebInspector.InplaceEditor.startEditing(this._titleElement, editingConfig);
637 this.treeOutline._shadowRoot.getSelection().setBaseAndExtent(this._titleElem ent, 0, this._titleElement, 1);
638 }
639
640 createIcon() {
641 if (!this._iconElement) {
642 this._iconElement = createElementWithClass('div', 'icon');
643 this._listItemNode.insertBefore(this._iconElement, this._listItemNode.firs tChild);
644 this._ensureSelection();
645 }
646 }
647
648 /**
649 * @return {string}
650 */
651 get tooltip() {
652 return this._tooltip || '';
653 }
654
655 /**
656 * @param {string} x
657 */
658 set tooltip(x) {
659 if (this._tooltip === x)
660 return;
661 this._tooltip = x;
662 this._listItemNode.title = x;
663 }
664
665 /**
666 * @return {boolean}
667 */
668 isExpandable() {
669 return this._expandable;
670 }
671
672 /**
673 * @param {boolean} expandable
674 */
675 setExpandable(expandable) {
676 if (this._expandable === expandable)
677 return;
678
679 this._expandable = expandable;
680
681 this._listItemNode.classList.toggle('parent', expandable);
682 if (!expandable)
683 this.collapse();
684 }
685
686 /**
687 * @param {boolean} collapsible
688 */
689 setCollapsible(collapsible) {
690 if (this._collapsible === collapsible)
691 return;
692
693 this._collapsible = collapsible;
694
695 this._listItemNode.classList.toggle('always-parent', !collapsible);
696 if (!collapsible)
697 this.expand();
698 }
699
700 get hidden() {
701 return this._hidden;
702 }
703
704 set hidden(x) {
705 if (this._hidden === x)
706 return;
707
708 this._hidden = x;
709
710 this._listItemNode.classList.toggle('hidden', x);
711 this._childrenListNode.classList.toggle('hidden', x);
712 }
713
714 invalidateChildren() {
715 if (this._children) {
716 this.removeChildren();
717 this._children = null;
718 }
719 }
720
721 _ensureSelection() {
722 if (!this.treeOutline || !this.treeOutline._renderSelection)
723 return;
724 if (!this._selectionElement)
725 this._selectionElement = createElementWithClass('div', 'selection fill');
726 this._listItemNode.insertBefore(this._selectionElement, this.listItemElement .firstChild);
727 }
728
729 /**
730 * @param {!Event} event
731 */
732 _treeElementToggled(event) {
733 var element = event.currentTarget;
734 if (element.treeElement !== this || element.hasSelection())
735 return;
736
737 var toggleOnClick = this.toggleOnClick && !this.selectable;
738 var isInTriangle = this.isEventWithinDisclosureTriangle(event);
739 if (!toggleOnClick && !isInTriangle)
740 return;
741
742 if (event.target && event.target.enclosingNodeOrSelfWithNodeName('a'))
743 return;
744
745 if (this.expanded) {
746 if (event.altKey)
747 this.collapseRecursively();
748 else
749 this.collapse();
750 } else {
751 if (event.altKey)
752 this.expandRecursively();
753 else
754 this.expand();
755 }
756 event.consume();
757 }
758
759 /**
760 * @param {!Event} event
761 */
762 _handleMouseDown(event) {
763 var element = event.currentTarget;
764 if (!element)
765 return;
766 if (!this.selectable)
767 return;
768 if (element.treeElement !== this)
769 return;
770
771 if (this.isEventWithinDisclosureTriangle(event))
772 return;
773
774 this.selectOnMouseDown(event);
775 }
776
777 /**
778 * @param {!Event} event
779 */
780 _handleDoubleClick(event) {
781 var element = event.currentTarget;
782 if (!element || element.treeElement !== this)
783 return;
784
785 var handled = this.ondblclick(event);
786 if (handled)
787 return;
788 if (this._expandable && !this.expanded)
789 this.expand();
790 }
791
792 _detach() {
793 this._listItemNode.remove();
794 this._childrenListNode.remove();
795 }
796
797 collapse() {
798 if (!this.expanded || !this._collapsible)
799 return;
800 this._listItemNode.classList.remove('expanded');
801 this._childrenListNode.classList.remove('expanded');
802 this.expanded = false;
803 this.oncollapse();
804 if (this.treeOutline)
805 this.treeOutline.dispatchEventToListeners(TreeOutline.Events.ElementCollap sed, this);
806 }
807
808 collapseRecursively() {
809 var item = this;
810 while (item) {
811 if (item.expanded)
812 item.collapse();
813 item = item.traverseNextTreeElement(false, this, true);
814 }
815 }
816
817 expand() {
818 if (!this._expandable || (this.expanded && this._children))
819 return;
820
821 // Set this before onpopulate. Since onpopulate can add elements, this makes
822 // sure the expanded flag is true before calling those functions. This preve nts the possibility
823 // of an infinite loop if onpopulate were to call expand.
824
825 this.expanded = true;
826
827 this._populateIfNeeded();
828 this._listItemNode.classList.add('expanded');
829 this._childrenListNode.classList.add('expanded');
830
831 if (this.treeOutline) {
832 this.onexpand();
833 this.treeOutline.dispatchEventToListeners(TreeOutline.Events.ElementExpand ed, this);
834 }
835 }
836
837 /**
838 * @param {number=} maxDepth
839 */
840 expandRecursively(maxDepth) {
841 var item = this;
842 var info = {};
843 var depth = 0;
844
845 // The Inspector uses TreeOutlines to represents object properties, so recur sive expansion
846 // in some case can be infinite, since JavaScript objects can hold circular references.
847 // So default to a recursion cap of 3 levels, since that gives fairly good r esults.
848 if (isNaN(maxDepth))
849 maxDepth = 3;
850
851 while (item) {
852 if (depth < maxDepth)
853 item.expand();
854 item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info );
855 depth += info.depthChange;
856 }
857 }
858
859 /**
860 * @param {boolean=} center
861 */
862 reveal(center) {
863 var currentAncestor = this.parent;
864 while (currentAncestor && !currentAncestor.root) {
865 if (!currentAncestor.expanded)
866 currentAncestor.expand();
867 currentAncestor = currentAncestor.parent;
868 }
869
870 this.treeOutline._deferredScrollIntoView(this, !!center);
871 }
872
873 /**
874 * @return {boolean}
875 */
876 revealed() {
877 var currentAncestor = this.parent;
878 while (currentAncestor && !currentAncestor.root) {
879 if (!currentAncestor.expanded)
880 return false;
881 currentAncestor = currentAncestor.parent;
882 }
883
884 return true;
885 }
886
887 selectOnMouseDown(event) {
888 if (this.select(false, true))
889 event.consume(true);
890 }
891
892 /**
893 * @param {boolean=} omitFocus
894 * @param {boolean=} selectedByUser
895 * @return {boolean}
896 */
897 select(omitFocus, selectedByUser) {
898 if (!this.treeOutline || !this.selectable || this.selected)
899 return false;
900
901 if (this.treeOutline.selectedTreeElement)
902 this.treeOutline.selectedTreeElement.deselect();
903 this.treeOutline.selectedTreeElement = null;
904
905 if (this.treeOutline._rootElement === this)
906 return false;
907
908 this.selected = true;
909
910 if (!omitFocus)
911 this.treeOutline.focus();
912
913 // Focusing on another node may detach "this" from tree.
914 if (!this.treeOutline)
915 return false;
916 this.treeOutline.selectedTreeElement = this;
917 this._listItemNode.classList.add('selected');
918 this.treeOutline.dispatchEventToListeners(TreeOutline.Events.ElementSelected , this);
919 return this.onselect(selectedByUser);
920 }
921
922 /**
923 * @param {boolean=} omitFocus
924 */
925 revealAndSelect(omitFocus) {
926 this.reveal(true);
927 this.select(omitFocus);
928 }
929
930 /**
931 * @param {boolean=} supressOnDeselect
932 */
933 deselect(supressOnDeselect) {
934 if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !t his.selected)
935 return;
936
937 this.selected = false;
938 this.treeOutline.selectedTreeElement = null;
939 this._listItemNode.classList.remove('selected');
940 }
941
942 _populateIfNeeded() {
943 if (this.treeOutline && this._expandable && !this._children) {
944 this._children = [];
945 this.onpopulate();
946 }
947 }
948
949 onpopulate() {
950 // Overridden by subclasses.
951 }
952
953 /**
954 * @return {boolean}
955 */
956 onenter() {
957 return false;
958 }
959
960 /**
961 * @return {boolean}
962 */
963 ondelete() {
964 return false;
965 }
966
967 /**
968 * @return {boolean}
969 */
970 onspace() {
971 return false;
972 }
973
974 onbind() {
975 }
976
977 onunbind() {
978 }
979
980 onattach() {
981 }
982
983 onexpand() {
984 }
985
986 oncollapse() {
987 }
988
989 /**
990 * @param {!Event} e
991 * @return {boolean}
992 */
993 ondblclick(e) {
994 return false;
995 }
996
997 /**
998 * @param {boolean=} selectedByUser
999 * @return {boolean}
1000 */
1001 onselect(selectedByUser) {
1002 return false;
1003 }
1004
1005 /**
1006 * @param {boolean} skipUnrevealed
1007 * @param {?TreeElement=} stayWithin
1008 * @param {boolean=} dontPopulate
1009 * @param {!Object=} info
1010 * @return {?TreeElement}
1011 */
1012 traverseNextTreeElement(skipUnrevealed, stayWithin, dontPopulate, info) {
1013 if (!dontPopulate)
1014 this._populateIfNeeded();
1015
1016 if (info)
1017 info.depthChange = 0;
1018
1019 var element = skipUnrevealed ? (this.revealed() ? this.firstChild() : null) : this.firstChild();
1020 if (element && (!skipUnrevealed || (skipUnrevealed && this.expanded))) {
1021 if (info)
1022 info.depthChange = 1;
1023 return element;
1024 }
1025
1026 if (this === stayWithin)
1027 return null;
1028
1029 element = skipUnrevealed ? (this.revealed() ? this.nextSibling : null) : thi s.nextSibling;
1030 if (element)
1031 return element;
1032
1033 element = this;
1034 while (element && !element.root &&
1035 !(skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) &&
1036 element.parent !== stayWithin) {
1037 if (info)
1038 info.depthChange -= 1;
1039 element = element.parent;
1040 }
1041
1042 if (!element || element.root)
1043 return null;
1044
1045 return (skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
1046 }
1047
1048 /**
1049 * @param {boolean} skipUnrevealed
1050 * @param {boolean=} dontPopulate
1051 * @return {?TreeElement}
1052 */
1053 traversePreviousTreeElement(skipUnrevealed, dontPopulate) {
1054 var element = skipUnrevealed ? (this.revealed() ? this.previousSibling : nul l) : this.previousSibling;
1055 if (!dontPopulate && element)
1056 element._populateIfNeeded();
1057
1058 while (element && (skipUnrevealed ? (element.revealed() && element.expanded ? element.lastChild() : null) :
1059 element.lastChild())) {
1060 if (!dontPopulate)
1061 element._populateIfNeeded();
1062 element =
1063 (skipUnrevealed ? (element.revealed() && element.expanded ? element.la stChild() : null) :
1064 element.lastChild());
1065 }
1066
1067 if (element)
1068 return element;
1069
1070 if (!this.parent || this.parent.root)
1071 return null;
1072
1073 return this.parent;
1074 }
1075
1076 /**
1077 * @return {boolean}
1078 */
1079 isEventWithinDisclosureTriangle(event) {
1080 // FIXME: We should not use getComputedStyle(). For that we need to get rid of using ::before for disclosure triangle. (http://webk.it/74446)
1081 var paddingLeftValue = window.getComputedStyle(this._listItemNode).paddingLe ft;
1082 console.assert(paddingLeftValue.endsWith('px'));
1083 var computedLeftPadding = parseFloat(paddingLeftValue);
1084 var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
1085 return event.pageX >= left && event.pageX <= left + TreeElement._ArrowToggle Width && this._expandable;
1086 }
1087 };
385 1088
386 /** @const */ 1089 /** @const */
387 TreeElement._ArrowToggleWidth = 10; 1090 TreeElement._ArrowToggleWidth = 10;
388
389 TreeElement.prototype = {
390 /**
391 * @param {?TreeElement} ancestor
392 * @return {boolean}
393 */
394 hasAncestor: function(ancestor)
395 {
396 if (!ancestor)
397 return false;
398
399 var currentNode = this.parent;
400 while (currentNode) {
401 if (ancestor === currentNode)
402 return true;
403 currentNode = currentNode.parent;
404 }
405
406 return false;
407 },
408
409 /**
410 * @param {?TreeElement} ancestor
411 * @return {boolean}
412 */
413 hasAncestorOrSelf: function(ancestor)
414 {
415 return this === ancestor || this.hasAncestor(ancestor);
416 },
417
418 /**
419 * @return {!Array.<!TreeElement>}
420 */
421 children: function()
422 {
423 return this._children || [];
424 },
425
426 /**
427 * @return {number}
428 */
429 childCount: function()
430 {
431 return this._children ? this._children.length : 0;
432 },
433
434 /**
435 * @return {?TreeElement}
436 */
437 firstChild: function()
438 {
439 return this._children ? this._children[0] : null;
440 },
441
442 /**
443 * @return {?TreeElement}
444 */
445 lastChild: function()
446 {
447 return this._children ? this._children[this._children.length - 1] : null ;
448 },
449
450 /**
451 * @param {number} index
452 * @return {?TreeElement}
453 */
454 childAt: function(index)
455 {
456 return this._children ? this._children[index] : null;
457 },
458
459 /**
460 * @param {!TreeElement} child
461 * @return {number}
462 */
463 indexOfChild: function(child)
464 {
465 return this._children ? this._children.indexOf(child) : -1;
466 },
467
468 /**
469 * @param {!TreeElement} child
470 */
471 appendChild: function(child)
472 {
473 if (!this._children)
474 this._children = [];
475
476 var insertionIndex;
477 if (this.treeOutline && this.treeOutline._comparator)
478 insertionIndex = this._children.lowerBound(child, this.treeOutline._ comparator);
479 else
480 insertionIndex = this._children.length;
481 this.insertChild(child, insertionIndex);
482 },
483
484 /**
485 * @param {!TreeElement} child
486 * @param {number} index
487 */
488 insertChild: function(child, index)
489 {
490 if (!this._children)
491 this._children = [];
492
493 if (!child)
494 throw "child can't be undefined or null";
495
496 console.assert(!child.parent, "Attempting to insert a child that is alre ady in the tree, reparenting is not supported.");
497
498 var previousChild = (index > 0 ? this._children[index - 1] : null);
499 if (previousChild) {
500 previousChild.nextSibling = child;
501 child.previousSibling = previousChild;
502 } else {
503 child.previousSibling = null;
504 }
505
506 var nextChild = this._children[index];
507 if (nextChild) {
508 nextChild.previousSibling = child;
509 child.nextSibling = nextChild;
510 } else {
511 child.nextSibling = null;
512 }
513
514 this._children.splice(index, 0, child);
515
516 this.setExpandable(true);
517 child.parent = this;
518
519 if (this.treeOutline)
520 this.treeOutline._bindTreeElement(child);
521 for (var current = child.firstChild(); this.treeOutline && current; curr ent = current.traverseNextTreeElement(false, child, true))
522 this.treeOutline._bindTreeElement(current);
523 child.onattach();
524 child._ensureSelection();
525 if (this.treeOutline)
526 this.treeOutline.dispatchEventToListeners(TreeOutline.Events.Element Attached, child);
527 var nextSibling = child.nextSibling ? child.nextSibling._listItemNode : null;
528 this._childrenListNode.insertBefore(child._listItemNode, nextSibling);
529 this._childrenListNode.insertBefore(child._childrenListNode, nextSibling );
530 if (child.selected)
531 child.select();
532 if (child.expanded)
533 child.expand();
534 },
535
536 /**
537 * @param {number} childIndex
538 */
539 removeChildAtIndex: function(childIndex)
540 {
541 if (childIndex < 0 || childIndex >= this._children.length)
542 throw "childIndex out of range";
543
544 var child = this._children[childIndex];
545 this._children.splice(childIndex, 1);
546
547 var parent = child.parent;
548 if (this.treeOutline && this.treeOutline.selectedTreeElement && this.tre eOutline.selectedTreeElement.hasAncestorOrSelf(child)) {
549 if (child.nextSibling)
550 child.nextSibling.select(true);
551 else if (child.previousSibling)
552 child.previousSibling.select(true);
553 else if (parent)
554 parent.select(true);
555 }
556
557 if (child.previousSibling)
558 child.previousSibling.nextSibling = child.nextSibling;
559 if (child.nextSibling)
560 child.nextSibling.previousSibling = child.previousSibling;
561 child.parent = null;
562
563 if (this.treeOutline)
564 this.treeOutline._unbindTreeElement(child);
565 for (var current = child.firstChild(); this.treeOutline && current; curr ent = current.traverseNextTreeElement(false, child, true))
566 this.treeOutline._unbindTreeElement(current);
567
568 child._detach();
569 },
570
571 /**
572 * @param {!TreeElement} child
573 */
574 removeChild: function(child)
575 {
576 if (!child)
577 throw "child can't be undefined or null";
578 if (child.parent !== this)
579 return;
580
581 var childIndex = this._children.indexOf(child);
582 if (childIndex === -1)
583 throw "child not found in this node's children";
584
585 this.removeChildAtIndex(childIndex);
586 },
587
588 removeChildren: function()
589 {
590 if (!this.root && this.treeOutline && this.treeOutline.selectedTreeEleme nt && this.treeOutline.selectedTreeElement.hasAncestorOrSelf(this))
591 this.select(true);
592
593 for (var i = 0; this._children && i < this._children.length; ++i) {
594 var child = this._children[i];
595 child.previousSibling = null;
596 child.nextSibling = null;
597 child.parent = null;
598
599 if (this.treeOutline)
600 this.treeOutline._unbindTreeElement(child);
601 for (var current = child.firstChild(); this.treeOutline && current; current = current.traverseNextTreeElement(false, child, true))
602 this.treeOutline._unbindTreeElement(current);
603 child._detach();
604 }
605 this._children = [];
606 },
607
608 get selectable()
609 {
610 if (this._hidden)
611 return false;
612 return this._selectable;
613 },
614
615 set selectable(x)
616 {
617 this._selectable = x;
618 },
619
620 get listItemElement()
621 {
622 return this._listItemNode;
623 },
624
625 get childrenListElement()
626 {
627 return this._childrenListNode;
628 },
629
630 get title()
631 {
632 return this._title;
633 },
634
635 /**
636 * @param {string|!Node} x
637 */
638 set title(x)
639 {
640 if (this._title === x)
641 return;
642 this._title = x;
643
644 if (typeof x === "string") {
645 this._titleElement = createElementWithClass("span", "tree-element-ti tle");
646 this._titleElement.textContent = x;
647 this.tooltip = x;
648 } else {
649 this._titleElement = x;
650 this.tooltip = "";
651 }
652
653 this._listItemNode.removeChildren();
654 if (this._iconElement)
655 this._listItemNode.appendChild(this._iconElement);
656
657 this._listItemNode.appendChild(this._titleElement);
658 this._ensureSelection();
659 },
660
661 /**
662 * @return {string}
663 */
664 titleAsText: function()
665 {
666 if (!this._title)
667 return "";
668 if (typeof this._title === "string")
669 return this._title;
670 return this._title.textContent;
671 },
672
673 /**
674 * @param {!WebInspector.InplaceEditor.Config} editingConfig
675 */
676 startEditingTitle: function(editingConfig)
677 {
678 WebInspector.InplaceEditor.startEditing(this._titleElement, editingConfi g);
679 this.treeOutline._shadowRoot.getSelection().setBaseAndExtent(this._title Element, 0, this._titleElement, 1);
680 },
681
682 createIcon()
683 {
684 if (!this._iconElement) {
685 this._iconElement = createElementWithClass("div", "icon");
686 this._listItemNode.insertBefore(this._iconElement, this._listItemNod e.firstChild);
687 this._ensureSelection();
688 }
689 },
690
691 get tooltip()
692 {
693 return this._tooltip || "";
694 },
695
696 /**
697 * @param {string} x
698 */
699 set tooltip(x)
700 {
701 if (this._tooltip === x)
702 return;
703 this._tooltip = x;
704 this._listItemNode.title = x;
705 },
706
707 /**
708 * @return {boolean}
709 */
710 isExpandable: function()
711 {
712 return this._expandable;
713 },
714
715 /**
716 * @param {boolean} expandable
717 */
718 setExpandable: function(expandable)
719 {
720 if (this._expandable === expandable)
721 return;
722
723 this._expandable = expandable;
724
725 this._listItemNode.classList.toggle("parent", expandable);
726 if (!expandable)
727 this.collapse();
728 },
729
730 /**
731 * @param {boolean} collapsible
732 */
733 setCollapsible: function(collapsible)
734 {
735 if (this._collapsible === collapsible)
736 return;
737
738 this._collapsible = collapsible;
739
740 this._listItemNode.classList.toggle("always-parent", !collapsible);
741 if (!collapsible)
742 this.expand();
743 },
744
745 get hidden()
746 {
747 return this._hidden;
748 },
749
750 set hidden(x)
751 {
752 if (this._hidden === x)
753 return;
754
755 this._hidden = x;
756
757 this._listItemNode.classList.toggle("hidden", x);
758 this._childrenListNode.classList.toggle("hidden", x);
759 },
760
761 invalidateChildren: function()
762 {
763 if (this._children) {
764 this.removeChildren();
765 this._children = null;
766 }
767 },
768
769 _ensureSelection: function()
770 {
771 if (!this.treeOutline || !this.treeOutline._renderSelection)
772 return;
773 if (!this._selectionElement)
774 this._selectionElement = createElementWithClass("div", "selection fi ll");
775 this._listItemNode.insertBefore(this._selectionElement, this.listItemEle ment.firstChild);
776 },
777
778 /**
779 * @param {!Event} event
780 */
781 _treeElementToggled: function(event)
782 {
783 var element = event.currentTarget;
784 if (element.treeElement !== this || element.hasSelection())
785 return;
786
787 var toggleOnClick = this.toggleOnClick && !this.selectable;
788 var isInTriangle = this.isEventWithinDisclosureTriangle(event);
789 if (!toggleOnClick && !isInTriangle)
790 return;
791
792 if (event.target && event.target.enclosingNodeOrSelfWithNodeName("a"))
793 return;
794
795 if (this.expanded) {
796 if (event.altKey)
797 this.collapseRecursively();
798 else
799 this.collapse();
800 } else {
801 if (event.altKey)
802 this.expandRecursively();
803 else
804 this.expand();
805 }
806 event.consume();
807 },
808
809 /**
810 * @param {!Event} event
811 */
812 _handleMouseDown: function(event)
813 {
814 var element = event.currentTarget;
815 if (!element)
816 return;
817 if (!this.selectable)
818 return;
819 if (element.treeElement !== this)
820 return;
821
822 if (this.isEventWithinDisclosureTriangle(event))
823 return;
824
825 this.selectOnMouseDown(event);
826 },
827
828 /**
829 * @param {!Event} event
830 */
831 _handleDoubleClick: function(event)
832 {
833 var element = event.currentTarget;
834 if (!element || element.treeElement !== this)
835 return;
836
837 var handled = this.ondblclick(event);
838 if (handled)
839 return;
840 if (this._expandable && !this.expanded)
841 this.expand();
842 },
843
844 _detach: function()
845 {
846 this._listItemNode.remove();
847 this._childrenListNode.remove();
848 },
849
850 collapse: function()
851 {
852 if (!this.expanded || !this._collapsible)
853 return;
854 this._listItemNode.classList.remove("expanded");
855 this._childrenListNode.classList.remove("expanded");
856 this.expanded = false;
857 this.oncollapse();
858 if (this.treeOutline)
859 this.treeOutline.dispatchEventToListeners(TreeOutline.Events.Element Collapsed, this);
860 },
861
862 collapseRecursively: function()
863 {
864 var item = this;
865 while (item) {
866 if (item.expanded)
867 item.collapse();
868 item = item.traverseNextTreeElement(false, this, true);
869 }
870 },
871
872 expand: function()
873 {
874 if (!this._expandable || (this.expanded && this._children))
875 return;
876
877 // Set this before onpopulate. Since onpopulate can add elements, this m akes
878 // sure the expanded flag is true before calling those functions. This p revents the possibility
879 // of an infinite loop if onpopulate were to call expand.
880
881 this.expanded = true;
882
883 this._populateIfNeeded();
884 this._listItemNode.classList.add("expanded");
885 this._childrenListNode.classList.add("expanded");
886
887 if (this.treeOutline) {
888 this.onexpand();
889 this.treeOutline.dispatchEventToListeners(TreeOutline.Events.Element Expanded, this);
890 }
891 },
892
893 /**
894 * @param {number=} maxDepth
895 */
896 expandRecursively: function(maxDepth)
897 {
898 var item = this;
899 var info = {};
900 var depth = 0;
901
902 // The Inspector uses TreeOutlines to represents object properties, so r ecursive expansion
903 // in some case can be infinite, since JavaScript objects can hold circu lar references.
904 // So default to a recursion cap of 3 levels, since that gives fairly go od results.
905 if (isNaN(maxDepth))
906 maxDepth = 3;
907
908 while (item) {
909 if (depth < maxDepth)
910 item.expand();
911 item = item.traverseNextTreeElement(false, this, (depth >= maxDepth) , info);
912 depth += info.depthChange;
913 }
914 },
915
916 /**
917 * @param {boolean=} center
918 */
919 reveal: function(center)
920 {
921 var currentAncestor = this.parent;
922 while (currentAncestor && !currentAncestor.root) {
923 if (!currentAncestor.expanded)
924 currentAncestor.expand();
925 currentAncestor = currentAncestor.parent;
926 }
927
928 this.treeOutline._deferredScrollIntoView(this, !!center);
929 },
930
931 /**
932 * @return {boolean}
933 */
934 revealed: function()
935 {
936 var currentAncestor = this.parent;
937 while (currentAncestor && !currentAncestor.root) {
938 if (!currentAncestor.expanded)
939 return false;
940 currentAncestor = currentAncestor.parent;
941 }
942
943 return true;
944 },
945
946 selectOnMouseDown: function(event)
947 {
948 if (this.select(false, true))
949 event.consume(true);
950 },
951
952 /**
953 * @param {boolean=} omitFocus
954 * @param {boolean=} selectedByUser
955 * @return {boolean}
956 */
957 select: function(omitFocus, selectedByUser)
958 {
959 if (!this.treeOutline || !this.selectable || this.selected)
960 return false;
961
962 if (this.treeOutline.selectedTreeElement)
963 this.treeOutline.selectedTreeElement.deselect();
964 this.treeOutline.selectedTreeElement = null;
965
966 if (this.treeOutline._rootElement === this)
967 return false;
968
969 this.selected = true;
970
971 if (!omitFocus)
972 this.treeOutline.focus();
973
974 // Focusing on another node may detach "this" from tree.
975 if (!this.treeOutline)
976 return false;
977 this.treeOutline.selectedTreeElement = this;
978 this._listItemNode.classList.add("selected");
979 this.treeOutline.dispatchEventToListeners(TreeOutline.Events.ElementSele cted, this);
980 return this.onselect(selectedByUser);
981 },
982
983 /**
984 * @param {boolean=} omitFocus
985 */
986 revealAndSelect: function(omitFocus)
987 {
988 this.reveal(true);
989 this.select(omitFocus);
990 },
991
992 /**
993 * @param {boolean=} supressOnDeselect
994 */
995 deselect: function(supressOnDeselect)
996 {
997 if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this | | !this.selected)
998 return;
999
1000 this.selected = false;
1001 this.treeOutline.selectedTreeElement = null;
1002 this._listItemNode.classList.remove("selected");
1003 },
1004
1005 _populateIfNeeded: function()
1006 {
1007 if (this.treeOutline && this._expandable && !this._children) {
1008 this._children = [];
1009 this.onpopulate();
1010 }
1011 },
1012
1013 onpopulate: function()
1014 {
1015 // Overridden by subclasses.
1016 },
1017
1018 /**
1019 * @return {boolean}
1020 */
1021 onenter: function()
1022 {
1023 return false;
1024 },
1025
1026 /**
1027 * @return {boolean}
1028 */
1029 ondelete: function()
1030 {
1031 return false;
1032 },
1033
1034 /**
1035 * @return {boolean}
1036 */
1037 onspace: function()
1038 {
1039 return false;
1040 },
1041
1042 onbind: function()
1043 {
1044 },
1045
1046 onunbind: function()
1047 {
1048 },
1049
1050 onattach: function()
1051 {
1052 },
1053
1054 onexpand: function()
1055 {
1056 },
1057
1058 oncollapse: function()
1059 {
1060 },
1061
1062 /**
1063 * @param {!Event} e
1064 * @return {boolean}
1065 */
1066 ondblclick: function(e)
1067 {
1068 return false;
1069 },
1070
1071 /**
1072 * @param {boolean=} selectedByUser
1073 * @return {boolean}
1074 */
1075 onselect: function(selectedByUser)
1076 {
1077 return false;
1078 },
1079
1080 /**
1081 * @param {boolean} skipUnrevealed
1082 * @param {?TreeElement=} stayWithin
1083 * @param {boolean=} dontPopulate
1084 * @param {!Object=} info
1085 * @return {?TreeElement}
1086 */
1087 traverseNextTreeElement: function(skipUnrevealed, stayWithin, dontPopulate, info)
1088 {
1089 if (!dontPopulate)
1090 this._populateIfNeeded();
1091
1092 if (info)
1093 info.depthChange = 0;
1094
1095 var element = skipUnrevealed ? (this.revealed() ? this.firstChild() : nu ll) : this.firstChild();
1096 if (element && (!skipUnrevealed || (skipUnrevealed && this.expanded))) {
1097 if (info)
1098 info.depthChange = 1;
1099 return element;
1100 }
1101
1102 if (this === stayWithin)
1103 return null;
1104
1105 element = skipUnrevealed ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
1106 if (element)
1107 return element;
1108
1109 element = this;
1110 while (element && !element.root && !(skipUnrevealed ? (element.revealed( ) ? element.nextSibling : null) : element.nextSibling) && element.parent !== sta yWithin) {
1111 if (info)
1112 info.depthChange -= 1;
1113 element = element.parent;
1114 }
1115
1116 if (!element || element.root)
1117 return null;
1118
1119 return (skipUnrevealed ? (element.revealed() ? element.nextSibling : nul l) : element.nextSibling);
1120 },
1121
1122 /**
1123 * @param {boolean} skipUnrevealed
1124 * @param {boolean=} dontPopulate
1125 * @return {?TreeElement}
1126 */
1127 traversePreviousTreeElement: function(skipUnrevealed, dontPopulate)
1128 {
1129 var element = skipUnrevealed ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
1130 if (!dontPopulate && element)
1131 element._populateIfNeeded();
1132
1133 while (element && (skipUnrevealed ? (element.revealed() && element.expan ded ? element.lastChild() : null) : element.lastChild())) {
1134 if (!dontPopulate)
1135 element._populateIfNeeded();
1136 element = (skipUnrevealed ? (element.revealed() && element.expanded ? element.lastChild() : null) : element.lastChild());
1137 }
1138
1139 if (element)
1140 return element;
1141
1142 if (!this.parent || this.parent.root)
1143 return null;
1144
1145 return this.parent;
1146 },
1147
1148 /**
1149 * @return {boolean}
1150 */
1151 isEventWithinDisclosureTriangle: function(event)
1152 {
1153 // FIXME: We should not use getComputedStyle(). For that we need to get rid of using ::before for disclosure triangle. (http://webk.it/74446)
1154 var paddingLeftValue = window.getComputedStyle(this._listItemNode).paddi ngLeft;
1155 console.assert(paddingLeftValue.endsWith("px"));
1156 var computedLeftPadding = parseFloat(paddingLeftValue);
1157 var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
1158 return event.pageX >= left && event.pageX <= left + TreeElement._ArrowTo ggleWidth && this._expandable;
1159 }
1160 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698