blob: 73d48179102e9093d67eb93d5b5976f0c2d04ccd [file] [log] [blame]
Shane Cliffordec98d522020-05-01 17:24:531// Copyright 2020 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Blink Reformat4c46d092018-04-07 15:32:375/*
6 * Copyright (C) 2007 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
18 * its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
Tim van der Lippeee97fa32020-04-23 15:20:5633// @ts-nocheck
34// TODO(crbug.com/1011811): Enable TypeScript compiler checks
35
Paul Lewis17e384e2020-01-08 15:46:5136import * as Common from '../common/common.js';
Tim van der Lippeaa76aa22020-02-14 14:38:2437
38import * as ARIAUtils from './ARIAUtils.js';
Paul Lewis9950e182019-12-16 16:06:0739import {Icon} from './Icon.js'; // eslint-disable-line no-unused-vars
40import {Config, InplaceEditor} from './InplaceEditor.js'; // eslint-disable-line no-unused-vars
41import {Keys} from './KeyboardShortcut.js';
42import {isEditing} from './UIUtils.js';
43import {appendStyle} from './utils/append-style.js';
44import {createShadowRootWithCoreStyles} from './utils/create-shadow-root-with-core-styles.js';
45
Blink Reformat4c46d092018-04-07 15:32:3746/**
47 * @unrestricted
48 */
Paul Lewis17e384e2020-01-08 15:46:5149export class TreeOutline extends Common.ObjectWrapper.ObjectWrapper {
Blink Reformat4c46d092018-04-07 15:32:3750 constructor() {
51 super();
52 this._createRootElement();
53
Tim van der Lippe0830b3d2019-10-03 13:20:0754 /** @type {?TreeElement} */
Blink Reformat4c46d092018-04-07 15:32:3755 this.selectedTreeElement = null;
56 this.expandTreeElementsWhenArrowing = false;
Tim van der Lippe0830b3d2019-10-03 13:20:0757 /** @type {?function(!TreeElement, !TreeElement):number} */
Blink Reformat4c46d092018-04-07 15:32:3758 this._comparator = null;
59
60 this.contentElement = this._rootElement._childrenListNode;
Erik Luoea8f5092018-09-19 22:37:1361 this.contentElement.addEventListener('keydown', this._treeKeyDown.bind(this), false);
Blink Reformat4c46d092018-04-07 15:32:3762
Erik Luocc14b812018-11-03 01:33:0963 this._preventTabOrder = false;
Erik Luod6bf97b2018-08-25 02:06:5164 this._showSelectionOnKeyboardFocus = false;
Amanda Baker05d6a232019-10-24 01:23:3565 this.setFocusable(true);
Blink Reformat4c46d092018-04-07 15:32:3766 this.element = this.contentElement;
Tim van der Lippeaa76aa22020-02-14 14:38:2467 ARIAUtils.markAsTree(this.element);
Blink Reformat4c46d092018-04-07 15:32:3768 }
69
Erik Luod6bf97b2018-08-25 02:06:5170 /**
71 * @param {boolean} show
Erik Luocc14b812018-11-03 01:33:0972 * @param {boolean=} preventTabOrder
Erik Luod6bf97b2018-08-25 02:06:5173 */
Erik Luocc14b812018-11-03 01:33:0974 setShowSelectionOnKeyboardFocus(show, preventTabOrder) {
Erik Luod6bf97b2018-08-25 02:06:5175 this.contentElement.classList.toggle('hide-selection-when-blurred', show);
Erik Luocc14b812018-11-03 01:33:0976 this._preventTabOrder = !!preventTabOrder;
Amanda Baker05d6a232019-10-24 01:23:3577 if (this._focusable) {
78 this.contentElement.tabIndex = !!preventTabOrder ? -1 : 0;
79 }
Erik Luod6bf97b2018-08-25 02:06:5180 this._showSelectionOnKeyboardFocus = show;
81 }
82
Blink Reformat4c46d092018-04-07 15:32:3783 _createRootElement() {
Tim van der Lippe0830b3d2019-10-03 13:20:0784 this._rootElement = new TreeElement();
Blink Reformat4c46d092018-04-07 15:32:3785 this._rootElement.treeOutline = this;
86 this._rootElement.root = true;
87 this._rootElement.selectable = false;
88 this._rootElement.expanded = true;
89 this._rootElement._childrenListNode.classList.remove('children');
90 }
91
92 /**
Tim van der Lippe0830b3d2019-10-03 13:20:0793 * @return {!TreeElement}
Blink Reformat4c46d092018-04-07 15:32:3794 */
95 rootElement() {
96 return this._rootElement;
97 }
98
99 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07100 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37101 */
102 firstChild() {
103 return this._rootElement.firstChild();
104 }
105
106 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07107 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37108 */
109 _lastDescendent() {
110 let last = this._rootElement.lastChild();
Tim van der Lippe1d6e57a2019-09-30 11:55:34111 while (last.expanded && last.childCount()) {
Blink Reformat4c46d092018-04-07 15:32:37112 last = last.lastChild();
Tim van der Lippe1d6e57a2019-09-30 11:55:34113 }
Blink Reformat4c46d092018-04-07 15:32:37114 return last;
115 }
116
117 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07118 * @param {!TreeElement} child
Sigurd Schneiderdc2aa0d2020-07-09 10:20:09119 * @param {(function(!TreeElement, !TreeElement):number)=} comparator
Blink Reformat4c46d092018-04-07 15:32:37120 */
Sigurd Schneiderdc2aa0d2020-07-09 10:20:09121 appendChild(child, comparator) {
122 this._rootElement.appendChild(child, comparator);
Blink Reformat4c46d092018-04-07 15:32:37123 }
124
125 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07126 * @param {!TreeElement} child
Blink Reformat4c46d092018-04-07 15:32:37127 * @param {number} index
128 */
129 insertChild(child, index) {
130 this._rootElement.insertChild(child, index);
131 }
132
133 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07134 * @param {!TreeElement} child
Blink Reformat4c46d092018-04-07 15:32:37135 */
136 removeChild(child) {
137 this._rootElement.removeChild(child);
138 }
139
140 removeChildren() {
141 this._rootElement.removeChildren();
142 }
143
144 /**
145 * @param {number} x
146 * @param {number} y
Tim van der Lippe0830b3d2019-10-03 13:20:07147 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37148 */
149 treeElementFromPoint(x, y) {
150 const node = this.contentElement.ownerDocument.deepElementFromPoint(x, y);
Tim van der Lippe1d6e57a2019-09-30 11:55:34151 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37152 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34153 }
Blink Reformat4c46d092018-04-07 15:32:37154
155 const listNode = node.enclosingNodeOrSelfWithNodeNameInArray(['ol', 'li']);
Tim van der Lippe1d6e57a2019-09-30 11:55:34156 if (listNode) {
Blink Reformat4c46d092018-04-07 15:32:37157 return listNode.parentTreeElement || listNode.treeElement;
Tim van der Lippe1d6e57a2019-09-30 11:55:34158 }
Blink Reformat4c46d092018-04-07 15:32:37159 return null;
160 }
161
162 /**
163 * @param {?Event} event
Tim van der Lippe0830b3d2019-10-03 13:20:07164 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37165 */
166 treeElementFromEvent(event) {
167 return event ? this.treeElementFromPoint(event.pageX, event.pageY) : null;
168 }
169
170 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07171 * @param {?function(!TreeElement, !TreeElement):number} comparator
Blink Reformat4c46d092018-04-07 15:32:37172 */
173 setComparator(comparator) {
174 this._comparator = comparator;
175 }
176
177 /**
178 * @param {boolean} focusable
179 */
180 setFocusable(focusable) {
Amanda Baker05d6a232019-10-24 01:23:35181 this._focusable = focusable;
182 this.updateFocusable();
183 }
184
185 updateFocusable() {
186 if (this._focusable) {
187 this.contentElement.tabIndex = (this._preventTabOrder || !!this.selectedTreeElement) ? -1 : 0;
Tim van der Lippe1d6e57a2019-09-30 11:55:34188 if (this.selectedTreeElement) {
Blink Reformat4c46d092018-04-07 15:32:37189 this.selectedTreeElement._setFocusable(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34190 }
Blink Reformat4c46d092018-04-07 15:32:37191 } else {
Blink Reformat4c46d092018-04-07 15:32:37192 this.contentElement.removeAttribute('tabIndex');
Tim van der Lippe1d6e57a2019-09-30 11:55:34193 if (this.selectedTreeElement) {
Blink Reformat4c46d092018-04-07 15:32:37194 this.selectedTreeElement._setFocusable(false);
Tim van der Lippe1d6e57a2019-09-30 11:55:34195 }
Blink Reformat4c46d092018-04-07 15:32:37196 }
197 }
198
199 focus() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34200 if (this.selectedTreeElement) {
Blink Reformat4c46d092018-04-07 15:32:37201 this.selectedTreeElement.listItemElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:34202 } else {
Blink Reformat4c46d092018-04-07 15:32:37203 this.contentElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:34204 }
Blink Reformat4c46d092018-04-07 15:32:37205 }
206
Pavel Feldman7ad5b272019-01-08 03:01:00207 useLightSelectionColor() {
208 this._useLightSelectionColor = true;
209 }
210
Blink Reformat4c46d092018-04-07 15:32:37211 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07212 * @param {!TreeElement} element
Blink Reformat4c46d092018-04-07 15:32:37213 */
214 _bindTreeElement(element) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34215 if (element.treeOutline) {
Blink Reformat4c46d092018-04-07 15:32:37216 console.error('Binding element for the second time: ' + new Error().stack);
Tim van der Lippe1d6e57a2019-09-30 11:55:34217 }
Blink Reformat4c46d092018-04-07 15:32:37218 element.treeOutline = this;
219 element.onbind();
220 }
221
222 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07223 * @param {!TreeElement} element
Blink Reformat4c46d092018-04-07 15:32:37224 */
225 _unbindTreeElement(element) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34226 if (!element.treeOutline) {
Blink Reformat4c46d092018-04-07 15:32:37227 console.error('Unbinding element that was not bound: ' + new Error().stack);
Tim van der Lippe1d6e57a2019-09-30 11:55:34228 }
Blink Reformat4c46d092018-04-07 15:32:37229
230 element.deselect();
231 element.onunbind();
232 element.treeOutline = null;
233 }
234
235 /**
236 * @return {boolean}
237 */
238 selectPrevious() {
239 let nextSelectedElement = this.selectedTreeElement.traversePreviousTreeElement(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34240 while (nextSelectedElement && !nextSelectedElement.selectable) {
Blink Reformat4c46d092018-04-07 15:32:37241 nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!this.expandTreeElementsWhenArrowing);
Tim van der Lippe1d6e57a2019-09-30 11:55:34242 }
243 if (!nextSelectedElement) {
Blink Reformat4c46d092018-04-07 15:32:37244 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34245 }
Blink Reformat4c46d092018-04-07 15:32:37246 nextSelectedElement.select(false, true);
247 return true;
248 }
249
250 /**
251 * @return {boolean}
252 */
253 selectNext() {
254 let nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34255 while (nextSelectedElement && !nextSelectedElement.selectable) {
Blink Reformat4c46d092018-04-07 15:32:37256 nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.expandTreeElementsWhenArrowing);
Tim van der Lippe1d6e57a2019-09-30 11:55:34257 }
258 if (!nextSelectedElement) {
Blink Reformat4c46d092018-04-07 15:32:37259 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34260 }
Blink Reformat4c46d092018-04-07 15:32:37261 nextSelectedElement.select(false, true);
262 return true;
263 }
264
Amanda Baker98c26ea2019-10-24 01:40:52265 /**
266 * @param {boolean=} omitFocus
267 * @param {boolean=} selectedByUser
268 */
269 forceSelect(omitFocus = false, selectedByUser = true) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34270 if (this.selectedTreeElement) {
Erik Luo31c21f62018-12-13 03:39:39271 this.selectedTreeElement.deselect();
Tim van der Lippe1d6e57a2019-09-30 11:55:34272 }
Amanda Baker98c26ea2019-10-24 01:40:52273 this._selectFirst(omitFocus, selectedByUser);
Erik Luo31c21f62018-12-13 03:39:39274 }
275
Blink Reformat4c46d092018-04-07 15:32:37276 /**
Amanda Baker98c26ea2019-10-24 01:40:52277 * @param {boolean=} omitFocus
278 * @param {boolean=} selectedByUser
Blink Reformat4c46d092018-04-07 15:32:37279 * @return {boolean}
280 */
Amanda Baker98c26ea2019-10-24 01:40:52281 _selectFirst(omitFocus = false, selectedByUser = true) {
Blink Reformat4c46d092018-04-07 15:32:37282 let first = this.firstChild();
Tim van der Lippe1d6e57a2019-09-30 11:55:34283 while (first && !first.selectable) {
Blink Reformat4c46d092018-04-07 15:32:37284 first = first.traverseNextTreeElement(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34285 }
286 if (!first) {
Blink Reformat4c46d092018-04-07 15:32:37287 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34288 }
Amanda Baker98c26ea2019-10-24 01:40:52289 first.select(omitFocus, selectedByUser);
Blink Reformat4c46d092018-04-07 15:32:37290 return true;
291 }
292
293 /**
294 * @return {boolean}
295 */
296 _selectLast() {
297 let last = this._lastDescendent();
Tim van der Lippe1d6e57a2019-09-30 11:55:34298 while (last && !last.selectable) {
Blink Reformat4c46d092018-04-07 15:32:37299 last = last.traversePreviousTreeElement(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34300 }
301 if (!last) {
Blink Reformat4c46d092018-04-07 15:32:37302 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34303 }
Blink Reformat4c46d092018-04-07 15:32:37304 last.select(false, true);
305 return true;
306 }
307
308 /**
Blink Reformat4c46d092018-04-07 15:32:37309 * @param {!Event} event
310 */
311 _treeKeyDown(event) {
Paul Lewis9950e182019-12-16 16:06:07312 if (event.shiftKey || event.metaKey || event.ctrlKey || isEditing()) {
Blink Reformat4c46d092018-04-07 15:32:37313 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34314 }
Blink Reformat4c46d092018-04-07 15:32:37315
316 let handled = false;
Amanda Baker05d6a232019-10-24 01:23:35317 if (!this.selectedTreeElement) {
318 if (event.key === 'ArrowUp' && !event.altKey) {
319 handled = this._selectLast();
320 } else if (event.key === 'ArrowDown' && !event.altKey) {
321 handled = this._selectFirst();
322 }
323 } else if (event.key === 'ArrowUp' && !event.altKey) {
Blink Reformat4c46d092018-04-07 15:32:37324 handled = this.selectPrevious();
325 } else if (event.key === 'ArrowDown' && !event.altKey) {
326 handled = this.selectNext();
327 } else if (event.key === 'ArrowLeft') {
328 handled = this.selectedTreeElement.collapseOrAscend(event.altKey);
329 } else if (event.key === 'ArrowRight') {
330 if (!this.selectedTreeElement.revealed()) {
331 this.selectedTreeElement.reveal();
332 handled = true;
333 } else {
334 handled = this.selectedTreeElement.descendOrExpand(event.altKey);
335 }
336 } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /* Delete */) {
337 handled = this.selectedTreeElement.ondelete();
338 } else if (isEnterKey(event)) {
339 handled = this.selectedTreeElement.onenter();
Paul Lewis9950e182019-12-16 16:06:07340 } else if (event.keyCode === Keys.Space.code) {
Blink Reformat4c46d092018-04-07 15:32:37341 handled = this.selectedTreeElement.onspace();
342 } else if (event.key === 'Home') {
Erik Luo31c21f62018-12-13 03:39:39343 handled = this._selectFirst();
Blink Reformat4c46d092018-04-07 15:32:37344 } else if (event.key === 'End') {
345 handled = this._selectLast();
346 }
347
Tim van der Lippe1d6e57a2019-09-30 11:55:34348 if (handled) {
Blink Reformat4c46d092018-04-07 15:32:37349 event.consume(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34350 }
Blink Reformat4c46d092018-04-07 15:32:37351 }
352
353 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07354 * @param {!TreeElement} treeElement
Blink Reformat4c46d092018-04-07 15:32:37355 * @param {boolean} center
356 */
357 _deferredScrollIntoView(treeElement, center) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34358 if (!this._treeElementToScrollIntoView) {
Blink Reformat4c46d092018-04-07 15:32:37359 this.element.window().requestAnimationFrame(deferredScrollIntoView.bind(this));
Tim van der Lippe1d6e57a2019-09-30 11:55:34360 }
Blink Reformat4c46d092018-04-07 15:32:37361 this._treeElementToScrollIntoView = treeElement;
362 this._centerUponScrollIntoView = center;
363 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07364 * @this {TreeOutline}
Blink Reformat4c46d092018-04-07 15:32:37365 */
366 function deferredScrollIntoView() {
Shane Cliffordec98d522020-05-01 17:24:53367 // This function no longer uses scrollIntoViewIfNeeded because users were bothered
368 // by the fact that it always scrolls in both direction even if only one is necessary
369 // to bring the item into view.
370
371 const itemRect = this._treeElementToScrollIntoView.listItemElement.getBoundingClientRect();
372 const treeRect = this.contentElement.getBoundingClientRect();
373 const viewRect = this.element.getBoundingClientRect();
374
375 const currentScrollX = viewRect.left - treeRect.left;
376 const currentScrollY = viewRect.top - treeRect.top;
377
378 // Only scroll into view on each axis if the item is not visible at all
379 // but if we do scroll and _centerUponScrollIntoView is true
380 // then we center the top left corner of the item in view.
381 let deltaLeft = itemRect.left - treeRect.left;
382 if (deltaLeft > currentScrollX && deltaLeft < currentScrollX + viewRect.width) {
383 deltaLeft = currentScrollX;
384 } else if (this._centerUponScrollIntoView) {
385 deltaLeft = deltaLeft - viewRect.width / 2;
386 }
387 let deltaTop = itemRect.top - treeRect.top;
388 if (deltaTop > currentScrollY && deltaTop < currentScrollY + viewRect.height) {
389 deltaTop = currentScrollY;
390 } else if (this._centerUponScrollIntoView) {
391 deltaTop = deltaTop - viewRect.height / 2;
392 }
393 this.element.scrollTo(deltaLeft, deltaTop);
Blink Reformat4c46d092018-04-07 15:32:37394 delete this._treeElementToScrollIntoView;
395 delete this._centerUponScrollIntoView;
396 }
397 }
Tim van der Lippe0830b3d2019-10-03 13:20:07398}
Blink Reformat4c46d092018-04-07 15:32:37399
400/** @enum {symbol} */
Paul Lewis9950e182019-12-16 16:06:07401export const Events = {
Blink Reformat4c46d092018-04-07 15:32:37402 ElementAttached: Symbol('ElementAttached'),
Erik Luo96fee7b2019-06-21 04:30:29403 ElementsDetached: Symbol('ElementsDetached'),
Blink Reformat4c46d092018-04-07 15:32:37404 ElementExpanded: Symbol('ElementExpanded'),
405 ElementCollapsed: Symbol('ElementCollapsed'),
406 ElementSelected: Symbol('ElementSelected')
407};
408
409/**
410 * @unrestricted
411 */
Tim van der Lippe0830b3d2019-10-03 13:20:07412export class TreeOutlineInShadow extends TreeOutline {
Blink Reformat4c46d092018-04-07 15:32:37413 constructor() {
414 super();
415 this.contentElement.classList.add('tree-outline');
416
417 // Redefine element to the external one.
418 this.element = createElement('div');
Paul Lewis9950e182019-12-16 16:06:07419 this._shadowRoot = createShadowRootWithCoreStyles(this.element, 'ui/treeoutline.css');
Blink Reformat4c46d092018-04-07 15:32:37420 this._disclosureElement = this._shadowRoot.createChild('div', 'tree-outline-disclosure');
421 this._disclosureElement.appendChild(this.contentElement);
422 this._renderSelection = true;
423 }
424
425 /**
426 * @param {string} cssFile
Jack Franklin71519f82020-11-03 12:08:59427 * @param {!{enableLegacyPatching:boolean}} options
Blink Reformat4c46d092018-04-07 15:32:37428 */
Jack Franklin71519f82020-11-03 12:08:59429 registerRequiredCSS(cssFile, options) {
430 appendStyle(this._shadowRoot, cssFile, options);
Blink Reformat4c46d092018-04-07 15:32:37431 }
432
433 hideOverflow() {
434 this._disclosureElement.classList.add('tree-outline-disclosure-hide-overflow');
435 }
436
437 makeDense() {
438 this.contentElement.classList.add('tree-outline-dense');
439 }
Tim van der Lippe0830b3d2019-10-03 13:20:07440}
Blink Reformat4c46d092018-04-07 15:32:37441
442/**
443 * @unrestricted
444 */
Tim van der Lippe0830b3d2019-10-03 13:20:07445export class TreeElement {
Blink Reformat4c46d092018-04-07 15:32:37446 /**
447 * @param {(string|!Node)=} title
448 * @param {boolean=} expandable
449 */
450 constructor(title, expandable) {
Tim van der Lippe0830b3d2019-10-03 13:20:07451 /** @type {?TreeOutline} */
Blink Reformat4c46d092018-04-07 15:32:37452 this.treeOutline = null;
453 this.parent = null;
454 this.previousSibling = null;
455 this.nextSibling = null;
456 this._boundOnFocus = this._onFocus.bind(this);
457 this._boundOnBlur = this._onBlur.bind(this);
458
Changhao Hanc1817b22020-09-07 09:04:18459 this._listItemNode = /** @type {!HTMLLIElement} */ (createElement('li'));
Simon Zünd7334f5c2020-10-07 08:06:54460
Tim van der Lippeffa78622019-09-16 12:07:12461 this.titleElement = this._listItemNode.createChild('span', 'tree-element-title');
Blink Reformat4c46d092018-04-07 15:32:37462 this._listItemNode.treeElement = this;
Tim van der Lippe1d6e57a2019-09-30 11:55:34463 if (title) {
Blink Reformat4c46d092018-04-07 15:32:37464 this.title = title;
Tim van der Lippe1d6e57a2019-09-30 11:55:34465 }
Blink Reformat4c46d092018-04-07 15:32:37466 this._listItemNode.addEventListener('mousedown', this._handleMouseDown.bind(this), false);
467 this._listItemNode.addEventListener('click', this._treeElementToggled.bind(this), false);
468 this._listItemNode.addEventListener('dblclick', this._handleDoubleClick.bind(this), false);
Tim van der Lippeaa76aa22020-02-14 14:38:24469 ARIAUtils.markAsTreeitem(this._listItemNode);
Blink Reformat4c46d092018-04-07 15:32:37470
471 this._childrenListNode = createElement('ol');
472 this._childrenListNode.parentTreeElement = this;
473 this._childrenListNode.classList.add('children');
Tim van der Lippeaa76aa22020-02-14 14:38:24474 ARIAUtils.markAsGroup(this._childrenListNode);
Blink Reformat4c46d092018-04-07 15:32:37475
476 this._hidden = false;
477 this._selectable = true;
478 this.expanded = false;
479 this.selected = false;
480 this.setExpandable(expandable || false);
481 this._collapsible = true;
Changhao Hanbb4af122020-09-10 13:45:25482 this.toggleOnClick = false;
Blink Reformat4c46d092018-04-07 15:32:37483 }
484
485 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07486 * @param {?TreeElement} ancestor
Blink Reformat4c46d092018-04-07 15:32:37487 * @return {boolean}
488 */
489 hasAncestor(ancestor) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34490 if (!ancestor) {
Blink Reformat4c46d092018-04-07 15:32:37491 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34492 }
Blink Reformat4c46d092018-04-07 15:32:37493
494 let currentNode = this.parent;
495 while (currentNode) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34496 if (ancestor === currentNode) {
Blink Reformat4c46d092018-04-07 15:32:37497 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34498 }
Blink Reformat4c46d092018-04-07 15:32:37499 currentNode = currentNode.parent;
500 }
501
502 return false;
503 }
504
505 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07506 * @param {?TreeElement} ancestor
Blink Reformat4c46d092018-04-07 15:32:37507 * @return {boolean}
508 */
509 hasAncestorOrSelf(ancestor) {
510 return this === ancestor || this.hasAncestor(ancestor);
511 }
512
513 /**
Amanda Baker98c26ea2019-10-24 01:40:52514 * @return {boolean}
515 */
516 isHidden() {
517 if (this.hidden) {
518 return true;
519 }
520
521 let currentNode = this.parent;
522 while (currentNode) {
523 if (currentNode.hidden) {
524 return true;
525 }
526 currentNode = currentNode.parent;
527 }
528
529 return false;
530 }
531
532 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07533 * @return {!Array.<!TreeElement>}
Blink Reformat4c46d092018-04-07 15:32:37534 */
535 children() {
536 return this._children || [];
537 }
538
539 /**
540 * @return {number}
541 */
542 childCount() {
543 return this._children ? this._children.length : 0;
544 }
545
546 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07547 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37548 */
549 firstChild() {
550 return this._children ? this._children[0] : null;
551 }
552
553 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07554 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37555 */
556 lastChild() {
557 return this._children ? this._children[this._children.length - 1] : null;
558 }
559
560 /**
561 * @param {number} index
Tim van der Lippe0830b3d2019-10-03 13:20:07562 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37563 */
564 childAt(index) {
565 return this._children ? this._children[index] : null;
566 }
567
568 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07569 * @param {!TreeElement} child
Blink Reformat4c46d092018-04-07 15:32:37570 * @return {number}
571 */
572 indexOfChild(child) {
573 return this._children ? this._children.indexOf(child) : -1;
574 }
575
576 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07577 * @param {!TreeElement} child
Sigurd Schneiderdc2aa0d2020-07-09 10:20:09578 * @param {(function(!TreeElement, !TreeElement):number)=} comparator
Blink Reformat4c46d092018-04-07 15:32:37579 */
Sigurd Schneiderdc2aa0d2020-07-09 10:20:09580 appendChild(child, comparator) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34581 if (!this._children) {
Blink Reformat4c46d092018-04-07 15:32:37582 this._children = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34583 }
Blink Reformat4c46d092018-04-07 15:32:37584
585 let insertionIndex;
Sigurd Schneiderdc2aa0d2020-07-09 10:20:09586 if (comparator) {
587 insertionIndex = this._children.lowerBound(child, comparator);
588 } else if (this.treeOutline && this.treeOutline._comparator) {
Blink Reformat4c46d092018-04-07 15:32:37589 insertionIndex = this._children.lowerBound(child, this.treeOutline._comparator);
Tim van der Lippe1d6e57a2019-09-30 11:55:34590 } else {
Blink Reformat4c46d092018-04-07 15:32:37591 insertionIndex = this._children.length;
Tim van der Lippe1d6e57a2019-09-30 11:55:34592 }
Blink Reformat4c46d092018-04-07 15:32:37593 this.insertChild(child, insertionIndex);
594 }
595
596 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07597 * @param {!TreeElement} child
Blink Reformat4c46d092018-04-07 15:32:37598 * @param {number} index
599 */
600 insertChild(child, index) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34601 if (!this._children) {
Blink Reformat4c46d092018-04-07 15:32:37602 this._children = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34603 }
Blink Reformat4c46d092018-04-07 15:32:37604
Tim van der Lippe1d6e57a2019-09-30 11:55:34605 if (!child) {
Blink Reformat4c46d092018-04-07 15:32:37606 throw 'child can\'t be undefined or null';
Tim van der Lippe1d6e57a2019-09-30 11:55:34607 }
Blink Reformat4c46d092018-04-07 15:32:37608
609 console.assert(
610 !child.parent, 'Attempting to insert a child that is already in the tree, reparenting is not supported.');
611
612 const previousChild = (index > 0 ? this._children[index - 1] : null);
613 if (previousChild) {
614 previousChild.nextSibling = child;
615 child.previousSibling = previousChild;
616 } else {
617 child.previousSibling = null;
618 }
619
620 const nextChild = this._children[index];
621 if (nextChild) {
622 nextChild.previousSibling = child;
623 child.nextSibling = nextChild;
624 } else {
625 child.nextSibling = null;
626 }
627
628 this._children.splice(index, 0, child);
629
630 this.setExpandable(true);
631 child.parent = this;
632
Tim van der Lippe1d6e57a2019-09-30 11:55:34633 if (this.treeOutline) {
Blink Reformat4c46d092018-04-07 15:32:37634 this.treeOutline._bindTreeElement(child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34635 }
Blink Reformat4c46d092018-04-07 15:32:37636 for (let current = child.firstChild(); this.treeOutline && current;
Tim van der Lippe1d6e57a2019-09-30 11:55:34637 current = current.traverseNextTreeElement(false, child, true)) {
Blink Reformat4c46d092018-04-07 15:32:37638 this.treeOutline._bindTreeElement(current);
Tim van der Lippe1d6e57a2019-09-30 11:55:34639 }
Blink Reformat4c46d092018-04-07 15:32:37640 child.onattach();
641 child._ensureSelection();
Tim van der Lippe1d6e57a2019-09-30 11:55:34642 if (this.treeOutline) {
Tim van der Lippe0830b3d2019-10-03 13:20:07643 this.treeOutline.dispatchEventToListeners(Events.ElementAttached, child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34644 }
Blink Reformat4c46d092018-04-07 15:32:37645 const nextSibling = child.nextSibling ? child.nextSibling._listItemNode : null;
646 this._childrenListNode.insertBefore(child._listItemNode, nextSibling);
647 this._childrenListNode.insertBefore(child._childrenListNode, nextSibling);
Tim van der Lippe1d6e57a2019-09-30 11:55:34648 if (child.selected) {
Blink Reformat4c46d092018-04-07 15:32:37649 child.select();
Tim van der Lippe1d6e57a2019-09-30 11:55:34650 }
651 if (child.expanded) {
Blink Reformat4c46d092018-04-07 15:32:37652 child.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:34653 }
Blink Reformat4c46d092018-04-07 15:32:37654 }
655
656 /**
657 * @param {number} childIndex
658 */
659 removeChildAtIndex(childIndex) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34660 if (childIndex < 0 || childIndex >= this._children.length) {
Blink Reformat4c46d092018-04-07 15:32:37661 throw 'childIndex out of range';
Tim van der Lippe1d6e57a2019-09-30 11:55:34662 }
Blink Reformat4c46d092018-04-07 15:32:37663
664 const child = this._children[childIndex];
665 this._children.splice(childIndex, 1);
666
667 const parent = child.parent;
668 if (this.treeOutline && this.treeOutline.selectedTreeElement &&
669 this.treeOutline.selectedTreeElement.hasAncestorOrSelf(child)) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34670 if (child.nextSibling) {
Blink Reformat4c46d092018-04-07 15:32:37671 child.nextSibling.select(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34672 } else if (child.previousSibling) {
Blink Reformat4c46d092018-04-07 15:32:37673 child.previousSibling.select(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34674 } else if (parent) {
Blink Reformat4c46d092018-04-07 15:32:37675 parent.select(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34676 }
Blink Reformat4c46d092018-04-07 15:32:37677 }
678
Tim van der Lippe1d6e57a2019-09-30 11:55:34679 if (child.previousSibling) {
Blink Reformat4c46d092018-04-07 15:32:37680 child.previousSibling.nextSibling = child.nextSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:34681 }
682 if (child.nextSibling) {
Blink Reformat4c46d092018-04-07 15:32:37683 child.nextSibling.previousSibling = child.previousSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:34684 }
Blink Reformat4c46d092018-04-07 15:32:37685 child.parent = null;
686
Tim van der Lippe1d6e57a2019-09-30 11:55:34687 if (this.treeOutline) {
Blink Reformat4c46d092018-04-07 15:32:37688 this.treeOutline._unbindTreeElement(child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34689 }
Blink Reformat4c46d092018-04-07 15:32:37690 for (let current = child.firstChild(); this.treeOutline && current;
Tim van der Lippe1d6e57a2019-09-30 11:55:34691 current = current.traverseNextTreeElement(false, child, true)) {
Blink Reformat4c46d092018-04-07 15:32:37692 this.treeOutline._unbindTreeElement(current);
Tim van der Lippe1d6e57a2019-09-30 11:55:34693 }
Blink Reformat4c46d092018-04-07 15:32:37694
695 child._detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34696 if (this.treeOutline) {
Tim van der Lippe0830b3d2019-10-03 13:20:07697 this.treeOutline.dispatchEventToListeners(Events.ElementsDetached);
Tim van der Lippe1d6e57a2019-09-30 11:55:34698 }
Blink Reformat4c46d092018-04-07 15:32:37699 }
700
701 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07702 * @param {!TreeElement} child
Blink Reformat4c46d092018-04-07 15:32:37703 */
704 removeChild(child) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34705 if (!child) {
Blink Reformat4c46d092018-04-07 15:32:37706 throw 'child can\'t be undefined or null';
Tim van der Lippe1d6e57a2019-09-30 11:55:34707 }
708 if (child.parent !== this) {
Blink Reformat4c46d092018-04-07 15:32:37709 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34710 }
Blink Reformat4c46d092018-04-07 15:32:37711
712 const childIndex = this._children.indexOf(child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34713 if (childIndex === -1) {
Blink Reformat4c46d092018-04-07 15:32:37714 throw 'child not found in this node\'s children';
Tim van der Lippe1d6e57a2019-09-30 11:55:34715 }
Blink Reformat4c46d092018-04-07 15:32:37716
717 this.removeChildAtIndex(childIndex);
718 }
719
720 removeChildren() {
721 if (!this.root && this.treeOutline && this.treeOutline.selectedTreeElement &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34722 this.treeOutline.selectedTreeElement.hasAncestorOrSelf(this)) {
Blink Reformat4c46d092018-04-07 15:32:37723 this.select(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34724 }
Blink Reformat4c46d092018-04-07 15:32:37725
726 for (let i = 0; this._children && i < this._children.length; ++i) {
727 const child = this._children[i];
728 child.previousSibling = null;
729 child.nextSibling = null;
730 child.parent = null;
731
Tim van der Lippe1d6e57a2019-09-30 11:55:34732 if (this.treeOutline) {
Blink Reformat4c46d092018-04-07 15:32:37733 this.treeOutline._unbindTreeElement(child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34734 }
Blink Reformat4c46d092018-04-07 15:32:37735 for (let current = child.firstChild(); this.treeOutline && current;
Tim van der Lippe1d6e57a2019-09-30 11:55:34736 current = current.traverseNextTreeElement(false, child, true)) {
Blink Reformat4c46d092018-04-07 15:32:37737 this.treeOutline._unbindTreeElement(current);
Tim van der Lippe1d6e57a2019-09-30 11:55:34738 }
Blink Reformat4c46d092018-04-07 15:32:37739 child._detach();
740 }
741 this._children = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34742 if (this.treeOutline) {
Tim van der Lippe0830b3d2019-10-03 13:20:07743 this.treeOutline.dispatchEventToListeners(Events.ElementsDetached);
Tim van der Lippe1d6e57a2019-09-30 11:55:34744 }
Blink Reformat4c46d092018-04-07 15:32:37745 }
746
747 get selectable() {
Amanda Baker98c26ea2019-10-24 01:40:52748 if (this.isHidden()) {
Blink Reformat4c46d092018-04-07 15:32:37749 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34750 }
Blink Reformat4c46d092018-04-07 15:32:37751 return this._selectable;
752 }
753
754 set selectable(x) {
755 this._selectable = x;
756 }
757
758 get listItemElement() {
759 return this._listItemNode;
760 }
761
Blink Reformat4c46d092018-04-07 15:32:37762 get childrenListElement() {
763 return this._childrenListNode;
764 }
765
766 /**
767 * @return {string|!Node}
768 */
769 get title() {
770 return this._title;
771 }
772
773 /**
774 * @param {string|!Node} x
775 */
776 set title(x) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34777 if (this._title === x) {
Blink Reformat4c46d092018-04-07 15:32:37778 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34779 }
Blink Reformat4c46d092018-04-07 15:32:37780 this._title = x;
781
782 if (typeof x === 'string') {
Tim van der Lippeffa78622019-09-16 12:07:12783 this.titleElement.textContent = x;
Blink Reformat4c46d092018-04-07 15:32:37784 this.tooltip = x;
785 } else {
Tim van der Lippeffa78622019-09-16 12:07:12786 this.titleElement = x;
Blink Reformat4c46d092018-04-07 15:32:37787 this.tooltip = '';
788 }
789
790 this._listItemNode.removeChildren();
Tim van der Lippe1d6e57a2019-09-30 11:55:34791 if (this._leadingIconsElement) {
Blink Reformat4c46d092018-04-07 15:32:37792 this._listItemNode.appendChild(this._leadingIconsElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34793 }
Tim van der Lippeffa78622019-09-16 12:07:12794 this._listItemNode.appendChild(this.titleElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34795 if (this._trailingIconsElement) {
Blink Reformat4c46d092018-04-07 15:32:37796 this._listItemNode.appendChild(this._trailingIconsElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34797 }
Blink Reformat4c46d092018-04-07 15:32:37798 this._ensureSelection();
799 }
800
801 /**
802 * @return {string}
803 */
804 titleAsText() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34805 if (!this._title) {
Blink Reformat4c46d092018-04-07 15:32:37806 return '';
Tim van der Lippe1d6e57a2019-09-30 11:55:34807 }
808 if (typeof this._title === 'string') {
Blink Reformat4c46d092018-04-07 15:32:37809 return this._title;
Tim van der Lippe1d6e57a2019-09-30 11:55:34810 }
Blink Reformat4c46d092018-04-07 15:32:37811 return this._title.textContent;
812 }
813
814 /**
Tim van der Lippeee6e3e92020-05-13 13:01:37815 * @param {!Config<*>} editingConfig
Blink Reformat4c46d092018-04-07 15:32:37816 */
817 startEditingTitle(editingConfig) {
Paul Lewis9950e182019-12-16 16:06:07818 InplaceEditor.startEditing(/** @type {!Element} */ (this.titleElement), editingConfig);
Tim van der Lippeffa78622019-09-16 12:07:12819 this.treeOutline._shadowRoot.getSelection().selectAllChildren(this.titleElement);
Blink Reformat4c46d092018-04-07 15:32:37820 }
821
822 /**
Paul Lewis9950e182019-12-16 16:06:07823 * @param {!Array<!Icon>} icons
Blink Reformat4c46d092018-04-07 15:32:37824 */
825 setLeadingIcons(icons) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34826 if (!this._leadingIconsElement && !icons.length) {
Blink Reformat4c46d092018-04-07 15:32:37827 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34828 }
Blink Reformat4c46d092018-04-07 15:32:37829 if (!this._leadingIconsElement) {
Tim van der Lippef49e2322020-05-01 15:03:09830 this._leadingIconsElement = document.createElement('div');
831 this._leadingIconsElement.classList.add('leading-icons');
Blink Reformat4c46d092018-04-07 15:32:37832 this._leadingIconsElement.classList.add('icons-container');
Tim van der Lippeffa78622019-09-16 12:07:12833 this._listItemNode.insertBefore(this._leadingIconsElement, this.titleElement);
Blink Reformat4c46d092018-04-07 15:32:37834 this._ensureSelection();
835 }
836 this._leadingIconsElement.removeChildren();
Tim van der Lippe1d6e57a2019-09-30 11:55:34837 for (const icon of icons) {
Blink Reformat4c46d092018-04-07 15:32:37838 this._leadingIconsElement.appendChild(icon);
Tim van der Lippe1d6e57a2019-09-30 11:55:34839 }
Blink Reformat4c46d092018-04-07 15:32:37840 }
841
842 /**
Paul Lewis9950e182019-12-16 16:06:07843 * @param {!Array<!Icon>} icons
Blink Reformat4c46d092018-04-07 15:32:37844 */
845 setTrailingIcons(icons) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34846 if (!this._trailingIconsElement && !icons.length) {
Blink Reformat4c46d092018-04-07 15:32:37847 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34848 }
Blink Reformat4c46d092018-04-07 15:32:37849 if (!this._trailingIconsElement) {
Tim van der Lippef49e2322020-05-01 15:03:09850 this._trailingIconsElement = document.createElement('div');
851 this._trailingIconsElement.classList.add('trailing-icons');
Blink Reformat4c46d092018-04-07 15:32:37852 this._trailingIconsElement.classList.add('icons-container');
853 this._listItemNode.appendChild(this._trailingIconsElement);
854 this._ensureSelection();
855 }
856 this._trailingIconsElement.removeChildren();
Tim van der Lippe1d6e57a2019-09-30 11:55:34857 for (const icon of icons) {
Blink Reformat4c46d092018-04-07 15:32:37858 this._trailingIconsElement.appendChild(icon);
Tim van der Lippe1d6e57a2019-09-30 11:55:34859 }
Blink Reformat4c46d092018-04-07 15:32:37860 }
861
862
863 /**
864 * @return {string}
865 */
866 get tooltip() {
867 return this._tooltip || '';
868 }
869
870 /**
871 * @param {string} x
872 */
873 set tooltip(x) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34874 if (this._tooltip === x) {
Blink Reformat4c46d092018-04-07 15:32:37875 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34876 }
Blink Reformat4c46d092018-04-07 15:32:37877 this._tooltip = x;
878 this._listItemNode.title = x;
879 }
880
881 /**
882 * @return {boolean}
883 */
884 isExpandable() {
885 return this._expandable;
886 }
887
888 /**
889 * @param {boolean} expandable
890 */
891 setExpandable(expandable) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34892 if (this._expandable === expandable) {
Blink Reformat4c46d092018-04-07 15:32:37893 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34894 }
Blink Reformat4c46d092018-04-07 15:32:37895
896 this._expandable = expandable;
897
898 this._listItemNode.classList.toggle('parent', expandable);
899 if (!expandable) {
900 this.collapse();
Tim van der Lippeaa76aa22020-02-14 14:38:24901 ARIAUtils.unsetExpandable(this._listItemNode);
Blink Reformat4c46d092018-04-07 15:32:37902 } else {
Tim van der Lippeaa76aa22020-02-14 14:38:24903 ARIAUtils.setExpanded(this._listItemNode, false);
Blink Reformat4c46d092018-04-07 15:32:37904 }
905 }
906
907 /**
908 * @param {boolean} collapsible
909 */
910 setCollapsible(collapsible) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34911 if (this._collapsible === collapsible) {
Blink Reformat4c46d092018-04-07 15:32:37912 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34913 }
Blink Reformat4c46d092018-04-07 15:32:37914
915 this._collapsible = collapsible;
916
917 this._listItemNode.classList.toggle('always-parent', !collapsible);
Tim van der Lippe1d6e57a2019-09-30 11:55:34918 if (!collapsible) {
Blink Reformat4c46d092018-04-07 15:32:37919 this.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:34920 }
Blink Reformat4c46d092018-04-07 15:32:37921 }
922
923 get hidden() {
924 return this._hidden;
925 }
926
927 set hidden(x) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34928 if (this._hidden === x) {
Blink Reformat4c46d092018-04-07 15:32:37929 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34930 }
Blink Reformat4c46d092018-04-07 15:32:37931
932 this._hidden = x;
933
934 this._listItemNode.classList.toggle('hidden', x);
935 this._childrenListNode.classList.toggle('hidden', x);
Amanda Baker98c26ea2019-10-24 01:40:52936
937 if (x && this.treeOutline && this.treeOutline.selectedTreeElement &&
938 this.treeOutline.selectedTreeElement.hasAncestorOrSelf(this)) {
939 const hadFocus = this.treeOutline.selectedTreeElement.listItemElement.hasFocus();
940 this.treeOutline.forceSelect(!hadFocus, /* selectedByUser */ false);
941 }
Blink Reformat4c46d092018-04-07 15:32:37942 }
943
944 invalidateChildren() {
945 if (this._children) {
946 this.removeChildren();
947 this._children = null;
948 }
949 }
950
Blink Reformat4c46d092018-04-07 15:32:37951
952 _ensureSelection() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34953 if (!this.treeOutline || !this.treeOutline._renderSelection) {
Blink Reformat4c46d092018-04-07 15:32:37954 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34955 }
956 if (!this._selectionElement) {
Tim van der Lippee7f27052020-05-01 15:15:28957 this._selectionElement = document.createElement('div');
958 this._selectionElement.classList.add('selection');
959 this._selectionElement.classList.add('fill');
Tim van der Lippe1d6e57a2019-09-30 11:55:34960 }
Blink Reformat4c46d092018-04-07 15:32:37961 this._listItemNode.insertBefore(this._selectionElement, this.listItemElement.firstChild);
962 }
963
964 /**
965 * @param {!Event} event
966 */
967 _treeElementToggled(event) {
968 const element = event.currentTarget;
Tim van der Lippe1d6e57a2019-09-30 11:55:34969 if (element.treeElement !== this || element.hasSelection()) {
Blink Reformat4c46d092018-04-07 15:32:37970 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34971 }
Blink Reformat4c46d092018-04-07 15:32:37972
Erik Luod6bf97b2018-08-25 02:06:51973 console.assert(!!this.treeOutline);
974 const showSelectionOnKeyboardFocus = this.treeOutline ? this.treeOutline._showSelectionOnKeyboardFocus : false;
975 const toggleOnClick = this.toggleOnClick && (showSelectionOnKeyboardFocus || !this.selectable);
Blink Reformat4c46d092018-04-07 15:32:37976 const isInTriangle = this.isEventWithinDisclosureTriangle(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:34977 if (!toggleOnClick && !isInTriangle) {
Blink Reformat4c46d092018-04-07 15:32:37978 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34979 }
Blink Reformat4c46d092018-04-07 15:32:37980
981 if (this.expanded) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34982 if (event.altKey) {
Blink Reformat4c46d092018-04-07 15:32:37983 this.collapseRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:34984 } else {
Blink Reformat4c46d092018-04-07 15:32:37985 this.collapse();
Tim van der Lippe1d6e57a2019-09-30 11:55:34986 }
Blink Reformat4c46d092018-04-07 15:32:37987 } else {
Tim van der Lippe1d6e57a2019-09-30 11:55:34988 if (event.altKey) {
Blink Reformat4c46d092018-04-07 15:32:37989 this.expandRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:34990 } else {
Blink Reformat4c46d092018-04-07 15:32:37991 this.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:34992 }
Blink Reformat4c46d092018-04-07 15:32:37993 }
994 event.consume();
995 }
996
997 /**
998 * @param {!Event} event
999 */
1000 _handleMouseDown(event) {
1001 const element = event.currentTarget;
Tim van der Lippe1d6e57a2019-09-30 11:55:341002 if (!element) {
Blink Reformat4c46d092018-04-07 15:32:371003 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341004 }
1005 if (!this.selectable) {
Blink Reformat4c46d092018-04-07 15:32:371006 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341007 }
1008 if (element.treeElement !== this) {
Blink Reformat4c46d092018-04-07 15:32:371009 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341010 }
Blink Reformat4c46d092018-04-07 15:32:371011
Tim van der Lippe1d6e57a2019-09-30 11:55:341012 if (this.isEventWithinDisclosureTriangle(event)) {
Blink Reformat4c46d092018-04-07 15:32:371013 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341014 }
Blink Reformat4c46d092018-04-07 15:32:371015
1016 this.selectOnMouseDown(event);
1017 }
1018
1019 /**
1020 * @param {!Event} event
1021 */
1022 _handleDoubleClick(event) {
1023 const element = event.currentTarget;
Tim van der Lippe1d6e57a2019-09-30 11:55:341024 if (!element || element.treeElement !== this) {
Blink Reformat4c46d092018-04-07 15:32:371025 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341026 }
Blink Reformat4c46d092018-04-07 15:32:371027
1028 const handled = this.ondblclick(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:341029 if (handled) {
Blink Reformat4c46d092018-04-07 15:32:371030 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341031 }
1032 if (this._expandable && !this.expanded) {
Blink Reformat4c46d092018-04-07 15:32:371033 this.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:341034 }
Blink Reformat4c46d092018-04-07 15:32:371035 }
1036
1037 _detach() {
1038 this._listItemNode.remove();
1039 this._childrenListNode.remove();
1040 }
1041
1042 collapse() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341043 if (!this.expanded || !this._collapsible) {
Blink Reformat4c46d092018-04-07 15:32:371044 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341045 }
Blink Reformat4c46d092018-04-07 15:32:371046 this._listItemNode.classList.remove('expanded');
1047 this._childrenListNode.classList.remove('expanded');
Tim van der Lippeaa76aa22020-02-14 14:38:241048 ARIAUtils.setExpanded(this._listItemNode, false);
Blink Reformat4c46d092018-04-07 15:32:371049 this.expanded = false;
1050 this.oncollapse();
Tim van der Lippe1d6e57a2019-09-30 11:55:341051 if (this.treeOutline) {
Tim van der Lippe0830b3d2019-10-03 13:20:071052 this.treeOutline.dispatchEventToListeners(Events.ElementCollapsed, this);
Tim van der Lippe1d6e57a2019-09-30 11:55:341053 }
Amanda Baker98c26ea2019-10-24 01:40:521054
1055 const selectedTreeElement = this.treeOutline.selectedTreeElement;
1056 if (selectedTreeElement && selectedTreeElement.hasAncestor(this)) {
1057 this.select(/* omitFocus */ true, /* selectedByUser */ true);
1058 }
Blink Reformat4c46d092018-04-07 15:32:371059 }
1060
1061 collapseRecursively() {
1062 let item = this;
1063 while (item) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341064 if (item.expanded) {
Blink Reformat4c46d092018-04-07 15:32:371065 item.collapse();
Tim van der Lippe1d6e57a2019-09-30 11:55:341066 }
Blink Reformat4c46d092018-04-07 15:32:371067 item = item.traverseNextTreeElement(false, this, true);
1068 }
1069 }
1070
1071 collapseChildren() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341072 if (!this._children) {
Blink Reformat4c46d092018-04-07 15:32:371073 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341074 }
1075 for (const child of this._children) {
Blink Reformat4c46d092018-04-07 15:32:371076 child.collapseRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:341077 }
Blink Reformat4c46d092018-04-07 15:32:371078 }
1079
1080 expand() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341081 if (!this._expandable || (this.expanded && this._children)) {
Blink Reformat4c46d092018-04-07 15:32:371082 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341083 }
Blink Reformat4c46d092018-04-07 15:32:371084
1085 // Set this before onpopulate. Since onpopulate can add elements, this makes
1086 // sure the expanded flag is true before calling those functions. This prevents the possibility
1087 // of an infinite loop if onpopulate were to call expand.
1088
1089 this.expanded = true;
1090
1091 this._populateIfNeeded();
1092 this._listItemNode.classList.add('expanded');
1093 this._childrenListNode.classList.add('expanded');
Tim van der Lippeaa76aa22020-02-14 14:38:241094 ARIAUtils.setExpanded(this._listItemNode, true);
Blink Reformat4c46d092018-04-07 15:32:371095
1096 if (this.treeOutline) {
1097 this.onexpand();
Tim van der Lippe0830b3d2019-10-03 13:20:071098 this.treeOutline.dispatchEventToListeners(Events.ElementExpanded, this);
Blink Reformat4c46d092018-04-07 15:32:371099 }
1100 }
1101
1102 /**
1103 * @param {number=} maxDepth
Tim van der Lippeee6e3e92020-05-13 13:01:371104 * @returns {!Promise<void>}
Blink Reformat4c46d092018-04-07 15:32:371105 */
Paul Lewisbfa62952019-06-26 12:44:001106 async expandRecursively(maxDepth) {
Blink Reformat4c46d092018-04-07 15:32:371107 let item = this;
1108 const info = {};
1109 let depth = 0;
1110
1111 // The Inspector uses TreeOutlines to represents object properties, so recursive expansion
1112 // in some case can be infinite, since JavaScript objects can hold circular references.
1113 // So default to a recursion cap of 3 levels, since that gives fairly good results.
Tim van der Lippe1d6e57a2019-09-30 11:55:341114 if (isNaN(maxDepth)) {
Blink Reformat4c46d092018-04-07 15:32:371115 maxDepth = 3;
Tim van der Lippe1d6e57a2019-09-30 11:55:341116 }
Blink Reformat4c46d092018-04-07 15:32:371117
1118 while (item) {
Paul Lewisbfa62952019-06-26 12:44:001119 await item._populateIfNeeded();
1120
Tim van der Lippe1d6e57a2019-09-30 11:55:341121 if (depth < maxDepth) {
Blink Reformat4c46d092018-04-07 15:32:371122 item.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:341123 }
Paul Lewisbfa62952019-06-26 12:44:001124
Blink Reformat4c46d092018-04-07 15:32:371125 item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
1126 depth += info.depthChange;
1127 }
1128 }
1129
1130 /**
1131 * @param {boolean} altKey
1132 * @return {boolean}
1133 */
1134 collapseOrAscend(altKey) {
Erik Luo1617c3f2018-11-01 21:15:181135 if (this.expanded && this._collapsible) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341136 if (altKey) {
Blink Reformat4c46d092018-04-07 15:32:371137 this.collapseRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:341138 } else {
Blink Reformat4c46d092018-04-07 15:32:371139 this.collapse();
Tim van der Lippe1d6e57a2019-09-30 11:55:341140 }
Blink Reformat4c46d092018-04-07 15:32:371141 return true;
1142 }
1143
Tim van der Lippe1d6e57a2019-09-30 11:55:341144 if (!this.parent || this.parent.root) {
Blink Reformat4c46d092018-04-07 15:32:371145 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341146 }
Blink Reformat4c46d092018-04-07 15:32:371147
1148 if (!this.parent.selectable) {
1149 this.parent.collapse();
1150 return true;
1151 }
1152
1153 let nextSelectedElement = this.parent;
Tim van der Lippe1d6e57a2019-09-30 11:55:341154 while (nextSelectedElement && !nextSelectedElement.selectable) {
Blink Reformat4c46d092018-04-07 15:32:371155 nextSelectedElement = nextSelectedElement.parent;
Tim van der Lippe1d6e57a2019-09-30 11:55:341156 }
Blink Reformat4c46d092018-04-07 15:32:371157
Tim van der Lippe1d6e57a2019-09-30 11:55:341158 if (!nextSelectedElement) {
Blink Reformat4c46d092018-04-07 15:32:371159 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341160 }
Blink Reformat4c46d092018-04-07 15:32:371161 nextSelectedElement.select(false, true);
1162 return true;
1163 }
1164
1165 /**
1166 * @param {boolean} altKey
1167 * @return {boolean}
1168 */
1169 descendOrExpand(altKey) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341170 if (!this._expandable) {
Blink Reformat4c46d092018-04-07 15:32:371171 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341172 }
Blink Reformat4c46d092018-04-07 15:32:371173
1174 if (!this.expanded) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341175 if (altKey) {
Blink Reformat4c46d092018-04-07 15:32:371176 this.expandRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:341177 } else {
Blink Reformat4c46d092018-04-07 15:32:371178 this.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:341179 }
Blink Reformat4c46d092018-04-07 15:32:371180 return true;
1181 }
1182
1183 let nextSelectedElement = this.firstChild();
Tim van der Lippe1d6e57a2019-09-30 11:55:341184 while (nextSelectedElement && !nextSelectedElement.selectable) {
Blink Reformat4c46d092018-04-07 15:32:371185 nextSelectedElement = nextSelectedElement.nextSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:341186 }
Blink Reformat4c46d092018-04-07 15:32:371187
Tim van der Lippe1d6e57a2019-09-30 11:55:341188 if (!nextSelectedElement) {
Blink Reformat4c46d092018-04-07 15:32:371189 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341190 }
Blink Reformat4c46d092018-04-07 15:32:371191 nextSelectedElement.select(false, true);
1192 return true;
1193 }
1194
1195 /**
1196 * @param {boolean=} center
1197 */
1198 reveal(center) {
1199 let currentAncestor = this.parent;
1200 while (currentAncestor && !currentAncestor.root) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341201 if (!currentAncestor.expanded) {
Blink Reformat4c46d092018-04-07 15:32:371202 currentAncestor.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:341203 }
Blink Reformat4c46d092018-04-07 15:32:371204 currentAncestor = currentAncestor.parent;
1205 }
1206
1207 this.treeOutline._deferredScrollIntoView(this, !!center);
1208 }
1209
1210 /**
1211 * @return {boolean}
1212 */
1213 revealed() {
1214 let currentAncestor = this.parent;
1215 while (currentAncestor && !currentAncestor.root) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341216 if (!currentAncestor.expanded) {
Blink Reformat4c46d092018-04-07 15:32:371217 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341218 }
Blink Reformat4c46d092018-04-07 15:32:371219 currentAncestor = currentAncestor.parent;
1220 }
1221
1222 return true;
1223 }
1224
1225 selectOnMouseDown(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341226 if (this.select(false, true)) {
Blink Reformat4c46d092018-04-07 15:32:371227 event.consume(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:341228 }
Joel Einbinderfb3e1df2018-05-30 00:11:271229
1230 if (this._listItemNode.draggable && this._selectionElement) {
1231 const marginLeft =
1232 this.treeOutline.element.getBoundingClientRect().left - this._listItemNode.getBoundingClientRect().left;
1233 // By default the left margin extends far off screen. This is not a problem except when dragging an element.
1234 // Setting the margin once here should be fine, because we believe the left margin should never change.
1235 this._selectionElement.style.setProperty('margin-left', marginLeft + 'px');
1236 }
Blink Reformat4c46d092018-04-07 15:32:371237 }
1238
1239 /**
1240 * @param {boolean=} omitFocus
1241 * @param {boolean=} selectedByUser
1242 * @return {boolean}
1243 */
1244 select(omitFocus, selectedByUser) {
Erik Luo1617c3f2018-11-01 21:15:181245 if (!this.treeOutline || !this.selectable || this.selected) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341246 if (!omitFocus) {
Erik Luo1617c3f2018-11-01 21:15:181247 this.listItemElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:341248 }
Blink Reformat4c46d092018-04-07 15:32:371249 return false;
Erik Luo1617c3f2018-11-01 21:15:181250 }
Blink Reformat4c46d092018-04-07 15:32:371251 // Wait to deselect this element so that focus only changes once
1252 const lastSelected = this.treeOutline.selectedTreeElement;
1253 this.treeOutline.selectedTreeElement = null;
1254
1255 if (this.treeOutline._rootElement === this) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341256 if (lastSelected) {
Blink Reformat4c46d092018-04-07 15:32:371257 lastSelected.deselect();
Tim van der Lippe1d6e57a2019-09-30 11:55:341258 }
1259 if (!omitFocus) {
Erik Luo1617c3f2018-11-01 21:15:181260 this.listItemElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:341261 }
Blink Reformat4c46d092018-04-07 15:32:371262 return false;
1263 }
1264
1265 this.selected = true;
1266
1267 this.treeOutline.selectedTreeElement = this;
Amanda Baker05d6a232019-10-24 01:23:351268 this.treeOutline.updateFocusable();
Tim van der Lippe1d6e57a2019-09-30 11:55:341269 if (!omitFocus || this.treeOutline.contentElement.hasFocus()) {
Blink Reformat4c46d092018-04-07 15:32:371270 this.listItemElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:341271 }
Blink Reformat4c46d092018-04-07 15:32:371272
1273 this._listItemNode.classList.add('selected');
Tim van der Lippeaa76aa22020-02-14 14:38:241274 ARIAUtils.setSelected(this._listItemNode, true);
Tim van der Lippe0830b3d2019-10-03 13:20:071275 this.treeOutline.dispatchEventToListeners(Events.ElementSelected, this);
Tim van der Lippe1d6e57a2019-09-30 11:55:341276 if (lastSelected) {
Blink Reformat4c46d092018-04-07 15:32:371277 lastSelected.deselect();
Tim van der Lippe1d6e57a2019-09-30 11:55:341278 }
Blink Reformat4c46d092018-04-07 15:32:371279 return this.onselect(selectedByUser);
1280 }
1281
1282 /**
1283 * @param {boolean} focusable
1284 */
1285 _setFocusable(focusable) {
1286 if (focusable) {
Erik Luocc14b812018-11-03 01:33:091287 this._listItemNode.setAttribute('tabIndex', this.treeOutline && this.treeOutline._preventTabOrder ? -1 : 0);
Blink Reformat4c46d092018-04-07 15:32:371288 this._listItemNode.addEventListener('focus', this._boundOnFocus, false);
1289 this._listItemNode.addEventListener('blur', this._boundOnBlur, false);
1290 } else {
1291 this._listItemNode.removeAttribute('tabIndex');
1292 this._listItemNode.removeEventListener('focus', this._boundOnFocus, false);
1293 this._listItemNode.removeEventListener('blur', this._boundOnBlur, false);
1294 }
1295 }
1296
1297 _onFocus() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341298 if (this.treeOutline._useLightSelectionColor) {
Pavel Feldman7ad5b272019-01-08 03:01:001299 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341300 }
1301 if (!this.treeOutline.contentElement.classList.contains('hide-selection-when-blurred')) {
Erik Luod6bf97b2018-08-25 02:06:511302 this._listItemNode.classList.add('force-white-icons');
Tim van der Lippe1d6e57a2019-09-30 11:55:341303 }
Blink Reformat4c46d092018-04-07 15:32:371304 }
1305
1306 _onBlur() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341307 if (this.treeOutline._useLightSelectionColor) {
Pavel Feldman7ad5b272019-01-08 03:01:001308 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341309 }
1310 if (!this.treeOutline.contentElement.classList.contains('hide-selection-when-blurred')) {
Erik Luod6bf97b2018-08-25 02:06:511311 this._listItemNode.classList.remove('force-white-icons');
Tim van der Lippe1d6e57a2019-09-30 11:55:341312 }
Blink Reformat4c46d092018-04-07 15:32:371313 }
1314
1315 /**
1316 * @param {boolean=} omitFocus
1317 */
1318 revealAndSelect(omitFocus) {
1319 this.reveal(true);
1320 this.select(omitFocus);
1321 }
1322
1323 deselect() {
1324 const hadFocus = this._listItemNode.hasFocus();
1325 this.selected = false;
1326 this._listItemNode.classList.remove('selected');
Tim van der Lippeaa76aa22020-02-14 14:38:241327 ARIAUtils.clearSelected(this._listItemNode);
Blink Reformat4c46d092018-04-07 15:32:371328 this._setFocusable(false);
1329
1330 if (this.treeOutline && this.treeOutline.selectedTreeElement === this) {
1331 this.treeOutline.selectedTreeElement = null;
Amanda Baker05d6a232019-10-24 01:23:351332 this.treeOutline.updateFocusable();
Tim van der Lippe1d6e57a2019-09-30 11:55:341333 if (hadFocus) {
Blink Reformat4c46d092018-04-07 15:32:371334 this.treeOutline.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:341335 }
Blink Reformat4c46d092018-04-07 15:32:371336 }
1337 }
1338
Paul Lewisbfa62952019-06-26 12:44:001339 /**
Tim van der Lippeee6e3e92020-05-13 13:01:371340 * @returns {!Promise<void>}
Paul Lewisbfa62952019-06-26 12:44:001341 */
1342 async _populateIfNeeded() {
Blink Reformat4c46d092018-04-07 15:32:371343 if (this.treeOutline && this._expandable && !this._children) {
1344 this._children = [];
Paul Lewisbfa62952019-06-26 12:44:001345 await this.onpopulate();
Blink Reformat4c46d092018-04-07 15:32:371346 }
1347 }
1348
Paul Lewisbfa62952019-06-26 12:44:001349 /**
Tim van der Lippeee6e3e92020-05-13 13:01:371350 * @return {!Promise<void>}
Paul Lewisbfa62952019-06-26 12:44:001351 */
1352 async onpopulate() {
Blink Reformat4c46d092018-04-07 15:32:371353 // Overridden by subclasses.
1354 }
1355
1356 /**
1357 * @return {boolean}
1358 */
1359 onenter() {
1360 return false;
1361 }
1362
1363 /**
1364 * @return {boolean}
1365 */
1366 ondelete() {
1367 return false;
1368 }
1369
1370 /**
1371 * @return {boolean}
1372 */
1373 onspace() {
1374 return false;
1375 }
1376
1377 onbind() {
1378 }
1379
1380 onunbind() {
1381 }
1382
1383 onattach() {
1384 }
1385
1386 onexpand() {
1387 }
1388
1389 oncollapse() {
1390 }
1391
1392 /**
1393 * @param {!Event} e
1394 * @return {boolean}
1395 */
1396 ondblclick(e) {
1397 return false;
1398 }
1399
1400 /**
1401 * @param {boolean=} selectedByUser
1402 * @return {boolean}
1403 */
1404 onselect(selectedByUser) {
1405 return false;
1406 }
1407
1408 /**
1409 * @param {boolean} skipUnrevealed
Tim van der Lippe0830b3d2019-10-03 13:20:071410 * @param {?TreeElement=} stayWithin
Blink Reformat4c46d092018-04-07 15:32:371411 * @param {boolean=} dontPopulate
1412 * @param {!Object=} info
Tim van der Lippe0830b3d2019-10-03 13:20:071413 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:371414 */
1415 traverseNextTreeElement(skipUnrevealed, stayWithin, dontPopulate, info) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341416 if (!dontPopulate) {
Blink Reformat4c46d092018-04-07 15:32:371417 this._populateIfNeeded();
Tim van der Lippe1d6e57a2019-09-30 11:55:341418 }
Blink Reformat4c46d092018-04-07 15:32:371419
Tim van der Lippe1d6e57a2019-09-30 11:55:341420 if (info) {
Blink Reformat4c46d092018-04-07 15:32:371421 info.depthChange = 0;
Tim van der Lippe1d6e57a2019-09-30 11:55:341422 }
Blink Reformat4c46d092018-04-07 15:32:371423
1424 let element = skipUnrevealed ? (this.revealed() ? this.firstChild() : null) : this.firstChild();
1425 if (element && (!skipUnrevealed || (skipUnrevealed && this.expanded))) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341426 if (info) {
Blink Reformat4c46d092018-04-07 15:32:371427 info.depthChange = 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341428 }
Blink Reformat4c46d092018-04-07 15:32:371429 return element;
1430 }
1431
Tim van der Lippe1d6e57a2019-09-30 11:55:341432 if (this === stayWithin) {
Blink Reformat4c46d092018-04-07 15:32:371433 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:341434 }
Blink Reformat4c46d092018-04-07 15:32:371435
1436 element = skipUnrevealed ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:341437 if (element) {
Blink Reformat4c46d092018-04-07 15:32:371438 return element;
Tim van der Lippe1d6e57a2019-09-30 11:55:341439 }
Blink Reformat4c46d092018-04-07 15:32:371440
1441 element = this;
1442 while (element && !element.root &&
1443 !(skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) &&
1444 element.parent !== stayWithin) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341445 if (info) {
Blink Reformat4c46d092018-04-07 15:32:371446 info.depthChange -= 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341447 }
Blink Reformat4c46d092018-04-07 15:32:371448 element = element.parent;
1449 }
1450
Tim van der Lippe1d6e57a2019-09-30 11:55:341451 if (!element || element.root) {
Blink Reformat4c46d092018-04-07 15:32:371452 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:341453 }
Blink Reformat4c46d092018-04-07 15:32:371454
1455 return (skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
1456 }
1457
1458 /**
1459 * @param {boolean} skipUnrevealed
1460 * @param {boolean=} dontPopulate
Tim van der Lippe0830b3d2019-10-03 13:20:071461 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:371462 */
1463 traversePreviousTreeElement(skipUnrevealed, dontPopulate) {
1464 let element = skipUnrevealed ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:341465 if (!dontPopulate && element) {
Blink Reformat4c46d092018-04-07 15:32:371466 element._populateIfNeeded();
Tim van der Lippe1d6e57a2019-09-30 11:55:341467 }
Blink Reformat4c46d092018-04-07 15:32:371468
Tim van der Lippe0830b3d2019-10-03 13:20:071469 while (element &&
1470 (skipUnrevealed ? (element.revealed() && element.expanded ? element.lastChild() : null) :
1471 element.lastChild())) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341472 if (!dontPopulate) {
Blink Reformat4c46d092018-04-07 15:32:371473 element._populateIfNeeded();
Tim van der Lippe1d6e57a2019-09-30 11:55:341474 }
Blink Reformat4c46d092018-04-07 15:32:371475 element =
1476 (skipUnrevealed ? (element.revealed() && element.expanded ? element.lastChild() : null) :
1477 element.lastChild());
1478 }
1479
Tim van der Lippe1d6e57a2019-09-30 11:55:341480 if (element) {
Blink Reformat4c46d092018-04-07 15:32:371481 return element;
Tim van der Lippe1d6e57a2019-09-30 11:55:341482 }
Blink Reformat4c46d092018-04-07 15:32:371483
Tim van der Lippe1d6e57a2019-09-30 11:55:341484 if (!this.parent || this.parent.root) {
Blink Reformat4c46d092018-04-07 15:32:371485 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:341486 }
Blink Reformat4c46d092018-04-07 15:32:371487
1488 return this.parent;
1489 }
1490
1491 /**
1492 * @return {boolean}
1493 */
1494 isEventWithinDisclosureTriangle(event) {
1495 // FIXME: We should not use getComputedStyle(). For that we need to get rid of using ::before for disclosure triangle. (http://webk.it/74446)
1496 const paddingLeftValue = window.getComputedStyle(this._listItemNode).paddingLeft;
1497 console.assert(paddingLeftValue.endsWith('px'));
1498 const computedLeftPadding = parseFloat(paddingLeftValue);
1499 const left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
Tim van der Lippe0830b3d2019-10-03 13:20:071500 return event.pageX >= left && event.pageX <= left + TreeElement._ArrowToggleWidth && this._expandable;
Blink Reformat4c46d092018-04-07 15:32:371501 }
Tim van der Lippe0830b3d2019-10-03 13:20:071502}
Blink Reformat4c46d092018-04-07 15:32:371503
1504/** @const */
Tim van der Lippe0830b3d2019-10-03 13:20:071505TreeElement._ArrowToggleWidth = 10;
Blink Reformat4c46d092018-04-07 15:32:371506
1507(function() {
1508const img = new Image();
Joel Einbinderf6f86b62019-06-10 23:19:121509img.src = 'Images/treeoutlineTriangles.svg';
Tim van der Lippe0830b3d2019-10-03 13:20:071510TreeElement._imagePreload = img;
Blink Reformat4c46d092018-04-07 15:32:371511})();