• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
34WebInspector.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
66WebInspector.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 */
400WebInspector.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
412WebInspector.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