OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2007 Apple Inc. All rights reserved. | 2 * Copyright (C) 2007 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 25 matching lines...) Expand all Loading... |
36 this._createRootElement(); | 36 this._createRootElement(); |
37 | 37 |
38 this.selectedTreeElement = null; | 38 this.selectedTreeElement = null; |
39 this.expandTreeElementsWhenArrowing = false; | 39 this.expandTreeElementsWhenArrowing = false; |
40 /** @type {?function(!TreeElement, !TreeElement):number} */ | 40 /** @type {?function(!TreeElement, !TreeElement):number} */ |
41 this._comparator = null; | 41 this._comparator = null; |
42 | 42 |
43 this._contentElement = this._rootElement._childrenListNode; | 43 this._contentElement = this._rootElement._childrenListNode; |
44 this._contentElement.addEventListener("keydown", this._treeKeyDown.bind(this
), true); | 44 this._contentElement.addEventListener("keydown", this._treeKeyDown.bind(this
), true); |
45 | 45 |
| 46 this.element = this._contentElement; |
| 47 |
46 this.setFocusable(!nonFocusable); | 48 this.setFocusable(!nonFocusable); |
47 | 49 |
48 this.element = this._contentElement; | 50 this.element.addEventListener("keypress", this._handleKeyPressForHighlightin
g.bind(this), true); |
| 51 this.element.addEventListener("blur", this._clearFilter.bind(this), true); |
| 52 this.element.addEventListener("click", this._clearFilter.bind(this), true); |
| 53 |
| 54 this._currentSelectionFilterString = ""; |
| 55 this._interactiveFilterEnabled = false; |
| 56 /** @type {!Array.<!TreeElement>} */ |
| 57 this._highlightedNodes = [] |
49 } | 58 } |
50 | 59 |
51 TreeOutline.Events = { | 60 TreeOutline.Events = { |
52 ElementAttached: "ElementAttached", | 61 ElementAttached: "ElementAttached", |
53 ElementExpanded: "ElementExpanded", | 62 ElementExpanded: "ElementExpanded", |
54 ElementCollapsed: "ElementCollapsed", | 63 ElementCollapsed: "ElementCollapsed", |
55 ElementSelected: "ElementSelected" | 64 ElementSelected: "ElementSelected" |
56 } | 65 } |
57 | 66 |
58 TreeOutline.prototype = { | 67 TreeOutline.prototype = { |
59 _createRootElement: function() | 68 _createRootElement: function() |
60 { | 69 { |
61 this._rootElement = new TreeElement(); | 70 this._rootElement = new TreeElement(); |
62 this._rootElement.treeOutline = this; | 71 this._rootElement.treeOutline = this; |
63 this._rootElement.root = true; | 72 this._rootElement.root = true; |
64 this._rootElement.selectable = false; | 73 this._rootElement.selectable = false; |
65 this._rootElement.expanded = true; | 74 this._rootElement.expanded = true; |
66 this._rootElement._childrenListNode.classList.remove("children"); | 75 this._rootElement._childrenListNode.classList.remove("children"); |
67 }, | 76 }, |
68 | 77 |
69 /** | 78 /** |
70 * @return {!TreeElement} | 79 * @return {!TreeElement} |
71 */ | 80 */ |
72 rootElement: function() | 81 rootElement: function() |
73 { | 82 { |
74 return this._rootElement; | 83 return this._rootElement; |
75 }, | 84 }, |
76 | 85 |
77 /** | 86 /** |
| 87 * @param {boolean} enable |
| 88 */ |
| 89 setInteractiveFilterable: function(enable) |
| 90 { |
| 91 if (enable === this._interactiveFilterEnabled) |
| 92 return; |
| 93 if (!enable) |
| 94 this._setCurrentSelectionFilterString(""); |
| 95 this._interactiveFilterEnabled = enable; |
| 96 }, |
| 97 |
| 98 /** |
| 99 * @param {string} filterString |
| 100 */ |
| 101 _setCurrentSelectionFilterString: function(filterString) |
| 102 { |
| 103 this._currentSelectionFilterString = filterString; |
| 104 this._refreshHighlighting(); |
| 105 }, |
| 106 |
| 107 /** |
| 108 * @param {string} filterString |
| 109 * @return {!RegExp} |
| 110 */ |
| 111 _makeFilterRegexFromString: function(filterString) |
| 112 { |
| 113 return new RegExp(filterString.escapeForRegExp(), "gi") |
| 114 }, |
| 115 |
| 116 _refreshHighlighting: function() |
| 117 { |
| 118 if (!this._rootElement) |
| 119 return; |
| 120 |
| 121 for (var changedNode of this._highlightedNodes) |
| 122 changedNode._revertHighlightChanges(); |
| 123 |
| 124 this._highlightedNodes = []; |
| 125 |
| 126 if (!this._currentSelectionFilterString) |
| 127 return; |
| 128 |
| 129 if (this.selectedTreeElement && !this.selectedTreeElement.selectable) { |
| 130 if (!this.selectNext()) |
| 131 this.selectPrevious(); |
| 132 } |
| 133 |
| 134 var filterRegex = this._makeFilterRegexFromString(this._currentSelection
FilterString); |
| 135 var node = this._rootElement.firstChild(); |
| 136 while (node) { |
| 137 if (node._applyHighlightFilter(filterRegex)) |
| 138 this._highlightedNodes.push(node); |
| 139 node = node.traverseNextTreeElement(true, null, true); |
| 140 } |
| 141 }, |
| 142 |
| 143 _clearFilter: function() |
| 144 { |
| 145 if (this._interactiveFilterEnabled) |
| 146 this._setCurrentSelectionFilterString(""); |
| 147 }, |
| 148 |
| 149 /** |
| 150 * @param {!Event} event |
| 151 */ |
| 152 _handleKeyPressForHighlighting: function(event) |
| 153 { |
| 154 if (!this._interactiveFilterEnabled) |
| 155 return; |
| 156 |
| 157 if (event.target !== this._contentElement) |
| 158 return; |
| 159 |
| 160 if (!this.selectedTreeElement || event.shiftKey || event.metaKey || even
t.ctrlKey) |
| 161 return; |
| 162 |
| 163 var currentFilterString = this._currentSelectionFilterString; |
| 164 var key = event.data; |
| 165 if (key !== "\r" && key !== "\n" && (key !== " " || currentFilterString)
) |
| 166 this._setCurrentSelectionFilterString(currentFilterString + event.da
ta); |
| 167 }, |
| 168 |
| 169 /** |
78 * @return {?TreeElement} | 170 * @return {?TreeElement} |
79 */ | 171 */ |
80 firstChild: function() | 172 firstChild: function() |
81 { | 173 { |
82 return this._rootElement.firstChild(); | 174 return this._rootElement.firstChild(); |
83 }, | 175 }, |
84 | 176 |
85 /** | 177 /** |
86 * @param {!TreeElement} child | 178 * @param {!TreeElement} child |
87 */ | 179 */ |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 { | 237 { |
146 this._comparator = comparator; | 238 this._comparator = comparator; |
147 }, | 239 }, |
148 | 240 |
149 /** | 241 /** |
150 * @param {boolean} focusable | 242 * @param {boolean} focusable |
151 */ | 243 */ |
152 setFocusable: function(focusable) | 244 setFocusable: function(focusable) |
153 { | 245 { |
154 if (focusable) | 246 if (focusable) |
155 this._contentElement.setAttribute("tabIndex", 0); | 247 this.element.setAttribute("tabIndex", 0); |
156 else | 248 else |
157 this._contentElement.removeAttribute("tabIndex"); | 249 this.element.removeAttribute("tabIndex"); |
158 }, | 250 }, |
159 | 251 |
160 focus: function() | 252 focus: function() |
161 { | 253 { |
162 this._contentElement.focus(); | 254 this._contentElement.focus(); |
163 }, | 255 }, |
164 | 256 |
165 /** | 257 /** |
166 * @param {!TreeElement} element | 258 * @param {!TreeElement} element |
167 */ | 259 */ |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
222 * @param {!Event} event | 314 * @param {!Event} event |
223 */ | 315 */ |
224 _treeKeyDown: function(event) | 316 _treeKeyDown: function(event) |
225 { | 317 { |
226 if (event.target !== this._contentElement) | 318 if (event.target !== this._contentElement) |
227 return; | 319 return; |
228 | 320 |
229 if (!this.selectedTreeElement || event.shiftKey || event.metaKey || even
t.ctrlKey) | 321 if (!this.selectedTreeElement || event.shiftKey || event.metaKey || even
t.ctrlKey) |
230 return; | 322 return; |
231 | 323 |
| 324 var currentFilterString = this._currentSelectionFilterString; |
232 var handled = false; | 325 var handled = false; |
| 326 var key = event.keyCode; |
233 var nextSelectedElement; | 327 var nextSelectedElement; |
234 if (event.keyIdentifier === "Up" && !event.altKey) { | 328 |
235 handled = this.selectPrevious(); | 329 switch (key) { |
236 } else if (event.keyIdentifier === "Down" && !event.altKey) { | 330 case WebInspector.KeyboardShortcut.Keys.Up.code: |
237 handled = this.selectNext(); | 331 if (!event.altKey) |
238 } else if (event.keyIdentifier === "Left") { | 332 handled = this.selectPrevious(); |
| 333 break; |
| 334 case WebInspector.KeyboardShortcut.Keys.Down.code: |
| 335 if (!event.altKey) |
| 336 handled = this.selectNext(); |
| 337 break; |
| 338 case WebInspector.KeyboardShortcut.Keys.Left.code: |
| 339 if (this._interactiveFilterEnabled) |
| 340 this._clearFilter(); |
| 341 |
239 if (this.selectedTreeElement.expanded) { | 342 if (this.selectedTreeElement.expanded) { |
240 if (event.altKey) | 343 if (event.altKey) |
241 this.selectedTreeElement.collapseRecursively(); | 344 this.selectedTreeElement.collapseRecursively(); |
242 else | 345 else |
243 this.selectedTreeElement.collapse(); | 346 this.selectedTreeElement.collapse(); |
244 handled = true; | 347 handled = true; |
245 } else if (this.selectedTreeElement.parent && !this.selectedTreeElem
ent.parent.root) { | 348 } else if (this.selectedTreeElement.parent && !this.selectedTreeElem
ent.parent.root) { |
246 handled = true; | 349 handled = true; |
247 if (this.selectedTreeElement.parent.selectable) { | 350 if (this.selectedTreeElement.parent.selectable) { |
248 nextSelectedElement = this.selectedTreeElement.parent; | 351 nextSelectedElement = this.selectedTreeElement.parent; |
249 while (nextSelectedElement && !nextSelectedElement.selectabl
e) | 352 while (nextSelectedElement && !nextSelectedElement.selectabl
e) |
250 nextSelectedElement = nextSelectedElement.parent; | 353 nextSelectedElement = nextSelectedElement.parent; |
251 handled = nextSelectedElement ? true : false; | 354 handled = nextSelectedElement ? true : false; |
252 } else if (this.selectedTreeElement.parent) | 355 } else if (this.selectedTreeElement.parent) { |
253 this.selectedTreeElement.parent.collapse(); | 356 this.selectedTreeElement.parent.collapse(); |
| 357 } |
254 } | 358 } |
255 } else if (event.keyIdentifier === "Right") { | 359 break; |
| 360 case WebInspector.KeyboardShortcut.Keys.Right.code: |
| 361 if (this._interactiveFilterEnabled) |
| 362 this._clearFilter(); |
| 363 |
256 if (!this.selectedTreeElement.revealed()) { | 364 if (!this.selectedTreeElement.revealed()) { |
257 this.selectedTreeElement.reveal(); | 365 this.selectedTreeElement.reveal(); |
258 handled = true; | 366 handled = true; |
259 } else if (this.selectedTreeElement._expandable) { | 367 } else if (this.selectedTreeElement._expandable) { |
260 handled = true; | 368 handled = true; |
261 if (this.selectedTreeElement.expanded) { | 369 if (this.selectedTreeElement.expanded) { |
262 nextSelectedElement = this.selectedTreeElement.firstChild(); | 370 nextSelectedElement = this.selectedTreeElement.firstChild(); |
263 while (nextSelectedElement && !nextSelectedElement.selectabl
e) | 371 while (nextSelectedElement && !nextSelectedElement.selectabl
e) |
264 nextSelectedElement = nextSelectedElement.nextSibling; | 372 nextSelectedElement = nextSelectedElement.nextSibling; |
265 handled = nextSelectedElement ? true : false; | 373 handled = nextSelectedElement ? true : false; |
266 } else { | 374 } else { |
267 if (event.altKey) | 375 if (event.altKey) |
268 this.selectedTreeElement.expandRecursively(); | 376 this.selectedTreeElement.expandRecursively(); |
269 else | 377 else |
270 this.selectedTreeElement.expand(); | 378 this.selectedTreeElement.expand(); |
271 } | 379 } |
272 } | 380 } |
273 } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /
* Delete */) | 381 break; |
274 handled = this.selectedTreeElement.ondelete(); | 382 case WebInspector.KeyboardShortcut.Keys.Backspace.code: |
275 else if (isEnterKey(event)) | 383 if (this._interactiveFilterEnabled && currentFilterString) { |
276 handled = this.selectedTreeElement.onenter(); | 384 handled = true; |
277 else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code
) | 385 this._setCurrentSelectionFilterString(currentFilterString.substr
(0, currentFilterString.length - 1)); |
278 handled = this.selectedTreeElement.onspace(); | 386 } else { |
| 387 handled = this.selectedTreeElement.ondelete(); |
| 388 } |
| 389 break; |
| 390 case WebInspector.KeyboardShortcut.Keys.Delete.code: |
| 391 if (this._interactiveFilterEnabled && currentFilterString) { |
| 392 handled = true; |
| 393 this._clearFilter(); |
| 394 } else |
| 395 handled = this.selectedTreeElement.ondelete(); |
| 396 break; |
| 397 case WebInspector.KeyboardShortcut.Keys.Esc.code: |
| 398 if (this._interactiveFilterEnabled) { |
| 399 if (currentFilterString) |
| 400 handled = true; |
| 401 this._clearFilter(); |
| 402 } |
| 403 break; |
| 404 case WebInspector.KeyboardShortcut.Keys.Space.code: |
| 405 if (!currentFilterString) |
| 406 handled = this.selectedTreeElement.onspace(); |
| 407 break; |
| 408 default: |
| 409 if (isEnterKey(event)) { |
| 410 if (this._interactiveFilterEnabled) |
| 411 this._clearFilter(); |
| 412 |
| 413 handled = this.selectedTreeElement.onenter(); |
| 414 } |
| 415 } |
279 | 416 |
280 if (nextSelectedElement) { | 417 if (nextSelectedElement) { |
281 nextSelectedElement.reveal(); | 418 nextSelectedElement.reveal(); |
282 nextSelectedElement.select(false, true); | 419 nextSelectedElement.select(false, true); |
283 } | 420 } |
284 | 421 |
285 if (handled) | 422 if (handled) |
286 event.consume(true); | 423 event.consume(true); |
287 }, | 424 }, |
288 | 425 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 this._childrenListNode = createElement("ol"); | 507 this._childrenListNode = createElement("ol"); |
371 this._childrenListNode.parentTreeElement = this; | 508 this._childrenListNode.parentTreeElement = this; |
372 this._childrenListNode.classList.add("children"); | 509 this._childrenListNode.classList.add("children"); |
373 | 510 |
374 this._hidden = false; | 511 this._hidden = false; |
375 this._selectable = true; | 512 this._selectable = true; |
376 this.expanded = false; | 513 this.expanded = false; |
377 this.selected = false; | 514 this.selected = false; |
378 this.setExpandable(expandable || false); | 515 this.setExpandable(expandable || false); |
379 this._collapsible = true; | 516 this._collapsible = true; |
| 517 |
| 518 /** @type {!Array.<!Object>} */ |
| 519 this._highlightChanges = []; |
380 } | 520 } |
381 | 521 |
382 /** @const */ | 522 /** @const */ |
383 TreeElement._ArrowToggleWidth = 10; | 523 TreeElement._ArrowToggleWidth = 10; |
384 | 524 |
385 TreeElement.prototype = { | 525 TreeElement.prototype = { |
386 /** | 526 /** |
| 527 * @return {boolean} |
| 528 */ |
| 529 _checkFilter: function() |
| 530 { |
| 531 return this.treeOutline._currentSelectionFilterString ? this.treeOutline
._makeFilterRegexFromString(this.treeOutline._currentSelectionFilterString).test
(this._titleElement.textContent) : true; |
| 532 }, |
| 533 |
| 534 /** |
| 535 * @param {!RegExp} regex |
| 536 * @return {boolean} |
| 537 */ |
| 538 _applyHighlightFilter: function(regex) { |
| 539 var textContent = this._listItemNode.textContent; |
| 540 var ranges = []; |
| 541 |
| 542 this._revertHighlightChanges(); |
| 543 |
| 544 var match = regex.exec(textContent); |
| 545 while (match) { |
| 546 ranges.push(new WebInspector.SourceRange(match.index, match[0].lengt
h)); |
| 547 match = regex.exec(textContent); |
| 548 } |
| 549 if (ranges.length) |
| 550 WebInspector.highlightRangesWithStyleClass(this._listItemNode, range
s, "tree-text-interactive-highlight", this._highlightChanges); |
| 551 |
| 552 return !!this._highlightChanges.length |
| 553 }, |
| 554 |
| 555 _revertHighlightChanges: function() |
| 556 { |
| 557 WebInspector.revertDomChanges(this._highlightChanges); |
| 558 this._highlightChanges = []; |
| 559 }, |
| 560 |
| 561 /** |
387 * @param {?TreeElement} ancestor | 562 * @param {?TreeElement} ancestor |
388 * @return {boolean} | 563 * @return {boolean} |
389 */ | 564 */ |
390 hasAncestor: function(ancestor) | 565 hasAncestor: function(ancestor) |
391 { | 566 { |
392 if (!ancestor) | 567 if (!ancestor) |
393 return false; | 568 return false; |
394 | 569 |
395 var currentNode = this.parent; | 570 var currentNode = this.parent; |
396 while (currentNode) { | 571 while (currentNode) { |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
596 this.treeOutline._unbindTreeElement(child); | 771 this.treeOutline._unbindTreeElement(child); |
597 for (var current = child.firstChild(); this.treeOutline && current;
current = current.traverseNextTreeElement(false, child, true)) | 772 for (var current = child.firstChild(); this.treeOutline && current;
current = current.traverseNextTreeElement(false, child, true)) |
598 this.treeOutline._unbindTreeElement(current); | 773 this.treeOutline._unbindTreeElement(current); |
599 child._detach(); | 774 child._detach(); |
600 } | 775 } |
601 this._children = []; | 776 this._children = []; |
602 }, | 777 }, |
603 | 778 |
604 get selectable() | 779 get selectable() |
605 { | 780 { |
606 if (this._hidden) | 781 if (this._hidden || !this._checkFilter()) |
607 return false; | 782 return false; |
608 return this._selectable; | 783 return this._selectable; |
609 }, | 784 }, |
610 | 785 |
611 set selectable(x) | 786 set selectable(x) |
612 { | 787 { |
613 this._selectable = x; | 788 this._selectable = x; |
614 }, | 789 }, |
615 | 790 |
616 get listItemElement() | 791 get listItemElement() |
(...skipping 535 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1152 isEventWithinDisclosureTriangle: function(event) | 1327 isEventWithinDisclosureTriangle: function(event) |
1153 { | 1328 { |
1154 // FIXME: We should not use getComputedStyle(). For that we need to get
rid of using ::before for disclosure triangle. (http://webk.it/74446) | 1329 // FIXME: We should not use getComputedStyle(). For that we need to get
rid of using ::before for disclosure triangle. (http://webk.it/74446) |
1155 var paddingLeftValue = window.getComputedStyle(this._listItemNode).paddi
ngLeft; | 1330 var paddingLeftValue = window.getComputedStyle(this._listItemNode).paddi
ngLeft; |
1156 console.assert(paddingLeftValue.endsWith("px")); | 1331 console.assert(paddingLeftValue.endsWith("px")); |
1157 var computedLeftPadding = parseFloat(paddingLeftValue); | 1332 var computedLeftPadding = parseFloat(paddingLeftValue); |
1158 var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding; | 1333 var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding; |
1159 return event.pageX >= left && event.pageX <= left + TreeElement._ArrowTo
ggleWidth && this._expandable; | 1334 return event.pageX >= left && event.pageX <= left + TreeElement._ArrowTo
ggleWidth && this._expandable; |
1160 } | 1335 } |
1161 } | 1336 } |
OLD | NEW |