1 /* 2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following 13 * disclaimer in the documentation and/or other materials 14 * provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 27 * OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /** 31 * @constructor 32 * @extends {WebInspector.SidebarView} 33 */ 34 WebInspector.CSSNamedFlowCollectionsView = function() 35 { 36 WebInspector.SidebarView.call(this, WebInspector.SidebarView.SidebarPosition.Start); 37 this.registerRequiredCSS("cssNamedFlows.css"); 38 39 this._namedFlows = {}; 40 this._contentNodes = {}; 41 this._regionNodes = {}; 42 43 this.element.classList.add("css-named-flow-collections-view"); 44 this.element.classList.add("fill"); 45 46 this._statusElement = document.createElement("span"); 47 this._statusElement.textContent = WebInspector.UIString("CSS Named Flows"); 48 49 var sidebarHeader = this.firstElement().createChild("div", "tabbed-pane-header selected sidebar-header"); 50 var tab = sidebarHeader.createChild("div", "tabbed-pane-header-tab"); 51 tab.createChild("span", "tabbed-pane-header-tab-title").textContent = WebInspector.UIString("CSS Named Flows"); 52 53 this._sidebarContentElement = this.firstElement().createChild("div", "sidebar-content outline-disclosure"); 54 this._flowListElement = this._sidebarContentElement.createChild("ol"); 55 this._flowTree = new TreeOutline(this._flowListElement); 56 57 this._emptyElement = document.createElement("div"); 58 this._emptyElement.classList.add("info"); 59 this._emptyElement.textContent = WebInspector.UIString("No CSS Named Flows"); 60 61 this._tabbedPane = new WebInspector.TabbedPane(); 62 this._tabbedPane.closeableTabs = true; 63 this._tabbedPane.show(this.secondElement()); 64 } 65 66 WebInspector.CSSNamedFlowCollectionsView.prototype = { 67 showInDrawer: function() 68 { 69 WebInspector.inspectorView.showCloseableViewInDrawer("css-flows", WebInspector.UIString("CSS Flows"), this); 70 }, 71 72 reset: function() 73 { 74 if (!this._document) 75 return; 76 77 WebInspector.cssModel.getNamedFlowCollectionAsync(this._document.id, this._resetNamedFlows.bind(this)); 78 }, 79 80 /** 81 * @param {!WebInspector.DOMDocument} document 82 */ 83 _setDocument: function(document) 84 { 85 this._document = document; 86 this.reset(); 87 }, 88 89 /** 90 * @param {!WebInspector.Event} event 91 */ 92 _documentUpdated: function(event) 93 { 94 var document = /** @type {!WebInspector.DOMDocument} */ (event.data); 95 this._setDocument(document); 96 }, 97 98 /** 99 * @param {boolean} hasContent 100 */ 101 _setSidebarHasContent: function(hasContent) 102 { 103 if (hasContent) { 104 if (!this._emptyElement.parentNode) 105 return; 106 107 this._sidebarContentElement.removeChild(this._emptyElement); 108 this._sidebarContentElement.appendChild(this._flowListElement); 109 } else { 110 if (!this._flowListElement.parentNode) 111 return; 112 113 this._sidebarContentElement.removeChild(this._flowListElement); 114 this._sidebarContentElement.appendChild(this._emptyElement); 115 } 116 }, 117 118 /** 119 * @param {!WebInspector.NamedFlow} flow 120 */ 121 _appendNamedFlow: function(flow) 122 { 123 var flowHash = this._hashNamedFlow(flow.documentNodeId, flow.name); 124 var flowContainer = { flow: flow, flowHash: flowHash }; 125 126 for (var i = 0; i < flow.content.length; ++i) 127 this._contentNodes[flow.content[i]] = flowHash; 128 for (var i = 0; i < flow.regions.length; ++i) 129 this._regionNodes[flow.regions[i].nodeId] = flowHash; 130 131 var flowTreeItem = new WebInspector.FlowTreeElement(flowContainer); 132 flowTreeItem.onselect = this._selectNamedFlowTab.bind(this, flowHash); 133 134 flowContainer.flowTreeItem = flowTreeItem; 135 this._namedFlows[flowHash] = flowContainer; 136 137 if (!this._flowTree.children.length) 138 this._setSidebarHasContent(true); 139 this._flowTree.appendChild(flowTreeItem); 140 }, 141 142 /** 143 * @param {string} flowHash 144 */ 145 _removeNamedFlow: function(flowHash) 146 { 147 var flowContainer = this._namedFlows[flowHash]; 148 149 if (this._tabbedPane._tabsById[flowHash]) 150 this._tabbedPane.closeTab(flowHash); 151 this._flowTree.removeChild(flowContainer.flowTreeItem); 152 153 var flow = flowContainer.flow; 154 for (var i = 0; i < flow.content.length; ++i) 155 delete this._contentNodes[flow.content[i]]; 156 for (var i = 0; i < flow.regions.length; ++i) 157 delete this._regionNodes[flow.regions[i].nodeId]; 158 159 delete this._namedFlows[flowHash]; 160 161 if (!this._flowTree.children.length) 162 this._setSidebarHasContent(false); 163 }, 164 165 /** 166 * @param {!WebInspector.NamedFlow} flow 167 */ 168 _updateNamedFlow: function(flow) 169 { 170 var flowHash = this._hashNamedFlow(flow.documentNodeId, flow.name); 171 var flowContainer = this._namedFlows[flowHash]; 172 173 if (!flowContainer) 174 return; 175 176 var oldFlow = flowContainer.flow; 177 flowContainer.flow = flow; 178 179 for (var i = 0; i < oldFlow.content.length; ++i) 180 delete this._contentNodes[oldFlow.content[i]]; 181 for (var i = 0; i < oldFlow.regions.length; ++i) 182 delete this._regionNodes[oldFlow.regions[i].nodeId]; 183 184 for (var i = 0; i < flow.content.length; ++i) 185 this._contentNodes[flow.content[i]] = flowHash; 186 for (var i = 0; i < flow.regions.length; ++i) 187 this._regionNodes[flow.regions[i].nodeId] = flowHash; 188 189 flowContainer.flowTreeItem.setOverset(flow.overset); 190 191 if (flowContainer.flowView) 192 flowContainer.flowView.flow = flow; 193 }, 194 195 /** 196 * @param {?WebInspector.NamedFlowCollection} namedFlowCollection 197 */ 198 _resetNamedFlows: function(namedFlowCollection) 199 { 200 for (var flowHash in this._namedFlows) 201 this._removeNamedFlow(flowHash); 202 203 var namedFlows = namedFlowCollection ? namedFlowCollection.namedFlowMap : {}; 204 for (var flowName in namedFlows) 205 this._appendNamedFlow(namedFlows[flowName]); 206 207 if (!this._flowTree.children.length) 208 this._setSidebarHasContent(false); 209 else 210 this._showNamedFlowForNode(WebInspector.panel("elements").treeOutline.selectedDOMNode()); 211 }, 212 213 /** 214 * @param {!WebInspector.Event} event 215 */ 216 _namedFlowCreated: function(event) 217 { 218 // FIXME: We only have support for Named Flows in the main document. 219 if (event.data.documentNodeId !== this._document.id) 220 return; 221 222 var flow = /** @type {!WebInspector.NamedFlow} */ (event.data); 223 this._appendNamedFlow(flow); 224 }, 225 226 /** 227 * @param {!WebInspector.Event} event 228 */ 229 _namedFlowRemoved: function(event) 230 { 231 // FIXME: We only have support for Named Flows in the main document. 232 if (event.data.documentNodeId !== this._document.id) 233 return; 234 235 this._removeNamedFlow(this._hashNamedFlow(event.data.documentNodeId, event.data.flowName)); 236 }, 237 238 /** 239 * @param {!WebInspector.Event} event 240 */ 241 _regionLayoutUpdated: function(event) 242 { 243 // FIXME: We only have support for Named Flows in the main document. 244 if (event.data.documentNodeId !== this._document.id) 245 return; 246 247 var flow = /** @type {!WebInspector.NamedFlow} */ (event.data); 248 this._updateNamedFlow(flow); 249 }, 250 251 /** 252 * @param {!WebInspector.Event} event 253 */ 254 _regionOversetChanged: function(event) 255 { 256 // FIXME: We only have support for Named Flows in the main document. 257 if (event.data.documentNodeId !== this._document.id) 258 return; 259 260 var flow = /** @type {!WebInspector.NamedFlow} */ (event.data); 261 this._updateNamedFlow(flow); 262 }, 263 264 /** 265 * @param {!DOMAgent.NodeId} documentNodeId 266 * @param {string} flowName 267 */ 268 _hashNamedFlow: function(documentNodeId, flowName) 269 { 270 return documentNodeId + "|" + flowName; 271 }, 272 273 /** 274 * @param {string} flowHash 275 */ 276 _showNamedFlow: function(flowHash) 277 { 278 this._selectNamedFlowInSidebar(flowHash); 279 this._selectNamedFlowTab(flowHash); 280 }, 281 282 /** 283 * @param {string} flowHash 284 */ 285 _selectNamedFlowInSidebar: function(flowHash) 286 { 287 this._namedFlows[flowHash].flowTreeItem.select(true); 288 }, 289 290 /** 291 * @param {string} flowHash 292 * @return {boolean} 293 */ 294 _selectNamedFlowTab: function(flowHash) 295 { 296 var flowContainer = this._namedFlows[flowHash]; 297 298 if (this._tabbedPane.selectedTabId === flowHash) 299 return false; 300 301 if (!this._tabbedPane.selectTab(flowHash)) { 302 if (!flowContainer.flowView) 303 flowContainer.flowView = new WebInspector.CSSNamedFlowView(flowContainer.flow); 304 305 this._tabbedPane.appendTab(flowHash, flowContainer.flow.name, flowContainer.flowView); 306 this._tabbedPane.selectTab(flowHash); 307 } 308 return false; 309 }, 310 311 /** 312 * @param {!WebInspector.Event} event 313 */ 314 _selectedNodeChanged: function(event) 315 { 316 var node = /** @type {!WebInspector.DOMNode} */ (event.data); 317 this._showNamedFlowForNode(node); 318 }, 319 320 /** 321 * @param {!WebInspector.Event} event 322 */ 323 _tabSelected: function(event) 324 { 325 this._selectNamedFlowInSidebar(event.data.tabId); 326 }, 327 328 /** 329 * @param {!WebInspector.Event} event 330 */ 331 _tabClosed: function(event) 332 { 333 this._namedFlows[event.data.tabId].flowTreeItem.deselect(); 334 }, 335 336 /** 337 * @param {?WebInspector.DOMNode} node 338 */ 339 _showNamedFlowForNode: function(node) 340 { 341 if (!node) 342 return; 343 344 if (this._regionNodes[node.id]) { 345 this._showNamedFlow(this._regionNodes[node.id]); 346 return; 347 } 348 349 while (node) { 350 if (this._contentNodes[node.id]) { 351 this._showNamedFlow(this._contentNodes[node.id]); 352 return; 353 } 354 355 node = node.parentNode; 356 } 357 }, 358 359 wasShown: function() 360 { 361 WebInspector.SidebarView.prototype.wasShown.call(this); 362 363 WebInspector.domAgent.requestDocument(this._setDocument.bind(this)); 364 365 WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdated, this); 366 367 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.NamedFlowCreated, this._namedFlowCreated, this); 368 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, this._namedFlowRemoved, this); 369 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, this._regionLayoutUpdated, this); 370 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.RegionOversetChanged, this._regionOversetChanged, this); 371 372 WebInspector.panel("elements").treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this); 373 374 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); 375 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this); 376 }, 377 378 willHide: function() 379 { 380 WebInspector.domAgent.removeEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdated, this); 381 382 WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.NamedFlowCreated, this._namedFlowCreated, this); 383 WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, this._namedFlowRemoved, this); 384 WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, this._regionLayoutUpdated, this); 385 WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.RegionOversetChanged, this._regionOversetChanged, this); 386 387 WebInspector.panel("elements").treeOutline.removeEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this); 388 389 this._tabbedPane.removeEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); 390 this._tabbedPane.removeEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this); 391 }, 392 393 __proto__: WebInspector.SidebarView.prototype 394 } 395 396 /** 397 * @constructor 398 * @extends {TreeElement} 399 */ 400 WebInspector.FlowTreeElement = function(flowContainer) 401 { 402 var container = document.createElement("div"); 403 container.createChild("div", "selection"); 404 container.createChild("span", "title").createChild("span").textContent = flowContainer.flow.name; 405 406 TreeElement.call(this, container, flowContainer, false); 407 408 this._overset = false; 409 this.setOverset(flowContainer.flow.overset); 410 } 411 412 WebInspector.FlowTreeElement.prototype = { 413 /** 414 * @param {boolean} newOverset 415 */ 416 setOverset: function(newOverset) 417 { 418 if (this._overset === newOverset) 419 return; 420 421 if (newOverset) { 422 this.title.classList.add("named-flow-overflow"); 423 this.tooltip = WebInspector.UIString("Overflows."); 424 } else { 425 this.title.classList.remove("named-flow-overflow"); 426 this.tooltip = ""; 427 } 428 429 this._overset = newOverset; 430 }, 431 432 __proto__: TreeElement.prototype 433 } 434