blob: d0165d9cbc94f568d1e3b4c2f2ec17dab9bb14e0 [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
427 */
428 registerRequiredCSS(cssFile) {
Paul Lewis9950e182019-12-16 16:06:07429 appendStyle(this._shadowRoot, cssFile);
Blink Reformat4c46d092018-04-07 15:32:37430 }
431
432 hideOverflow() {
433 this._disclosureElement.classList.add('tree-outline-disclosure-hide-overflow');
434 }
435
436 makeDense() {
437 this.contentElement.classList.add('tree-outline-dense');
438 }
Tim van der Lippe0830b3d2019-10-03 13:20:07439}
Blink Reformat4c46d092018-04-07 15:32:37440
441/**
442 * @unrestricted
443 */
Tim van der Lippe0830b3d2019-10-03 13:20:07444export class TreeElement {
Blink Reformat4c46d092018-04-07 15:32:37445 /**
446 * @param {(string|!Node)=} title
447 * @param {boolean=} expandable
448 */
449 constructor(title, expandable) {
Tim van der Lippe0830b3d2019-10-03 13:20:07450 /** @type {?TreeOutline} */
Blink Reformat4c46d092018-04-07 15:32:37451 this.treeOutline = null;
452 this.parent = null;
453 this.previousSibling = null;
454 this.nextSibling = null;
455 this._boundOnFocus = this._onFocus.bind(this);
456 this._boundOnBlur = this._onBlur.bind(this);
457
Changhao Hanc1817b22020-09-07 09:04:18458 this._listItemNode = /** @type {!HTMLLIElement} */ (createElement('li'));
Tim van der Lippeffa78622019-09-16 12:07:12459 /** @protected */
460 this.titleElement = this._listItemNode.createChild('span', 'tree-element-title');
Blink Reformat4c46d092018-04-07 15:32:37461 this._listItemNode.treeElement = this;
Tim van der Lippe1d6e57a2019-09-30 11:55:34462 if (title) {
Blink Reformat4c46d092018-04-07 15:32:37463 this.title = title;
Tim van der Lippe1d6e57a2019-09-30 11:55:34464 }
Blink Reformat4c46d092018-04-07 15:32:37465 this._listItemNode.addEventListener('mousedown', this._handleMouseDown.bind(this), false);
466 this._listItemNode.addEventListener('click', this._treeElementToggled.bind(this), false);
467 this._listItemNode.addEventListener('dblclick', this._handleDoubleClick.bind(this), false);
Tim van der Lippeaa76aa22020-02-14 14:38:24468 ARIAUtils.markAsTreeitem(this._listItemNode);
Blink Reformat4c46d092018-04-07 15:32:37469
470 this._childrenListNode = createElement('ol');
471 this._childrenListNode.parentTreeElement = this;
472 this._childrenListNode.classList.add('children');
Tim van der Lippeaa76aa22020-02-14 14:38:24473 ARIAUtils.markAsGroup(this._childrenListNode);
Blink Reformat4c46d092018-04-07 15:32:37474
475 this._hidden = false;
476 this._selectable = true;
477 this.expanded = false;
478 this.selected = false;
479 this.setExpandable(expandable || false);
480 this._collapsible = true;
481 }
482
483 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07484 * @param {?TreeElement} ancestor
Blink Reformat4c46d092018-04-07 15:32:37485 * @return {boolean}
486 */
487 hasAncestor(ancestor) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34488 if (!ancestor) {
Blink Reformat4c46d092018-04-07 15:32:37489 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34490 }
Blink Reformat4c46d092018-04-07 15:32:37491
492 let currentNode = this.parent;
493 while (currentNode) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34494 if (ancestor === currentNode) {
Blink Reformat4c46d092018-04-07 15:32:37495 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34496 }
Blink Reformat4c46d092018-04-07 15:32:37497 currentNode = currentNode.parent;
498 }
499
500 return false;
501 }
502
503 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07504 * @param {?TreeElement} ancestor
Blink Reformat4c46d092018-04-07 15:32:37505 * @return {boolean}
506 */
507 hasAncestorOrSelf(ancestor) {
508 return this === ancestor || this.hasAncestor(ancestor);
509 }
510
511 /**
Amanda Baker98c26ea2019-10-24 01:40:52512 * @return {boolean}
513 */
514 isHidden() {
515 if (this.hidden) {
516 return true;
517 }
518
519 let currentNode = this.parent;
520 while (currentNode) {
521 if (currentNode.hidden) {
522 return true;
523 }
524 currentNode = currentNode.parent;
525 }
526
527 return false;
528 }
529
530 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07531 * @return {!Array.<!TreeElement>}
Blink Reformat4c46d092018-04-07 15:32:37532 */
533 children() {
534 return this._children || [];
535 }
536
537 /**
538 * @return {number}
539 */
540 childCount() {
541 return this._children ? this._children.length : 0;
542 }
543
544 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07545 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37546 */
547 firstChild() {
548 return this._children ? this._children[0] : null;
549 }
550
551 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07552 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37553 */
554 lastChild() {
555 return this._children ? this._children[this._children.length - 1] : null;
556 }
557
558 /**
559 * @param {number} index
Tim van der Lippe0830b3d2019-10-03 13:20:07560 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:37561 */
562 childAt(index) {
563 return this._children ? this._children[index] : null;
564 }
565
566 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07567 * @param {!TreeElement} child
Blink Reformat4c46d092018-04-07 15:32:37568 * @return {number}
569 */
570 indexOfChild(child) {
571 return this._children ? this._children.indexOf(child) : -1;
572 }
573
574 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07575 * @param {!TreeElement} child
Sigurd Schneiderdc2aa0d2020-07-09 10:20:09576 * @param {(function(!TreeElement, !TreeElement):number)=} comparator
Blink Reformat4c46d092018-04-07 15:32:37577 */
Sigurd Schneiderdc2aa0d2020-07-09 10:20:09578 appendChild(child, comparator) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34579 if (!this._children) {
Blink Reformat4c46d092018-04-07 15:32:37580 this._children = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34581 }
Blink Reformat4c46d092018-04-07 15:32:37582
583 let insertionIndex;
Sigurd Schneiderdc2aa0d2020-07-09 10:20:09584 if (comparator) {
585 insertionIndex = this._children.lowerBound(child, comparator);
586 } else if (this.treeOutline && this.treeOutline._comparator) {
Blink Reformat4c46d092018-04-07 15:32:37587 insertionIndex = this._children.lowerBound(child, this.treeOutline._comparator);
Tim van der Lippe1d6e57a2019-09-30 11:55:34588 } else {
Blink Reformat4c46d092018-04-07 15:32:37589 insertionIndex = this._children.length;
Tim van der Lippe1d6e57a2019-09-30 11:55:34590 }
Blink Reformat4c46d092018-04-07 15:32:37591 this.insertChild(child, insertionIndex);
592 }
593
594 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07595 * @param {!TreeElement} child
Blink Reformat4c46d092018-04-07 15:32:37596 * @param {number} index
597 */
598 insertChild(child, index) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34599 if (!this._children) {
Blink Reformat4c46d092018-04-07 15:32:37600 this._children = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34601 }
Blink Reformat4c46d092018-04-07 15:32:37602
Tim van der Lippe1d6e57a2019-09-30 11:55:34603 if (!child) {
Blink Reformat4c46d092018-04-07 15:32:37604 throw 'child can\'t be undefined or null';
Tim van der Lippe1d6e57a2019-09-30 11:55:34605 }
Blink Reformat4c46d092018-04-07 15:32:37606
607 console.assert(
608 !child.parent, 'Attempting to insert a child that is already in the tree, reparenting is not supported.');
609
610 const previousChild = (index > 0 ? this._children[index - 1] : null);
611 if (previousChild) {
612 previousChild.nextSibling = child;
613 child.previousSibling = previousChild;
614 } else {
615 child.previousSibling = null;
616 }
617
618 const nextChild = this._children[index];
619 if (nextChild) {
620 nextChild.previousSibling = child;
621 child.nextSibling = nextChild;
622 } else {
623 child.nextSibling = null;
624 }
625
626 this._children.splice(index, 0, child);
627
628 this.setExpandable(true);
629 child.parent = this;
630
Tim van der Lippe1d6e57a2019-09-30 11:55:34631 if (this.treeOutline) {
Blink Reformat4c46d092018-04-07 15:32:37632 this.treeOutline._bindTreeElement(child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34633 }
Blink Reformat4c46d092018-04-07 15:32:37634 for (let current = child.firstChild(); this.treeOutline && current;
Tim van der Lippe1d6e57a2019-09-30 11:55:34635 current = current.traverseNextTreeElement(false, child, true)) {
Blink Reformat4c46d092018-04-07 15:32:37636 this.treeOutline._bindTreeElement(current);
Tim van der Lippe1d6e57a2019-09-30 11:55:34637 }
Blink Reformat4c46d092018-04-07 15:32:37638 child.onattach();
639 child._ensureSelection();
Tim van der Lippe1d6e57a2019-09-30 11:55:34640 if (this.treeOutline) {
Tim van der Lippe0830b3d2019-10-03 13:20:07641 this.treeOutline.dispatchEventToListeners(Events.ElementAttached, child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34642 }
Blink Reformat4c46d092018-04-07 15:32:37643 const nextSibling = child.nextSibling ? child.nextSibling._listItemNode : null;
644 this._childrenListNode.insertBefore(child._listItemNode, nextSibling);
645 this._childrenListNode.insertBefore(child._childrenListNode, nextSibling);
Tim van der Lippe1d6e57a2019-09-30 11:55:34646 if (child.selected) {
Blink Reformat4c46d092018-04-07 15:32:37647 child.select();
Tim van der Lippe1d6e57a2019-09-30 11:55:34648 }
649 if (child.expanded) {
Blink Reformat4c46d092018-04-07 15:32:37650 child.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:34651 }
Blink Reformat4c46d092018-04-07 15:32:37652 }
653
654 /**
655 * @param {number} childIndex
656 */
657 removeChildAtIndex(childIndex) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34658 if (childIndex < 0 || childIndex >= this._children.length) {
Blink Reformat4c46d092018-04-07 15:32:37659 throw 'childIndex out of range';
Tim van der Lippe1d6e57a2019-09-30 11:55:34660 }
Blink Reformat4c46d092018-04-07 15:32:37661
662 const child = this._children[childIndex];
663 this._children.splice(childIndex, 1);
664
665 const parent = child.parent;
666 if (this.treeOutline && this.treeOutline.selectedTreeElement &&
667 this.treeOutline.selectedTreeElement.hasAncestorOrSelf(child)) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34668 if (child.nextSibling) {
Blink Reformat4c46d092018-04-07 15:32:37669 child.nextSibling.select(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34670 } else if (child.previousSibling) {
Blink Reformat4c46d092018-04-07 15:32:37671 child.previousSibling.select(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34672 } else if (parent) {
Blink Reformat4c46d092018-04-07 15:32:37673 parent.select(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34674 }
Blink Reformat4c46d092018-04-07 15:32:37675 }
676
Tim van der Lippe1d6e57a2019-09-30 11:55:34677 if (child.previousSibling) {
Blink Reformat4c46d092018-04-07 15:32:37678 child.previousSibling.nextSibling = child.nextSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:34679 }
680 if (child.nextSibling) {
Blink Reformat4c46d092018-04-07 15:32:37681 child.nextSibling.previousSibling = child.previousSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:34682 }
Blink Reformat4c46d092018-04-07 15:32:37683 child.parent = null;
684
Tim van der Lippe1d6e57a2019-09-30 11:55:34685 if (this.treeOutline) {
Blink Reformat4c46d092018-04-07 15:32:37686 this.treeOutline._unbindTreeElement(child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34687 }
Blink Reformat4c46d092018-04-07 15:32:37688 for (let current = child.firstChild(); this.treeOutline && current;
Tim van der Lippe1d6e57a2019-09-30 11:55:34689 current = current.traverseNextTreeElement(false, child, true)) {
Blink Reformat4c46d092018-04-07 15:32:37690 this.treeOutline._unbindTreeElement(current);
Tim van der Lippe1d6e57a2019-09-30 11:55:34691 }
Blink Reformat4c46d092018-04-07 15:32:37692
693 child._detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34694 if (this.treeOutline) {
Tim van der Lippe0830b3d2019-10-03 13:20:07695 this.treeOutline.dispatchEventToListeners(Events.ElementsDetached);
Tim van der Lippe1d6e57a2019-09-30 11:55:34696 }
Blink Reformat4c46d092018-04-07 15:32:37697 }
698
699 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07700 * @param {!TreeElement} child
Blink Reformat4c46d092018-04-07 15:32:37701 */
702 removeChild(child) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34703 if (!child) {
Blink Reformat4c46d092018-04-07 15:32:37704 throw 'child can\'t be undefined or null';
Tim van der Lippe1d6e57a2019-09-30 11:55:34705 }
706 if (child.parent !== this) {
Blink Reformat4c46d092018-04-07 15:32:37707 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34708 }
Blink Reformat4c46d092018-04-07 15:32:37709
710 const childIndex = this._children.indexOf(child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34711 if (childIndex === -1) {
Blink Reformat4c46d092018-04-07 15:32:37712 throw 'child not found in this node\'s children';
Tim van der Lippe1d6e57a2019-09-30 11:55:34713 }
Blink Reformat4c46d092018-04-07 15:32:37714
715 this.removeChildAtIndex(childIndex);
716 }
717
718 removeChildren() {
719 if (!this.root && this.treeOutline && this.treeOutline.selectedTreeElement &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34720 this.treeOutline.selectedTreeElement.hasAncestorOrSelf(this)) {
Blink Reformat4c46d092018-04-07 15:32:37721 this.select(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34722 }
Blink Reformat4c46d092018-04-07 15:32:37723
724 for (let i = 0; this._children && i < this._children.length; ++i) {
725 const child = this._children[i];
726 child.previousSibling = null;
727 child.nextSibling = null;
728 child.parent = null;
729
Tim van der Lippe1d6e57a2019-09-30 11:55:34730 if (this.treeOutline) {
Blink Reformat4c46d092018-04-07 15:32:37731 this.treeOutline._unbindTreeElement(child);
Tim van der Lippe1d6e57a2019-09-30 11:55:34732 }
Blink Reformat4c46d092018-04-07 15:32:37733 for (let current = child.firstChild(); this.treeOutline && current;
Tim van der Lippe1d6e57a2019-09-30 11:55:34734 current = current.traverseNextTreeElement(false, child, true)) {
Blink Reformat4c46d092018-04-07 15:32:37735 this.treeOutline._unbindTreeElement(current);
Tim van der Lippe1d6e57a2019-09-30 11:55:34736 }
Blink Reformat4c46d092018-04-07 15:32:37737 child._detach();
738 }
739 this._children = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34740 if (this.treeOutline) {
Tim van der Lippe0830b3d2019-10-03 13:20:07741 this.treeOutline.dispatchEventToListeners(Events.ElementsDetached);
Tim van der Lippe1d6e57a2019-09-30 11:55:34742 }
Blink Reformat4c46d092018-04-07 15:32:37743 }
744
745 get selectable() {
Amanda Baker98c26ea2019-10-24 01:40:52746 if (this.isHidden()) {
Blink Reformat4c46d092018-04-07 15:32:37747 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34748 }
Blink Reformat4c46d092018-04-07 15:32:37749 return this._selectable;
750 }
751
752 set selectable(x) {
753 this._selectable = x;
754 }
755
756 get listItemElement() {
757 return this._listItemNode;
758 }
759
Blink Reformat4c46d092018-04-07 15:32:37760 get childrenListElement() {
761 return this._childrenListNode;
762 }
763
764 /**
765 * @return {string|!Node}
766 */
767 get title() {
768 return this._title;
769 }
770
771 /**
772 * @param {string|!Node} x
773 */
774 set title(x) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34775 if (this._title === x) {
Blink Reformat4c46d092018-04-07 15:32:37776 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34777 }
Blink Reformat4c46d092018-04-07 15:32:37778 this._title = x;
779
780 if (typeof x === 'string') {
Tim van der Lippeffa78622019-09-16 12:07:12781 this.titleElement.textContent = x;
Blink Reformat4c46d092018-04-07 15:32:37782 this.tooltip = x;
783 } else {
Tim van der Lippeffa78622019-09-16 12:07:12784 this.titleElement = x;
Blink Reformat4c46d092018-04-07 15:32:37785 this.tooltip = '';
786 }
787
788 this._listItemNode.removeChildren();
Tim van der Lippe1d6e57a2019-09-30 11:55:34789 if (this._leadingIconsElement) {
Blink Reformat4c46d092018-04-07 15:32:37790 this._listItemNode.appendChild(this._leadingIconsElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34791 }
Tim van der Lippeffa78622019-09-16 12:07:12792 this._listItemNode.appendChild(this.titleElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34793 if (this._trailingIconsElement) {
Blink Reformat4c46d092018-04-07 15:32:37794 this._listItemNode.appendChild(this._trailingIconsElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34795 }
Blink Reformat4c46d092018-04-07 15:32:37796 this._ensureSelection();
797 }
798
799 /**
800 * @return {string}
801 */
802 titleAsText() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34803 if (!this._title) {
Blink Reformat4c46d092018-04-07 15:32:37804 return '';
Tim van der Lippe1d6e57a2019-09-30 11:55:34805 }
806 if (typeof this._title === 'string') {
Blink Reformat4c46d092018-04-07 15:32:37807 return this._title;
Tim van der Lippe1d6e57a2019-09-30 11:55:34808 }
Blink Reformat4c46d092018-04-07 15:32:37809 return this._title.textContent;
810 }
811
812 /**
Tim van der Lippeee6e3e92020-05-13 13:01:37813 * @param {!Config<*>} editingConfig
Blink Reformat4c46d092018-04-07 15:32:37814 */
815 startEditingTitle(editingConfig) {
Paul Lewis9950e182019-12-16 16:06:07816 InplaceEditor.startEditing(/** @type {!Element} */ (this.titleElement), editingConfig);
Tim van der Lippeffa78622019-09-16 12:07:12817 this.treeOutline._shadowRoot.getSelection().selectAllChildren(this.titleElement);
Blink Reformat4c46d092018-04-07 15:32:37818 }
819
820 /**
Paul Lewis9950e182019-12-16 16:06:07821 * @param {!Array<!Icon>} icons
Blink Reformat4c46d092018-04-07 15:32:37822 */
823 setLeadingIcons(icons) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34824 if (!this._leadingIconsElement && !icons.length) {
Blink Reformat4c46d092018-04-07 15:32:37825 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34826 }
Blink Reformat4c46d092018-04-07 15:32:37827 if (!this._leadingIconsElement) {
Tim van der Lippef49e2322020-05-01 15:03:09828 this._leadingIconsElement = document.createElement('div');
829 this._leadingIconsElement.classList.add('leading-icons');
Blink Reformat4c46d092018-04-07 15:32:37830 this._leadingIconsElement.classList.add('icons-container');
Tim van der Lippeffa78622019-09-16 12:07:12831 this._listItemNode.insertBefore(this._leadingIconsElement, this.titleElement);
Blink Reformat4c46d092018-04-07 15:32:37832 this._ensureSelection();
833 }
834 this._leadingIconsElement.removeChildren();
Tim van der Lippe1d6e57a2019-09-30 11:55:34835 for (const icon of icons) {
Blink Reformat4c46d092018-04-07 15:32:37836 this._leadingIconsElement.appendChild(icon);
Tim van der Lippe1d6e57a2019-09-30 11:55:34837 }
Blink Reformat4c46d092018-04-07 15:32:37838 }
839
840 /**
Paul Lewis9950e182019-12-16 16:06:07841 * @param {!Array<!Icon>} icons
Blink Reformat4c46d092018-04-07 15:32:37842 */
843 setTrailingIcons(icons) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34844 if (!this._trailingIconsElement && !icons.length) {
Blink Reformat4c46d092018-04-07 15:32:37845 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34846 }
Blink Reformat4c46d092018-04-07 15:32:37847 if (!this._trailingIconsElement) {
Tim van der Lippef49e2322020-05-01 15:03:09848 this._trailingIconsElement = document.createElement('div');
849 this._trailingIconsElement.classList.add('trailing-icons');
Blink Reformat4c46d092018-04-07 15:32:37850 this._trailingIconsElement.classList.add('icons-container');
851 this._listItemNode.appendChild(this._trailingIconsElement);
852 this._ensureSelection();
853 }
854 this._trailingIconsElement.removeChildren();
Tim van der Lippe1d6e57a2019-09-30 11:55:34855 for (const icon of icons) {
Blink Reformat4c46d092018-04-07 15:32:37856 this._trailingIconsElement.appendChild(icon);
Tim van der Lippe1d6e57a2019-09-30 11:55:34857 }
Blink Reformat4c46d092018-04-07 15:32:37858 }
859
860
861 /**
862 * @return {string}
863 */
864 get tooltip() {
865 return this._tooltip || '';
866 }
867
868 /**
869 * @param {string} x
870 */
871 set tooltip(x) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34872 if (this._tooltip === x) {
Blink Reformat4c46d092018-04-07 15:32:37873 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34874 }
Blink Reformat4c46d092018-04-07 15:32:37875 this._tooltip = x;
876 this._listItemNode.title = x;
877 }
878
879 /**
880 * @return {boolean}
881 */
882 isExpandable() {
883 return this._expandable;
884 }
885
886 /**
887 * @param {boolean} expandable
888 */
889 setExpandable(expandable) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34890 if (this._expandable === expandable) {
Blink Reformat4c46d092018-04-07 15:32:37891 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34892 }
Blink Reformat4c46d092018-04-07 15:32:37893
894 this._expandable = expandable;
895
896 this._listItemNode.classList.toggle('parent', expandable);
897 if (!expandable) {
898 this.collapse();
Tim van der Lippeaa76aa22020-02-14 14:38:24899 ARIAUtils.unsetExpandable(this._listItemNode);
Blink Reformat4c46d092018-04-07 15:32:37900 } else {
Tim van der Lippeaa76aa22020-02-14 14:38:24901 ARIAUtils.setExpanded(this._listItemNode, false);
Blink Reformat4c46d092018-04-07 15:32:37902 }
903 }
904
905 /**
906 * @param {boolean} collapsible
907 */
908 setCollapsible(collapsible) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34909 if (this._collapsible === collapsible) {
Blink Reformat4c46d092018-04-07 15:32:37910 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34911 }
Blink Reformat4c46d092018-04-07 15:32:37912
913 this._collapsible = collapsible;
914
915 this._listItemNode.classList.toggle('always-parent', !collapsible);
Tim van der Lippe1d6e57a2019-09-30 11:55:34916 if (!collapsible) {
Blink Reformat4c46d092018-04-07 15:32:37917 this.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:34918 }
Blink Reformat4c46d092018-04-07 15:32:37919 }
920
921 get hidden() {
922 return this._hidden;
923 }
924
925 set hidden(x) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34926 if (this._hidden === x) {
Blink Reformat4c46d092018-04-07 15:32:37927 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34928 }
Blink Reformat4c46d092018-04-07 15:32:37929
930 this._hidden = x;
931
932 this._listItemNode.classList.toggle('hidden', x);
933 this._childrenListNode.classList.toggle('hidden', x);
Amanda Baker98c26ea2019-10-24 01:40:52934
935 if (x && this.treeOutline && this.treeOutline.selectedTreeElement &&
936 this.treeOutline.selectedTreeElement.hasAncestorOrSelf(this)) {
937 const hadFocus = this.treeOutline.selectedTreeElement.listItemElement.hasFocus();
938 this.treeOutline.forceSelect(!hadFocus, /* selectedByUser */ false);
939 }
Blink Reformat4c46d092018-04-07 15:32:37940 }
941
942 invalidateChildren() {
943 if (this._children) {
944 this.removeChildren();
945 this._children = null;
946 }
947 }
948
Blink Reformat4c46d092018-04-07 15:32:37949
950 _ensureSelection() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34951 if (!this.treeOutline || !this.treeOutline._renderSelection) {
Blink Reformat4c46d092018-04-07 15:32:37952 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34953 }
954 if (!this._selectionElement) {
Tim van der Lippee7f27052020-05-01 15:15:28955 this._selectionElement = document.createElement('div');
956 this._selectionElement.classList.add('selection');
957 this._selectionElement.classList.add('fill');
Tim van der Lippe1d6e57a2019-09-30 11:55:34958 }
Blink Reformat4c46d092018-04-07 15:32:37959 this._listItemNode.insertBefore(this._selectionElement, this.listItemElement.firstChild);
960 }
961
962 /**
963 * @param {!Event} event
964 */
965 _treeElementToggled(event) {
966 const element = event.currentTarget;
Tim van der Lippe1d6e57a2019-09-30 11:55:34967 if (element.treeElement !== this || element.hasSelection()) {
Blink Reformat4c46d092018-04-07 15:32:37968 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34969 }
Blink Reformat4c46d092018-04-07 15:32:37970
Erik Luod6bf97b2018-08-25 02:06:51971 console.assert(!!this.treeOutline);
972 const showSelectionOnKeyboardFocus = this.treeOutline ? this.treeOutline._showSelectionOnKeyboardFocus : false;
973 const toggleOnClick = this.toggleOnClick && (showSelectionOnKeyboardFocus || !this.selectable);
Blink Reformat4c46d092018-04-07 15:32:37974 const isInTriangle = this.isEventWithinDisclosureTriangle(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:34975 if (!toggleOnClick && !isInTriangle) {
Blink Reformat4c46d092018-04-07 15:32:37976 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34977 }
Blink Reformat4c46d092018-04-07 15:32:37978
979 if (this.expanded) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34980 if (event.altKey) {
Blink Reformat4c46d092018-04-07 15:32:37981 this.collapseRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:34982 } else {
Blink Reformat4c46d092018-04-07 15:32:37983 this.collapse();
Tim van der Lippe1d6e57a2019-09-30 11:55:34984 }
Blink Reformat4c46d092018-04-07 15:32:37985 } else {
Tim van der Lippe1d6e57a2019-09-30 11:55:34986 if (event.altKey) {
Blink Reformat4c46d092018-04-07 15:32:37987 this.expandRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:34988 } else {
Blink Reformat4c46d092018-04-07 15:32:37989 this.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:34990 }
Blink Reformat4c46d092018-04-07 15:32:37991 }
992 event.consume();
993 }
994
995 /**
996 * @param {!Event} event
997 */
998 _handleMouseDown(event) {
999 const element = event.currentTarget;
Tim van der Lippe1d6e57a2019-09-30 11:55:341000 if (!element) {
Blink Reformat4c46d092018-04-07 15:32:371001 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341002 }
1003 if (!this.selectable) {
Blink Reformat4c46d092018-04-07 15:32:371004 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341005 }
1006 if (element.treeElement !== this) {
Blink Reformat4c46d092018-04-07 15:32:371007 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341008 }
Blink Reformat4c46d092018-04-07 15:32:371009
Tim van der Lippe1d6e57a2019-09-30 11:55:341010 if (this.isEventWithinDisclosureTriangle(event)) {
Blink Reformat4c46d092018-04-07 15:32:371011 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341012 }
Blink Reformat4c46d092018-04-07 15:32:371013
1014 this.selectOnMouseDown(event);
1015 }
1016
1017 /**
1018 * @param {!Event} event
1019 */
1020 _handleDoubleClick(event) {
1021 const element = event.currentTarget;
Tim van der Lippe1d6e57a2019-09-30 11:55:341022 if (!element || element.treeElement !== this) {
Blink Reformat4c46d092018-04-07 15:32:371023 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341024 }
Blink Reformat4c46d092018-04-07 15:32:371025
1026 const handled = this.ondblclick(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:341027 if (handled) {
Blink Reformat4c46d092018-04-07 15:32:371028 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341029 }
1030 if (this._expandable && !this.expanded) {
Blink Reformat4c46d092018-04-07 15:32:371031 this.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:341032 }
Blink Reformat4c46d092018-04-07 15:32:371033 }
1034
1035 _detach() {
1036 this._listItemNode.remove();
1037 this._childrenListNode.remove();
1038 }
1039
1040 collapse() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341041 if (!this.expanded || !this._collapsible) {
Blink Reformat4c46d092018-04-07 15:32:371042 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341043 }
Blink Reformat4c46d092018-04-07 15:32:371044 this._listItemNode.classList.remove('expanded');
1045 this._childrenListNode.classList.remove('expanded');
Tim van der Lippeaa76aa22020-02-14 14:38:241046 ARIAUtils.setExpanded(this._listItemNode, false);
Blink Reformat4c46d092018-04-07 15:32:371047 this.expanded = false;
1048 this.oncollapse();
Tim van der Lippe1d6e57a2019-09-30 11:55:341049 if (this.treeOutline) {
Tim van der Lippe0830b3d2019-10-03 13:20:071050 this.treeOutline.dispatchEventToListeners(Events.ElementCollapsed, this);
Tim van der Lippe1d6e57a2019-09-30 11:55:341051 }
Amanda Baker98c26ea2019-10-24 01:40:521052
1053 const selectedTreeElement = this.treeOutline.selectedTreeElement;
1054 if (selectedTreeElement && selectedTreeElement.hasAncestor(this)) {
1055 this.select(/* omitFocus */ true, /* selectedByUser */ true);
1056 }
Blink Reformat4c46d092018-04-07 15:32:371057 }
1058
1059 collapseRecursively() {
1060 let item = this;
1061 while (item) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341062 if (item.expanded) {
Blink Reformat4c46d092018-04-07 15:32:371063 item.collapse();
Tim van der Lippe1d6e57a2019-09-30 11:55:341064 }
Blink Reformat4c46d092018-04-07 15:32:371065 item = item.traverseNextTreeElement(false, this, true);
1066 }
1067 }
1068
1069 collapseChildren() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341070 if (!this._children) {
Blink Reformat4c46d092018-04-07 15:32:371071 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341072 }
1073 for (const child of this._children) {
Blink Reformat4c46d092018-04-07 15:32:371074 child.collapseRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:341075 }
Blink Reformat4c46d092018-04-07 15:32:371076 }
1077
1078 expand() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341079 if (!this._expandable || (this.expanded && this._children)) {
Blink Reformat4c46d092018-04-07 15:32:371080 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341081 }
Blink Reformat4c46d092018-04-07 15:32:371082
1083 // Set this before onpopulate. Since onpopulate can add elements, this makes
1084 // sure the expanded flag is true before calling those functions. This prevents the possibility
1085 // of an infinite loop if onpopulate were to call expand.
1086
1087 this.expanded = true;
1088
1089 this._populateIfNeeded();
1090 this._listItemNode.classList.add('expanded');
1091 this._childrenListNode.classList.add('expanded');
Tim van der Lippeaa76aa22020-02-14 14:38:241092 ARIAUtils.setExpanded(this._listItemNode, true);
Blink Reformat4c46d092018-04-07 15:32:371093
1094 if (this.treeOutline) {
1095 this.onexpand();
Tim van der Lippe0830b3d2019-10-03 13:20:071096 this.treeOutline.dispatchEventToListeners(Events.ElementExpanded, this);
Blink Reformat4c46d092018-04-07 15:32:371097 }
1098 }
1099
1100 /**
1101 * @param {number=} maxDepth
Tim van der Lippeee6e3e92020-05-13 13:01:371102 * @returns {!Promise<void>}
Blink Reformat4c46d092018-04-07 15:32:371103 */
Paul Lewisbfa62952019-06-26 12:44:001104 async expandRecursively(maxDepth) {
Blink Reformat4c46d092018-04-07 15:32:371105 let item = this;
1106 const info = {};
1107 let depth = 0;
1108
1109 // The Inspector uses TreeOutlines to represents object properties, so recursive expansion
1110 // in some case can be infinite, since JavaScript objects can hold circular references.
1111 // So default to a recursion cap of 3 levels, since that gives fairly good results.
Tim van der Lippe1d6e57a2019-09-30 11:55:341112 if (isNaN(maxDepth)) {
Blink Reformat4c46d092018-04-07 15:32:371113 maxDepth = 3;
Tim van der Lippe1d6e57a2019-09-30 11:55:341114 }
Blink Reformat4c46d092018-04-07 15:32:371115
1116 while (item) {
Paul Lewisbfa62952019-06-26 12:44:001117 await item._populateIfNeeded();
1118
Tim van der Lippe1d6e57a2019-09-30 11:55:341119 if (depth < maxDepth) {
Blink Reformat4c46d092018-04-07 15:32:371120 item.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:341121 }
Paul Lewisbfa62952019-06-26 12:44:001122
Blink Reformat4c46d092018-04-07 15:32:371123 item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
1124 depth += info.depthChange;
1125 }
1126 }
1127
1128 /**
1129 * @param {boolean} altKey
1130 * @return {boolean}
1131 */
1132 collapseOrAscend(altKey) {
Erik Luo1617c3f2018-11-01 21:15:181133 if (this.expanded && this._collapsible) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341134 if (altKey) {
Blink Reformat4c46d092018-04-07 15:32:371135 this.collapseRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:341136 } else {
Blink Reformat4c46d092018-04-07 15:32:371137 this.collapse();
Tim van der Lippe1d6e57a2019-09-30 11:55:341138 }
Blink Reformat4c46d092018-04-07 15:32:371139 return true;
1140 }
1141
Tim van der Lippe1d6e57a2019-09-30 11:55:341142 if (!this.parent || this.parent.root) {
Blink Reformat4c46d092018-04-07 15:32:371143 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341144 }
Blink Reformat4c46d092018-04-07 15:32:371145
1146 if (!this.parent.selectable) {
1147 this.parent.collapse();
1148 return true;
1149 }
1150
1151 let nextSelectedElement = this.parent;
Tim van der Lippe1d6e57a2019-09-30 11:55:341152 while (nextSelectedElement && !nextSelectedElement.selectable) {
Blink Reformat4c46d092018-04-07 15:32:371153 nextSelectedElement = nextSelectedElement.parent;
Tim van der Lippe1d6e57a2019-09-30 11:55:341154 }
Blink Reformat4c46d092018-04-07 15:32:371155
Tim van der Lippe1d6e57a2019-09-30 11:55:341156 if (!nextSelectedElement) {
Blink Reformat4c46d092018-04-07 15:32:371157 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341158 }
Blink Reformat4c46d092018-04-07 15:32:371159 nextSelectedElement.select(false, true);
1160 return true;
1161 }
1162
1163 /**
1164 * @param {boolean} altKey
1165 * @return {boolean}
1166 */
1167 descendOrExpand(altKey) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341168 if (!this._expandable) {
Blink Reformat4c46d092018-04-07 15:32:371169 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341170 }
Blink Reformat4c46d092018-04-07 15:32:371171
1172 if (!this.expanded) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341173 if (altKey) {
Blink Reformat4c46d092018-04-07 15:32:371174 this.expandRecursively();
Tim van der Lippe1d6e57a2019-09-30 11:55:341175 } else {
Blink Reformat4c46d092018-04-07 15:32:371176 this.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:341177 }
Blink Reformat4c46d092018-04-07 15:32:371178 return true;
1179 }
1180
1181 let nextSelectedElement = this.firstChild();
Tim van der Lippe1d6e57a2019-09-30 11:55:341182 while (nextSelectedElement && !nextSelectedElement.selectable) {
Blink Reformat4c46d092018-04-07 15:32:371183 nextSelectedElement = nextSelectedElement.nextSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:341184 }
Blink Reformat4c46d092018-04-07 15:32:371185
Tim van der Lippe1d6e57a2019-09-30 11:55:341186 if (!nextSelectedElement) {
Blink Reformat4c46d092018-04-07 15:32:371187 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341188 }
Blink Reformat4c46d092018-04-07 15:32:371189 nextSelectedElement.select(false, true);
1190 return true;
1191 }
1192
1193 /**
1194 * @param {boolean=} center
1195 */
1196 reveal(center) {
1197 let currentAncestor = this.parent;
1198 while (currentAncestor && !currentAncestor.root) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341199 if (!currentAncestor.expanded) {
Blink Reformat4c46d092018-04-07 15:32:371200 currentAncestor.expand();
Tim van der Lippe1d6e57a2019-09-30 11:55:341201 }
Blink Reformat4c46d092018-04-07 15:32:371202 currentAncestor = currentAncestor.parent;
1203 }
1204
1205 this.treeOutline._deferredScrollIntoView(this, !!center);
1206 }
1207
1208 /**
1209 * @return {boolean}
1210 */
1211 revealed() {
1212 let currentAncestor = this.parent;
1213 while (currentAncestor && !currentAncestor.root) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341214 if (!currentAncestor.expanded) {
Blink Reformat4c46d092018-04-07 15:32:371215 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341216 }
Blink Reformat4c46d092018-04-07 15:32:371217 currentAncestor = currentAncestor.parent;
1218 }
1219
1220 return true;
1221 }
1222
1223 selectOnMouseDown(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341224 if (this.select(false, true)) {
Blink Reformat4c46d092018-04-07 15:32:371225 event.consume(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:341226 }
Joel Einbinderfb3e1df2018-05-30 00:11:271227
1228 if (this._listItemNode.draggable && this._selectionElement) {
1229 const marginLeft =
1230 this.treeOutline.element.getBoundingClientRect().left - this._listItemNode.getBoundingClientRect().left;
1231 // By default the left margin extends far off screen. This is not a problem except when dragging an element.
1232 // Setting the margin once here should be fine, because we believe the left margin should never change.
1233 this._selectionElement.style.setProperty('margin-left', marginLeft + 'px');
1234 }
Blink Reformat4c46d092018-04-07 15:32:371235 }
1236
1237 /**
1238 * @param {boolean=} omitFocus
1239 * @param {boolean=} selectedByUser
1240 * @return {boolean}
1241 */
1242 select(omitFocus, selectedByUser) {
Erik Luo1617c3f2018-11-01 21:15:181243 if (!this.treeOutline || !this.selectable || this.selected) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341244 if (!omitFocus) {
Erik Luo1617c3f2018-11-01 21:15:181245 this.listItemElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:341246 }
Blink Reformat4c46d092018-04-07 15:32:371247 return false;
Erik Luo1617c3f2018-11-01 21:15:181248 }
Blink Reformat4c46d092018-04-07 15:32:371249 // Wait to deselect this element so that focus only changes once
1250 const lastSelected = this.treeOutline.selectedTreeElement;
1251 this.treeOutline.selectedTreeElement = null;
1252
1253 if (this.treeOutline._rootElement === this) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341254 if (lastSelected) {
Blink Reformat4c46d092018-04-07 15:32:371255 lastSelected.deselect();
Tim van der Lippe1d6e57a2019-09-30 11:55:341256 }
1257 if (!omitFocus) {
Erik Luo1617c3f2018-11-01 21:15:181258 this.listItemElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:341259 }
Blink Reformat4c46d092018-04-07 15:32:371260 return false;
1261 }
1262
1263 this.selected = true;
1264
1265 this.treeOutline.selectedTreeElement = this;
Amanda Baker05d6a232019-10-24 01:23:351266 this.treeOutline.updateFocusable();
Tim van der Lippe1d6e57a2019-09-30 11:55:341267 if (!omitFocus || this.treeOutline.contentElement.hasFocus()) {
Blink Reformat4c46d092018-04-07 15:32:371268 this.listItemElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:341269 }
Blink Reformat4c46d092018-04-07 15:32:371270
1271 this._listItemNode.classList.add('selected');
Tim van der Lippeaa76aa22020-02-14 14:38:241272 ARIAUtils.setSelected(this._listItemNode, true);
Tim van der Lippe0830b3d2019-10-03 13:20:071273 this.treeOutline.dispatchEventToListeners(Events.ElementSelected, this);
Tim van der Lippe1d6e57a2019-09-30 11:55:341274 if (lastSelected) {
Blink Reformat4c46d092018-04-07 15:32:371275 lastSelected.deselect();
Tim van der Lippe1d6e57a2019-09-30 11:55:341276 }
Blink Reformat4c46d092018-04-07 15:32:371277 return this.onselect(selectedByUser);
1278 }
1279
1280 /**
1281 * @param {boolean} focusable
1282 */
1283 _setFocusable(focusable) {
1284 if (focusable) {
Erik Luocc14b812018-11-03 01:33:091285 this._listItemNode.setAttribute('tabIndex', this.treeOutline && this.treeOutline._preventTabOrder ? -1 : 0);
Blink Reformat4c46d092018-04-07 15:32:371286 this._listItemNode.addEventListener('focus', this._boundOnFocus, false);
1287 this._listItemNode.addEventListener('blur', this._boundOnBlur, false);
1288 } else {
1289 this._listItemNode.removeAttribute('tabIndex');
1290 this._listItemNode.removeEventListener('focus', this._boundOnFocus, false);
1291 this._listItemNode.removeEventListener('blur', this._boundOnBlur, false);
1292 }
1293 }
1294
1295 _onFocus() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341296 if (this.treeOutline._useLightSelectionColor) {
Pavel Feldman7ad5b272019-01-08 03:01:001297 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341298 }
1299 if (!this.treeOutline.contentElement.classList.contains('hide-selection-when-blurred')) {
Erik Luod6bf97b2018-08-25 02:06:511300 this._listItemNode.classList.add('force-white-icons');
Tim van der Lippe1d6e57a2019-09-30 11:55:341301 }
Blink Reformat4c46d092018-04-07 15:32:371302 }
1303
1304 _onBlur() {
Tim van der Lippe1d6e57a2019-09-30 11:55:341305 if (this.treeOutline._useLightSelectionColor) {
Pavel Feldman7ad5b272019-01-08 03:01:001306 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341307 }
1308 if (!this.treeOutline.contentElement.classList.contains('hide-selection-when-blurred')) {
Erik Luod6bf97b2018-08-25 02:06:511309 this._listItemNode.classList.remove('force-white-icons');
Tim van der Lippe1d6e57a2019-09-30 11:55:341310 }
Blink Reformat4c46d092018-04-07 15:32:371311 }
1312
1313 /**
1314 * @param {boolean=} omitFocus
1315 */
1316 revealAndSelect(omitFocus) {
1317 this.reveal(true);
1318 this.select(omitFocus);
1319 }
1320
1321 deselect() {
1322 const hadFocus = this._listItemNode.hasFocus();
1323 this.selected = false;
1324 this._listItemNode.classList.remove('selected');
Tim van der Lippeaa76aa22020-02-14 14:38:241325 ARIAUtils.clearSelected(this._listItemNode);
Blink Reformat4c46d092018-04-07 15:32:371326 this._setFocusable(false);
1327
1328 if (this.treeOutline && this.treeOutline.selectedTreeElement === this) {
1329 this.treeOutline.selectedTreeElement = null;
Amanda Baker05d6a232019-10-24 01:23:351330 this.treeOutline.updateFocusable();
Tim van der Lippe1d6e57a2019-09-30 11:55:341331 if (hadFocus) {
Blink Reformat4c46d092018-04-07 15:32:371332 this.treeOutline.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:341333 }
Blink Reformat4c46d092018-04-07 15:32:371334 }
1335 }
1336
Paul Lewisbfa62952019-06-26 12:44:001337 /**
Tim van der Lippeee6e3e92020-05-13 13:01:371338 * @returns {!Promise<void>}
Paul Lewisbfa62952019-06-26 12:44:001339 */
1340 async _populateIfNeeded() {
Blink Reformat4c46d092018-04-07 15:32:371341 if (this.treeOutline && this._expandable && !this._children) {
1342 this._children = [];
Paul Lewisbfa62952019-06-26 12:44:001343 await this.onpopulate();
Blink Reformat4c46d092018-04-07 15:32:371344 }
1345 }
1346
Paul Lewisbfa62952019-06-26 12:44:001347 /**
Tim van der Lippeee6e3e92020-05-13 13:01:371348 * @return {!Promise<void>}
Paul Lewisbfa62952019-06-26 12:44:001349 */
1350 async onpopulate() {
Blink Reformat4c46d092018-04-07 15:32:371351 // Overridden by subclasses.
1352 }
1353
1354 /**
1355 * @return {boolean}
1356 */
1357 onenter() {
1358 return false;
1359 }
1360
1361 /**
1362 * @return {boolean}
1363 */
1364 ondelete() {
1365 return false;
1366 }
1367
1368 /**
1369 * @return {boolean}
1370 */
1371 onspace() {
1372 return false;
1373 }
1374
1375 onbind() {
1376 }
1377
1378 onunbind() {
1379 }
1380
1381 onattach() {
1382 }
1383
1384 onexpand() {
1385 }
1386
1387 oncollapse() {
1388 }
1389
1390 /**
1391 * @param {!Event} e
1392 * @return {boolean}
1393 */
1394 ondblclick(e) {
1395 return false;
1396 }
1397
1398 /**
1399 * @param {boolean=} selectedByUser
1400 * @return {boolean}
1401 */
1402 onselect(selectedByUser) {
1403 return false;
1404 }
1405
1406 /**
1407 * @param {boolean} skipUnrevealed
Tim van der Lippe0830b3d2019-10-03 13:20:071408 * @param {?TreeElement=} stayWithin
Blink Reformat4c46d092018-04-07 15:32:371409 * @param {boolean=} dontPopulate
1410 * @param {!Object=} info
Tim van der Lippe0830b3d2019-10-03 13:20:071411 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:371412 */
1413 traverseNextTreeElement(skipUnrevealed, stayWithin, dontPopulate, info) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341414 if (!dontPopulate) {
Blink Reformat4c46d092018-04-07 15:32:371415 this._populateIfNeeded();
Tim van der Lippe1d6e57a2019-09-30 11:55:341416 }
Blink Reformat4c46d092018-04-07 15:32:371417
Tim van der Lippe1d6e57a2019-09-30 11:55:341418 if (info) {
Blink Reformat4c46d092018-04-07 15:32:371419 info.depthChange = 0;
Tim van der Lippe1d6e57a2019-09-30 11:55:341420 }
Blink Reformat4c46d092018-04-07 15:32:371421
1422 let element = skipUnrevealed ? (this.revealed() ? this.firstChild() : null) : this.firstChild();
1423 if (element && (!skipUnrevealed || (skipUnrevealed && this.expanded))) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341424 if (info) {
Blink Reformat4c46d092018-04-07 15:32:371425 info.depthChange = 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341426 }
Blink Reformat4c46d092018-04-07 15:32:371427 return element;
1428 }
1429
Tim van der Lippe1d6e57a2019-09-30 11:55:341430 if (this === stayWithin) {
Blink Reformat4c46d092018-04-07 15:32:371431 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:341432 }
Blink Reformat4c46d092018-04-07 15:32:371433
1434 element = skipUnrevealed ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:341435 if (element) {
Blink Reformat4c46d092018-04-07 15:32:371436 return element;
Tim van der Lippe1d6e57a2019-09-30 11:55:341437 }
Blink Reformat4c46d092018-04-07 15:32:371438
1439 element = this;
1440 while (element && !element.root &&
1441 !(skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) &&
1442 element.parent !== stayWithin) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341443 if (info) {
Blink Reformat4c46d092018-04-07 15:32:371444 info.depthChange -= 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341445 }
Blink Reformat4c46d092018-04-07 15:32:371446 element = element.parent;
1447 }
1448
Tim van der Lippe1d6e57a2019-09-30 11:55:341449 if (!element || element.root) {
Blink Reformat4c46d092018-04-07 15:32:371450 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:341451 }
Blink Reformat4c46d092018-04-07 15:32:371452
1453 return (skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
1454 }
1455
1456 /**
1457 * @param {boolean} skipUnrevealed
1458 * @param {boolean=} dontPopulate
Tim van der Lippe0830b3d2019-10-03 13:20:071459 * @return {?TreeElement}
Blink Reformat4c46d092018-04-07 15:32:371460 */
1461 traversePreviousTreeElement(skipUnrevealed, dontPopulate) {
1462 let element = skipUnrevealed ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
Tim van der Lippe1d6e57a2019-09-30 11:55:341463 if (!dontPopulate && element) {
Blink Reformat4c46d092018-04-07 15:32:371464 element._populateIfNeeded();
Tim van der Lippe1d6e57a2019-09-30 11:55:341465 }
Blink Reformat4c46d092018-04-07 15:32:371466
Tim van der Lippe0830b3d2019-10-03 13:20:071467 while (element &&
1468 (skipUnrevealed ? (element.revealed() && element.expanded ? element.lastChild() : null) :
1469 element.lastChild())) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341470 if (!dontPopulate) {
Blink Reformat4c46d092018-04-07 15:32:371471 element._populateIfNeeded();
Tim van der Lippe1d6e57a2019-09-30 11:55:341472 }
Blink Reformat4c46d092018-04-07 15:32:371473 element =
1474 (skipUnrevealed ? (element.revealed() && element.expanded ? element.lastChild() : null) :
1475 element.lastChild());
1476 }
1477
Tim van der Lippe1d6e57a2019-09-30 11:55:341478 if (element) {
Blink Reformat4c46d092018-04-07 15:32:371479 return element;
Tim van der Lippe1d6e57a2019-09-30 11:55:341480 }
Blink Reformat4c46d092018-04-07 15:32:371481
Tim van der Lippe1d6e57a2019-09-30 11:55:341482 if (!this.parent || this.parent.root) {
Blink Reformat4c46d092018-04-07 15:32:371483 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:341484 }
Blink Reformat4c46d092018-04-07 15:32:371485
1486 return this.parent;
1487 }
1488
1489 /**
1490 * @return {boolean}
1491 */
1492 isEventWithinDisclosureTriangle(event) {
1493 // FIXME: We should not use getComputedStyle(). For that we need to get rid of using ::before for disclosure triangle. (http://webk.it/74446)
1494 const paddingLeftValue = window.getComputedStyle(this._listItemNode).paddingLeft;
1495 console.assert(paddingLeftValue.endsWith('px'));
1496 const computedLeftPadding = parseFloat(paddingLeftValue);
1497 const left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
Tim van der Lippe0830b3d2019-10-03 13:20:071498 return event.pageX >= left && event.pageX <= left + TreeElement._ArrowToggleWidth && this._expandable;
Blink Reformat4c46d092018-04-07 15:32:371499 }
Tim van der Lippe0830b3d2019-10-03 13:20:071500}
Blink Reformat4c46d092018-04-07 15:32:371501
1502/** @const */
Tim van der Lippe0830b3d2019-10-03 13:20:071503TreeElement._ArrowToggleWidth = 10;
Blink Reformat4c46d092018-04-07 15:32:371504
1505(function() {
1506const img = new Image();
Joel Einbinderf6f86b62019-06-10 23:19:121507img.src = 'Images/treeoutlineTriangles.svg';
Tim van der Lippe0830b3d2019-10-03 13:20:071508TreeElement._imagePreload = img;
Blink Reformat4c46d092018-04-07 15:32:371509})();