Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 /** | 4 /** |
| 5 * @unrestricted | 5 * @unrestricted |
| 6 */ | 6 */ |
| 7 Elements.ElementsBreadcrumbs = class extends UI.HBox { | 7 Elements.ElementsBreadcrumbs = class extends UI.HBox { |
| 8 constructor() { | 8 constructor() { |
| 9 super(true); | 9 super(true); |
| 10 this.registerRequiredCSS('elements/breadcrumbs.css'); | 10 this.registerRequiredCSS('elements/breadcrumbs.css'); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 36 return; | 36 return; |
| 37 } | 37 } |
| 38 } | 38 } |
| 39 } | 39 } |
| 40 | 40 |
| 41 /** | 41 /** |
| 42 * @param {?SDK.DOMNode} node | 42 * @param {?SDK.DOMNode} node |
| 43 */ | 43 */ |
| 44 setSelectedNode(node) { | 44 setSelectedNode(node) { |
| 45 this._currentDOMNode = node; | 45 this._currentDOMNode = node; |
| 46 this.update(); | 46 this.crumbsElement.window().requestAnimationFrame(() => this.update()); |
| 47 } | 47 } |
| 48 | 48 |
| 49 _mouseMovedInCrumbs(event) { | 49 _mouseMovedInCrumbs(event) { |
| 50 var nodeUnderMouse = event.target; | 50 var nodeUnderMouse = event.target; |
| 51 var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass('crumb'); | 51 var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass('crumb'); |
| 52 var node = /** @type {?SDK.DOMNode} */ (crumbElement ? crumbElement[this._no deSymbol] : null); | 52 var node = /** @type {?SDK.DOMNode} */ (crumbElement ? crumbElement[this._no deSymbol] : null); |
| 53 if (node) | 53 if (node) |
| 54 node.highlight(); | 54 node.highlight(); |
| 55 } | 55 } |
| 56 | 56 |
| 57 _mouseMovedOutOfCrumbs(event) { | 57 _mouseMovedOutOfCrumbs(event) { |
| 58 if (this._currentDOMNode) | 58 if (this._currentDOMNode) |
| 59 SDK.DOMModel.hideDOMNodeHighlight(); | 59 SDK.DOMModel.hideDOMNodeHighlight(); |
| 60 } | 60 } |
| 61 | 61 |
| 62 | |
| 63 /** | |
| 64 * @param {!Event} event | |
| 65 * @this {Elements.ElementsBreadcrumbs} | |
| 66 */ | |
| 67 _onClickCrumb(event) { | |
| 68 event.preventDefault(); | |
| 69 var crumb = /** @type {!Element} */ (event.currentTarget); | |
| 70 if (!crumb.classList.contains('collapsed')) { | |
| 71 this.dispatchEventToListeners(Elements.ElementsBreadcrumbs.Events.NodeSele cted, crumb[this._nodeSymbol]); | |
| 72 return; | |
| 73 } | |
| 74 | |
| 75 // Clicking a collapsed crumb will expose the hidden crumbs. | |
| 76 if (crumb === this.crumbsElement.firstChild) { | |
| 77 // If the clicked crumb is the first child, pick the farthest crumb | |
| 78 // that is still hidden. This allows the user to expose every crumb. | |
| 79 var currentCrumb = crumb; | |
| 80 while (currentCrumb) { | |
| 81 var hidden = currentCrumb.classList.contains('hidden'); | |
| 82 var collapsed = currentCrumb.classList.contains('collapsed'); | |
| 83 if (!hidden && !collapsed) | |
| 84 break; | |
| 85 crumb = currentCrumb; | |
| 86 currentCrumb = currentCrumb.nextSiblingElement; | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 this.updateSizes(crumb); | |
| 91 } | |
| 92 | |
| 93 /** | |
| 94 * @param {!SDK.DOMNode} domNode | |
| 95 * @param {!Element} crumbElement | |
| 96 * @return {?string} | |
| 97 */ | |
| 98 _determineElementTitle(domNode, crumbElement) { | |
|
lushnikov
2016/12/08 21:20:43
I would expect this method to accept only a domNod
phulce
2016/12/09 00:46:30
Done.
| |
| 99 switch (domNode.nodeType()) { | |
| 100 case Node.ELEMENT_NODE: | |
| 101 if (domNode.pseudoType()) | |
| 102 return '::' + domNode.pseudoType(); | |
| 103 | |
| 104 Components.DOMPresentationUtils.decorateNodeLabel(domNode, crumbElement) ; | |
| 105 return null; | |
| 106 case Node.TEXT_NODE: | |
| 107 return Common.UIString('(text)'); | |
| 108 case Node.COMMENT_NODE: | |
| 109 return '<!-->'; | |
| 110 case Node.DOCUMENT_TYPE_NODE: | |
| 111 return '<!DOCTYPE>'; | |
| 112 case Node.DOCUMENT_FRAGMENT_NODE: | |
| 113 return domNode.shadowRootType() ? '#shadow-root' : domNode.nodeNameInCor rectCase(); | |
| 114 default: | |
| 115 return domNode.nodeNameInCorrectCase(); | |
| 116 } | |
| 117 } | |
| 118 | |
| 62 /** | 119 /** |
| 63 * @param {boolean=} force | 120 * @param {boolean=} force |
| 64 */ | 121 */ |
| 65 update(force) { | 122 update(force) { |
| 66 if (!this.isShowing()) | 123 if (!this.isShowing()) |
| 67 return; | 124 return; |
| 68 | 125 |
| 69 var currentDOMNode = this._currentDOMNode; | 126 var currentDOMNode = this._currentDOMNode; |
| 70 var crumbs = this.crumbsElement; | 127 var crumbs = this.crumbsElement; |
| 71 | 128 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 84 | 141 |
| 85 if (handled && !force) { | 142 if (handled && !force) { |
| 86 // We don't need to rebuild the crumbs, but we need to adjust sizes | 143 // We don't need to rebuild the crumbs, but we need to adjust sizes |
| 87 // to reflect the new focused or root node. | 144 // to reflect the new focused or root node. |
| 88 this.updateSizes(); | 145 this.updateSizes(); |
| 89 return; | 146 return; |
| 90 } | 147 } |
| 91 | 148 |
| 92 crumbs.removeChildren(); | 149 crumbs.removeChildren(); |
| 93 | 150 |
| 94 var panel = this; | |
| 95 | |
| 96 /** | |
| 97 * @param {!Event} event | |
| 98 * @this {Elements.ElementsBreadcrumbs} | |
| 99 */ | |
| 100 function selectCrumb(event) { | |
| 101 event.preventDefault(); | |
| 102 var crumb = /** @type {!Element} */ (event.currentTarget); | |
| 103 if (!crumb.classList.contains('collapsed')) { | |
| 104 this.dispatchEventToListeners(Elements.ElementsBreadcrumbs.Events.NodeSe lected, crumb[this._nodeSymbol]); | |
| 105 return; | |
| 106 } | |
| 107 | |
| 108 // Clicking a collapsed crumb will expose the hidden crumbs. | |
| 109 if (crumb === panel.crumbsElement.firstChild) { | |
| 110 // If the focused crumb is the first child, pick the farthest crumb | |
| 111 // that is still hidden. This allows the user to expose every crumb. | |
| 112 var currentCrumb = crumb; | |
| 113 while (currentCrumb) { | |
| 114 var hidden = currentCrumb.classList.contains('hidden'); | |
| 115 var collapsed = currentCrumb.classList.contains('collapsed'); | |
| 116 if (!hidden && !collapsed) | |
| 117 break; | |
| 118 crumb = currentCrumb; | |
| 119 currentCrumb = currentCrumb.nextSiblingElement; | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 this.updateSizes(crumb); | |
| 124 } | |
| 125 | |
| 126 var boundSelectCrumb = selectCrumb.bind(this); | |
| 127 for (var current = currentDOMNode; current; current = current.parentNode) { | 151 for (var current = currentDOMNode; current; current = current.parentNode) { |
| 128 if (current.nodeType() === Node.DOCUMENT_NODE) | 152 if (current.nodeType() === Node.DOCUMENT_NODE) |
| 129 continue; | 153 continue; |
| 130 | 154 |
| 131 crumb = createElementWithClass('span', 'crumb'); | 155 crumb = createElementWithClass('span', 'crumb'); |
| 132 crumb[this._nodeSymbol] = current; | 156 crumb[this._nodeSymbol] = current; |
| 133 crumb.addEventListener('mousedown', boundSelectCrumb, false); | 157 crumb.addEventListener('mousedown', this._onClickCrumb.bind(this), false); |
| 134 | 158 |
| 135 var crumbTitle = ''; | 159 var crumbTitle = this._determineElementTitle(current, crumb); |
| 136 switch (current.nodeType()) { | 160 if (crumbTitle) { |
| 137 case Node.ELEMENT_NODE: | |
| 138 if (current.pseudoType()) | |
| 139 crumbTitle = '::' + current.pseudoType(); | |
| 140 else | |
| 141 Components.DOMPresentationUtils.decorateNodeLabel(current, crumb); | |
| 142 break; | |
| 143 | |
| 144 case Node.TEXT_NODE: | |
| 145 crumbTitle = Common.UIString('(text)'); | |
| 146 break; | |
| 147 | |
| 148 case Node.COMMENT_NODE: | |
| 149 crumbTitle = '<!-->'; | |
| 150 break; | |
| 151 | |
| 152 case Node.DOCUMENT_TYPE_NODE: | |
| 153 crumbTitle = '<!DOCTYPE>'; | |
| 154 break; | |
| 155 | |
| 156 case Node.DOCUMENT_FRAGMENT_NODE: | |
| 157 crumbTitle = current.shadowRootType() ? '#shadow-root' : current.nodeN ameInCorrectCase(); | |
| 158 break; | |
| 159 | |
| 160 default: | |
| 161 crumbTitle = current.nodeNameInCorrectCase(); | |
| 162 } | |
| 163 | |
| 164 if (!crumb.childNodes.length) { | |
| 165 var nameElement = createElement('span'); | 161 var nameElement = createElement('span'); |
| 166 nameElement.textContent = crumbTitle; | 162 nameElement.textContent = crumbTitle; |
| 167 crumb.appendChild(nameElement); | 163 crumb.appendChild(nameElement); |
| 168 crumb.title = crumbTitle; | 164 crumb.title = crumbTitle; |
| 169 } | 165 } |
| 170 | 166 |
| 171 if (current === currentDOMNode) | 167 if (current === currentDOMNode) |
| 172 crumb.classList.add('selected'); | 168 crumb.classList.add('selected'); |
| 173 crumbs.insertBefore(crumb, crumbs.firstChild); | 169 crumbs.insertBefore(crumb, crumbs.firstChild); |
| 174 } | 170 } |
| 175 | 171 |
| 176 this.updateSizes(); | 172 this.updateSizes(); |
| 177 } | 173 } |
| 178 | 174 |
| 179 /** | 175 /** |
| 180 * @param {!Element=} focusedCrumb | 176 * @param {!Element=} focusedCrumb |
| 177 * @return {!Object} | |
|
lushnikov
2016/12/08 21:20:43
let's annotate it with details: {{selectedIndex: n
phulce
2016/12/09 00:46:30
Done.
| |
| 181 */ | 178 */ |
| 182 updateSizes(focusedCrumb) { | 179 _resetCrumbStylesAndFindSelections(focusedCrumb) { |
| 183 if (!this.isShowing()) | |
| 184 return; | |
| 185 | |
| 186 var crumbs = this.crumbsElement; | 180 var crumbs = this.crumbsElement; |
| 187 if (!crumbs.firstChild) | |
| 188 return; | |
| 189 | |
| 190 var selectedIndex = 0; | 181 var selectedIndex = 0; |
| 191 var focusedIndex = 0; | 182 var focusedIndex = 0; |
| 192 var selectedCrumb; | 183 var selectedCrumb; |
| 193 | 184 |
| 194 // Reset crumb styles. | 185 // Reset crumb styles. |
| 195 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 186 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 196 var crumb = crumbs.children[i]; | 187 var crumb = crumbs.children[i]; |
| 197 // Find the selected crumb and index. | 188 // Find the selected crumb and index. |
| 198 if (!selectedCrumb && crumb.classList.contains('selected')) { | 189 if (!selectedCrumb && crumb.classList.contains('selected')) { |
| 199 selectedCrumb = crumb; | 190 selectedCrumb = crumb; |
| 200 selectedIndex = i; | 191 selectedIndex = i; |
| 201 } | 192 } |
| 202 | 193 |
| 203 // Find the focused crumb index. | 194 // Find the focused crumb index. |
| 204 if (crumb === focusedCrumb) | 195 if (crumb === focusedCrumb) |
| 205 focusedIndex = i; | 196 focusedIndex = i; |
| 206 | 197 |
| 207 crumb.classList.remove('compact', 'collapsed', 'hidden'); | 198 crumb.classList.remove('compact', 'collapsed', 'hidden'); |
| 208 } | 199 } |
| 209 | 200 |
| 210 // Layout 1: Measure total and normal crumb sizes | 201 return {selectedIndex, focusedIndex, selectedCrumb}; |
|
lushnikov
2016/12/08 21:20:43
nit: let's avoid object shorthands here as well
phulce
2016/12/09 00:46:30
Done.
| |
| 211 var contentElementWidth = this.contentElement.offsetWidth; | 202 } |
| 203 | |
| 204 /** | |
| 205 * @return {!Object} | |
|
lushnikov
2016/12/08 21:20:43
let's annotate this with details
phulce
2016/12/09 00:46:30
Done.
| |
| 206 */ | |
| 207 _measureElementSizes() { | |
| 208 var crumbs = this.crumbsElement; | |
| 209 | |
| 210 // Layout 1: Measure total and normal crumb sizes at the same time as a | |
| 211 // dummy element for the collapsed size | |
|
lushnikov
2016/12/08 21:20:43
style: "." in the end of the comment
phulce
2016/12/09 00:46:30
Done.
| |
| 212 var collapsedElem = createElementWithClass('span', 'crumb collapsed'); | |
|
lushnikov
2016/12/08 21:20:43
var collapsedElement = ...
style: we try to avoid
phulce
2016/12/09 00:46:30
Done.
| |
| 213 crumbs.insertBefore(collapsedElem, crumbs.firstChild); | |
| 214 | |
| 215 var available = crumbs.offsetWidth; | |
| 216 var collapsed = crumbs.firstChild.offsetWidth; | |
|
lushnikov
2016/12/08 21:20:43
var collapsed = collapsedElem.offsetWidth;
phulce
2016/12/09 00:46:30
Done.
| |
| 217 | |
| 212 var normalSizes = []; | 218 var normalSizes = []; |
| 213 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 219 for (var i = 1; i < crumbs.childNodes.length; ++i) { |
| 214 var crumb = crumbs.childNodes[i]; | 220 var crumb = crumbs.childNodes[i]; |
| 215 normalSizes[i] = crumb.offsetWidth; | 221 normalSizes[i - 1] = crumb.offsetWidth; |
| 216 } | 222 } |
| 217 | 223 |
| 224 crumbs.removeChild(collapsedElem); | |
| 225 | |
| 218 // Layout 2: Measure collapsed crumb sizes | 226 // Layout 2: Measure collapsed crumb sizes |
| 219 var compactSizes = []; | 227 var compactSizes = []; |
| 220 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 228 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 221 var crumb = crumbs.childNodes[i]; | 229 var crumb = crumbs.childNodes[i]; |
| 222 crumb.classList.add('compact'); | 230 crumb.classList.add('compact'); |
| 223 } | 231 } |
| 224 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 232 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 225 var crumb = crumbs.childNodes[i]; | 233 var crumb = crumbs.childNodes[i]; |
| 226 compactSizes[i] = crumb.offsetWidth; | 234 compactSizes[i] = crumb.offsetWidth; |
| 227 } | 235 } |
| 228 | 236 |
| 229 // Layout 3: Measure collapsed crumb size | |
| 230 crumbs.firstChild.classList.add('collapsed'); | |
| 231 var collapsedSize = crumbs.firstChild.offsetWidth; | |
| 232 | |
| 233 // Clean up. | 237 // Clean up. |
| 234 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 238 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 235 var crumb = crumbs.childNodes[i]; | 239 var crumb = crumbs.childNodes[i]; |
| 236 crumb.classList.remove('compact', 'collapsed'); | 240 crumb.classList.remove('compact', 'collapsed'); |
| 237 } | 241 } |
| 238 | 242 |
| 243 return {normal: normalSizes, compact: compactSizes, collapsed: collapsed, av ailable: available}; | |
| 244 } | |
| 245 | |
| 246 /** | |
| 247 * @param {!Element=} focusedCrumb | |
| 248 */ | |
| 249 updateSizes(focusedCrumb) { | |
| 250 if (!this.isShowing()) | |
| 251 return; | |
| 252 | |
| 253 var crumbs = this.crumbsElement; | |
| 254 if (!crumbs.firstChild) | |
| 255 return; | |
| 256 | |
| 257 var selections = this._resetCrumbStylesAndFindSelections(focusedCrumb); | |
| 258 var sizes = this._measureElementSizes(); | |
| 259 var selectedIndex = selections.selectedIndex; | |
| 260 var focusedIndex = selections.focusedIndex; | |
| 261 var selectedCrumb = selections.selectedCrumb; | |
| 262 | |
| 239 function crumbsAreSmallerThanContainer() { | 263 function crumbsAreSmallerThanContainer() { |
| 240 var totalSize = 0; | 264 var totalSize = 0; |
| 241 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 265 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 242 var crumb = crumbs.childNodes[i]; | 266 var crumb = crumbs.childNodes[i]; |
| 243 if (crumb.classList.contains('hidden')) | 267 if (crumb.classList.contains('hidden')) |
| 244 continue; | 268 continue; |
| 245 if (crumb.classList.contains('collapsed')) { | 269 if (crumb.classList.contains('collapsed')) { |
| 246 totalSize += collapsedSize; | 270 totalSize += sizes.collapsed; |
| 247 continue; | 271 continue; |
| 248 } | 272 } |
| 249 totalSize += crumb.classList.contains('compact') ? compactSizes[i] : nor malSizes[i]; | 273 totalSize += crumb.classList.contains('compact') ? sizes.compact[i] : si zes.normal[i]; |
| 250 } | 274 } |
| 251 const rightPadding = 10; | 275 const rightPadding = 10; |
| 252 return totalSize + rightPadding < contentElementWidth; | 276 return totalSize + rightPadding < sizes.available; |
| 253 } | 277 } |
| 254 | 278 |
| 255 if (crumbsAreSmallerThanContainer()) | 279 if (crumbsAreSmallerThanContainer()) |
| 256 return; // No need to compact the crumbs, they all fit at full size. | 280 return; // No need to compact the crumbs, they all fit at full size. |
| 257 | 281 |
| 258 var BothSides = 0; | 282 var BothSides = 0; |
| 259 var AncestorSide = -1; | 283 var AncestorSide = -1; |
| 260 var ChildSide = 1; | 284 var ChildSide = 1; |
| 261 | 285 |
| 262 /** | 286 /** |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 411 | 435 |
| 412 // Collapse the selected crumb as a last resort. Pass true to prevent coales cing. | 436 // Collapse the selected crumb as a last resort. Pass true to prevent coales cing. |
| 413 collapse(selectedCrumb, true); | 437 collapse(selectedCrumb, true); |
| 414 } | 438 } |
| 415 }; | 439 }; |
| 416 | 440 |
| 417 /** @enum {symbol} */ | 441 /** @enum {symbol} */ |
| 418 Elements.ElementsBreadcrumbs.Events = { | 442 Elements.ElementsBreadcrumbs.Events = { |
| 419 NodeSelected: Symbol('NodeSelected') | 443 NodeSelected: Symbol('NodeSelected') |
| 420 }; | 444 }; |
| OLD | NEW |