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) { | |
| 99 switch (domNode.nodeType()) { | |
| 100 case Node.ELEMENT_NODE: | |
| 101 if (domNode.pseudoType()) { | |
| 102 return '::' + domNode.pseudoType(); | |
| 103 } else { | |
|
lushnikov
2016/12/08 05:37:03
style: no "else" after the "then" branch was termi
phulce
2016/12/08 19:03:27
done, should I apply that to all the case statemen
| |
| 104 Components.DOMPresentationUtils.decorateNodeLabel(domNode, crumbElemen t); | |
| 105 return null; | |
| 106 } | |
| 107 break; | |
| 108 | |
| 109 case Node.TEXT_NODE: | |
| 110 return Common.UIString('(text)'); | |
| 111 break; | |
| 112 | |
| 113 case Node.COMMENT_NODE: | |
| 114 return '<!-->'; | |
| 115 break; | |
| 116 | |
| 117 case Node.DOCUMENT_TYPE_NODE: | |
| 118 return '<!DOCTYPE>'; | |
| 119 break; | |
| 120 | |
| 121 case Node.DOCUMENT_FRAGMENT_NODE: | |
| 122 return domNode.shadowRootType() ? '#shadow-root' : domNode.nodeNameInCor rectCase(); | |
| 123 break; | |
| 124 | |
| 125 default: | |
| 126 return domNode.nodeNameInCorrectCase(); | |
| 127 } | |
| 128 } | |
| 129 | |
| 62 /** | 130 /** |
| 63 * @param {boolean=} force | 131 * @param {boolean=} force |
| 64 */ | 132 */ |
| 65 update(force) { | 133 update(force) { |
| 66 if (!this.isShowing()) | 134 if (!this.isShowing()) |
| 67 return; | 135 return; |
| 68 | 136 |
| 69 var currentDOMNode = this._currentDOMNode; | 137 var currentDOMNode = this._currentDOMNode; |
| 70 var crumbs = this.crumbsElement; | 138 var crumbs = this.crumbsElement; |
| 71 | 139 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 84 | 152 |
| 85 if (handled && !force) { | 153 if (handled && !force) { |
| 86 // We don't need to rebuild the crumbs, but we need to adjust sizes | 154 // We don't need to rebuild the crumbs, but we need to adjust sizes |
| 87 // to reflect the new focused or root node. | 155 // to reflect the new focused or root node. |
| 88 this.updateSizes(); | 156 this.updateSizes(); |
| 89 return; | 157 return; |
| 90 } | 158 } |
| 91 | 159 |
| 92 crumbs.removeChildren(); | 160 crumbs.removeChildren(); |
| 93 | 161 |
| 94 var panel = this; | 162 var onClickCrumb = this._onClickCrumb.bind(this); |
|
lushnikov
2016/12/08 05:37:03
can we inline this?
phulce
2016/12/08 19:03:27
sure, we'll bind inside a loop but I guess the bre
| |
| 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) { | 163 for (var current = currentDOMNode; current; current = current.parentNode) { |
| 128 if (current.nodeType() === Node.DOCUMENT_NODE) | 164 if (current.nodeType() === Node.DOCUMENT_NODE) |
| 129 continue; | 165 continue; |
| 130 | 166 |
| 131 crumb = createElementWithClass('span', 'crumb'); | 167 crumb = createElementWithClass('span', 'crumb'); |
| 132 crumb[this._nodeSymbol] = current; | 168 crumb[this._nodeSymbol] = current; |
| 133 crumb.addEventListener('mousedown', boundSelectCrumb, false); | 169 crumb.addEventListener('mousedown', onClickCrumb, false); |
| 134 | 170 |
| 135 var crumbTitle = ''; | 171 var crumbTitle = this._determineElementTitle(current, crumb); |
| 136 switch (current.nodeType()) { | 172 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'); | 173 var nameElement = createElement('span'); |
| 166 nameElement.textContent = crumbTitle; | 174 nameElement.textContent = crumbTitle; |
| 167 crumb.appendChild(nameElement); | 175 crumb.appendChild(nameElement); |
| 168 crumb.title = crumbTitle; | 176 crumb.title = crumbTitle; |
| 169 } | 177 } |
| 170 | 178 |
| 171 if (current === currentDOMNode) | 179 if (current === currentDOMNode) |
| 172 crumb.classList.add('selected'); | 180 crumb.classList.add('selected'); |
| 173 crumbs.insertBefore(crumb, crumbs.firstChild); | 181 crumbs.insertBefore(crumb, crumbs.firstChild); |
| 174 } | 182 } |
| 175 | 183 |
| 176 this.updateSizes(); | 184 this.updateSizes(); |
| 177 } | 185 } |
| 178 | 186 |
| 179 /** | 187 /** |
| 180 * @param {!Element=} focusedCrumb | 188 * @param {!Element=} focusedCrumb |
| 189 * @return {!Object} | |
| 181 */ | 190 */ |
| 182 updateSizes(focusedCrumb) { | 191 _resetCrumbStylesAndFindSelections(focusedCrumb) { |
| 183 if (!this.isShowing()) | |
| 184 return; | |
| 185 | |
| 186 var crumbs = this.crumbsElement; | 192 var crumbs = this.crumbsElement; |
| 187 if (!crumbs.firstChild) | |
| 188 return; | |
| 189 | |
| 190 var selectedIndex = 0; | 193 var selectedIndex = 0; |
| 191 var focusedIndex = 0; | 194 var focusedIndex = 0; |
| 192 var selectedCrumb; | 195 var selectedCrumb; |
| 193 | 196 |
| 194 // Reset crumb styles. | 197 // Reset crumb styles. |
| 195 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 198 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 196 var crumb = crumbs.children[i]; | 199 var crumb = crumbs.children[i]; |
| 197 // Find the selected crumb and index. | 200 // Find the selected crumb and index. |
| 198 if (!selectedCrumb && crumb.classList.contains('selected')) { | 201 if (!selectedCrumb && crumb.classList.contains('selected')) { |
| 199 selectedCrumb = crumb; | 202 selectedCrumb = crumb; |
| 200 selectedIndex = i; | 203 selectedIndex = i; |
| 201 } | 204 } |
| 202 | 205 |
| 203 // Find the focused crumb index. | 206 // Find the focused crumb index. |
| 204 if (crumb === focusedCrumb) | 207 if (crumb === focusedCrumb) |
| 205 focusedIndex = i; | 208 focusedIndex = i; |
| 206 | 209 |
| 207 crumb.classList.remove('compact', 'collapsed', 'hidden'); | 210 crumb.classList.remove('compact', 'collapsed', 'hidden'); |
| 208 } | 211 } |
| 209 | 212 |
| 210 // Layout 1: Measure total and normal crumb sizes | 213 return {selectedIndex, focusedIndex, selectedCrumb}; |
| 211 var contentElementWidth = this.contentElement.offsetWidth; | 214 } |
| 212 var normalSizes = []; | 215 |
| 213 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 216 /** |
| 217 * @return {!Object} | |
| 218 */ | |
| 219 _measureElementSizes() { | |
| 220 var crumbs = this.crumbsElement; | |
| 221 | |
| 222 // Layout 1: Measure total and normal crumb sizes at the same time as a | |
| 223 // dummy element for the collapsed size | |
| 224 var collapsedElem = createElementWithClass('span', 'crumb collapsed'); | |
| 225 crumbs.insertBefore(collapsedElem, crumbs.firstChild); | |
| 226 | |
| 227 var available = crumbs.offsetWidth; | |
|
lushnikov
2016/12/08 05:37:03
can we use .measurePrefferedSize() method?
phulce
2016/12/08 19:03:27
oh, jw why would that be better? looks like that w
| |
| 228 var collapsed = crumbs.firstChild.offsetWidth; | |
| 229 | |
| 230 var normal = []; | |
| 231 for (var i = 1; i < crumbs.childNodes.length; ++i) { | |
| 214 var crumb = crumbs.childNodes[i]; | 232 var crumb = crumbs.childNodes[i]; |
| 215 normalSizes[i] = crumb.offsetWidth; | 233 normal[i - 1] = crumb.offsetWidth; |
| 216 } | 234 } |
| 217 | 235 |
| 236 crumbs.removeChild(collapsedElem); | |
| 237 | |
| 218 // Layout 2: Measure collapsed crumb sizes | 238 // Layout 2: Measure collapsed crumb sizes |
| 219 var compactSizes = []; | 239 var compact = []; |
| 220 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 240 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 221 var crumb = crumbs.childNodes[i]; | 241 var crumb = crumbs.childNodes[i]; |
| 222 crumb.classList.add('compact'); | 242 crumb.classList.add('compact'); |
| 223 } | 243 } |
| 224 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 244 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 225 var crumb = crumbs.childNodes[i]; | 245 var crumb = crumbs.childNodes[i]; |
| 226 compactSizes[i] = crumb.offsetWidth; | 246 compact[i] = crumb.offsetWidth; |
| 227 } | 247 } |
| 228 | 248 |
| 229 // Layout 3: Measure collapsed crumb size | |
| 230 crumbs.firstChild.classList.add('collapsed'); | |
| 231 var collapsedSize = crumbs.firstChild.offsetWidth; | |
| 232 | |
| 233 // Clean up. | 249 // Clean up. |
| 234 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 250 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 235 var crumb = crumbs.childNodes[i]; | 251 var crumb = crumbs.childNodes[i]; |
| 236 crumb.classList.remove('compact', 'collapsed'); | 252 crumb.classList.remove('compact', 'collapsed'); |
| 237 } | 253 } |
| 238 | 254 |
| 255 return {normal, compact, collapsed, available}; | |
|
lushnikov
2016/12/08 05:37:03
so far we don't use shorthand notation yet. No one
phulce
2016/12/08 19:03:27
apparently it does :) haha will remove for now
| |
| 256 } | |
| 257 | |
| 258 /** | |
| 259 * @param {!Element=} focusedCrumb | |
| 260 */ | |
| 261 updateSizes(focusedCrumb) { | |
| 262 if (!this.isShowing()) | |
| 263 return; | |
| 264 | |
| 265 var crumbs = this.crumbsElement; | |
| 266 if (!crumbs.firstChild) | |
| 267 return; | |
| 268 | |
| 269 var selections = this._resetCrumbStylesAndFindSelections(focusedCrumb); | |
| 270 var sizes = this._measureElementSizes(); | |
| 271 var selectedIndex = selections.selectedIndex; | |
| 272 var focusedIndex = selections.focusedIndex; | |
| 273 var selectedCrumb = selections.selectedCrumb; | |
| 274 | |
| 239 function crumbsAreSmallerThanContainer() { | 275 function crumbsAreSmallerThanContainer() { |
| 240 var totalSize = 0; | 276 var totalSize = 0; |
| 241 for (var i = 0; i < crumbs.childNodes.length; ++i) { | 277 for (var i = 0; i < crumbs.childNodes.length; ++i) { |
| 242 var crumb = crumbs.childNodes[i]; | 278 var crumb = crumbs.childNodes[i]; |
| 243 if (crumb.classList.contains('hidden')) | 279 if (crumb.classList.contains('hidden')) |
| 244 continue; | 280 continue; |
| 245 if (crumb.classList.contains('collapsed')) { | 281 if (crumb.classList.contains('collapsed')) { |
| 246 totalSize += collapsedSize; | 282 totalSize += sizes.collapsed; |
| 247 continue; | 283 continue; |
| 248 } | 284 } |
| 249 totalSize += crumb.classList.contains('compact') ? compactSizes[i] : nor malSizes[i]; | 285 totalSize += crumb.classList.contains('compact') ? sizes.compact[i] : si zes.normal[i]; |
| 250 } | 286 } |
| 251 const rightPadding = 10; | 287 const rightPadding = 10; |
| 252 return totalSize + rightPadding < contentElementWidth; | 288 return totalSize + rightPadding < sizes.available; |
| 253 } | 289 } |
| 254 | 290 |
| 255 if (crumbsAreSmallerThanContainer()) | 291 if (crumbsAreSmallerThanContainer()) |
| 256 return; // No need to compact the crumbs, they all fit at full size. | 292 return; // No need to compact the crumbs, they all fit at full size. |
| 257 | 293 |
| 258 var BothSides = 0; | 294 var BothSides = 0; |
| 259 var AncestorSide = -1; | 295 var AncestorSide = -1; |
| 260 var ChildSide = 1; | 296 var ChildSide = 1; |
| 261 | 297 |
| 262 /** | 298 /** |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 411 | 447 |
| 412 // Collapse the selected crumb as a last resort. Pass true to prevent coales cing. | 448 // Collapse the selected crumb as a last resort. Pass true to prevent coales cing. |
| 413 collapse(selectedCrumb, true); | 449 collapse(selectedCrumb, true); |
| 414 } | 450 } |
| 415 }; | 451 }; |
| 416 | 452 |
| 417 /** @enum {symbol} */ | 453 /** @enum {symbol} */ |
| 418 Elements.ElementsBreadcrumbs.Events = { | 454 Elements.ElementsBreadcrumbs.Events = { |
| 419 NodeSelected: Symbol('NodeSelected') | 455 NodeSelected: Symbol('NodeSelected') |
| 420 }; | 456 }; |
| OLD | NEW |