• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2007, 2008 Apple Inc.  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 copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29WebInspector.Panel = function()
30{
31    WebInspector.View.call(this);
32
33    this.element.addStyleClass("panel");
34}
35
36WebInspector.Panel.prototype = {
37    get toolbarItem()
38    {
39        if (this._toolbarItem)
40            return this._toolbarItem;
41
42        // Sample toolbar item as markup:
43        // <button class="toolbar-item resources toggleable">
44        // <div class="toolbar-icon"></div>
45        // <div class="toolbar-label">Resources</div>
46        // </button>
47
48        this._toolbarItem = document.createElement("button");
49        this._toolbarItem.className = "toolbar-item toggleable";
50        this._toolbarItem.panel = this;
51
52        if ("toolbarItemClass" in this)
53            this._toolbarItem.addStyleClass(this.toolbarItemClass);
54
55        var iconElement = document.createElement("div");
56        iconElement.className = "toolbar-icon";
57        this._toolbarItem.appendChild(iconElement);
58
59        if ("toolbarItemLabel" in this) {
60            var labelElement = document.createElement("div");
61            labelElement.className = "toolbar-label";
62            labelElement.textContent = this.toolbarItemLabel;
63            this._toolbarItem.appendChild(labelElement);
64        }
65
66        return this._toolbarItem;
67    },
68
69    show: function()
70    {
71        WebInspector.View.prototype.show.call(this);
72
73        var statusBarItems = this.statusBarItems;
74        if (statusBarItems) {
75            this._statusBarItemContainer = document.createElement("div");
76            for (var i = 0; i < statusBarItems.length; ++i)
77                this._statusBarItemContainer.appendChild(statusBarItems[i]);
78            document.getElementById("main-status-bar").appendChild(this._statusBarItemContainer);
79        }
80
81        if ("_toolbarItem" in this)
82            this._toolbarItem.addStyleClass("toggled-on");
83
84        WebInspector.currentFocusElement = this.defaultFocusedElement;
85
86        this.updateSidebarWidth();
87    },
88
89    hide: function()
90    {
91        WebInspector.View.prototype.hide.call(this);
92
93        if (this._statusBarItemContainer && this._statusBarItemContainer.parentNode)
94            this._statusBarItemContainer.parentNode.removeChild(this._statusBarItemContainer);
95        delete this._statusBarItemContainer;
96        if ("_toolbarItem" in this)
97            this._toolbarItem.removeStyleClass("toggled-on");
98    },
99
100    get defaultFocusedElement()
101    {
102        return this.sidebarTreeElement || this.element;
103    },
104
105    attach: function()
106    {
107        if (!this.element.parentNode)
108            document.getElementById("main-panels").appendChild(this.element);
109    },
110
111    searchCanceled: function(startingNewSearch)
112    {
113        if (this._searchResults) {
114            for (var i = 0; i < this._searchResults.length; ++i) {
115                var view = this._searchResults[i];
116                if (view.searchCanceled)
117                    view.searchCanceled();
118                delete view.currentQuery;
119            }
120        }
121
122        WebInspector.updateSearchMatchesCount(0, this);
123
124        if (this._currentSearchChunkIntervalIdentifier) {
125            clearInterval(this._currentSearchChunkIntervalIdentifier);
126            delete this._currentSearchChunkIntervalIdentifier;
127        }
128
129        this._totalSearchMatches = 0;
130        this._currentSearchResultIndex = 0;
131        this._searchResults = [];
132    },
133
134    performSearch: function(query)
135    {
136        // Call searchCanceled since it will reset everything we need before doing a new search.
137        this.searchCanceled(true);
138
139        var searchableViews = this.searchableViews;
140        if (!searchableViews || !searchableViews.length)
141            return;
142
143        var parentElement = this.viewsContainerElement;
144        var visibleView = this.visibleView;
145        var sortFuction = this.searchResultsSortFunction;
146
147        var matchesCountUpdateTimeout = null;
148
149        function updateMatchesCount()
150        {
151            WebInspector.updateSearchMatchesCount(this._totalSearchMatches, this);
152            matchesCountUpdateTimeout = null;
153        }
154
155        function updateMatchesCountSoon()
156        {
157            if (matchesCountUpdateTimeout)
158                return;
159            // Update the matches count every half-second so it doesn't feel twitchy.
160            matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500);
161        }
162
163        function finishedCallback(view, searchMatches)
164        {
165            if (!searchMatches)
166                return;
167
168            this._totalSearchMatches += searchMatches;
169            this._searchResults.push(view);
170
171            if (sortFuction)
172                this._searchResults.sort(sortFuction);
173
174            if (this.searchMatchFound)
175                this.searchMatchFound(view, searchMatches);
176
177            updateMatchesCountSoon.call(this);
178
179            if (view === visibleView)
180                view.jumpToFirstSearchResult();
181        }
182
183        var i = 0;
184        var panel = this;
185        var boundFinishedCallback = finishedCallback.bind(this);
186        var chunkIntervalIdentifier = null;
187
188        // Split up the work into chunks so we don't block the
189        // UI thread while processing.
190
191        function processChunk()
192        {
193            var view = searchableViews[i];
194
195            if (++i >= searchableViews.length) {
196                if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)
197                    delete panel._currentSearchChunkIntervalIdentifier;
198                clearInterval(chunkIntervalIdentifier);
199            }
200
201            if (!view)
202                return;
203
204            if (view.element.parentNode !== parentElement && view.element.parentNode && parentElement)
205                view.detach();
206
207            view.currentQuery = query;
208            view.performSearch(query, boundFinishedCallback);
209        }
210
211        processChunk();
212
213        chunkIntervalIdentifier = setInterval(processChunk, 25);
214        this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;
215    },
216
217    jumpToNextSearchResult: function()
218    {
219        if (!this.showView || !this._searchResults || !this._searchResults.length)
220            return;
221
222        var showFirstResult = false;
223
224        this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
225        if (this._currentSearchResultIndex === -1) {
226            this._currentSearchResultIndex = 0;
227            showFirstResult = true;
228        }
229
230        var currentView = this._searchResults[this._currentSearchResultIndex];
231
232        if (currentView.showingLastSearchResult()) {
233            if (++this._currentSearchResultIndex >= this._searchResults.length)
234                this._currentSearchResultIndex = 0;
235            currentView = this._searchResults[this._currentSearchResultIndex];
236            showFirstResult = true;
237        }
238
239        if (currentView !== this.visibleView) {
240            currentView = this.visibleView;
241            this._currentSearchResultIndex = 0;
242            showFirstResult = true;
243        }
244
245        if (showFirstResult)
246            currentView.jumpToFirstSearchResult();
247        else
248            currentView.jumpToNextSearchResult();
249    },
250
251    jumpToPreviousSearchResult: function()
252    {
253        if (!this.showView || !this._searchResults || !this._searchResults.length)
254            return;
255
256        var showLastResult = false;
257
258        this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
259        if (this._currentSearchResultIndex === -1) {
260            this._currentSearchResultIndex = 0;
261            showLastResult = true;
262        }
263
264        var currentView = this._searchResults[this._currentSearchResultIndex];
265
266        if (currentView.showingFirstSearchResult()) {
267            if (--this._currentSearchResultIndex < 0)
268                this._currentSearchResultIndex = (this._searchResults.length - 1);
269            currentView = this._searchResults[this._currentSearchResultIndex];
270            showLastResult = true;
271        }
272
273        if (currentView !== this.visibleView)
274            this.showView(currentView);
275
276        if (showLastResult)
277            currentView.jumpToLastSearchResult();
278        else
279            currentView.jumpToPreviousSearchResult();
280    },
281
282    createSidebar: function(parentElement, resizerParentElement)
283    {
284        if (this.hasSidebar)
285            return;
286
287        if (!parentElement)
288            parentElement = this.element;
289
290        if (!resizerParentElement)
291            resizerParentElement = parentElement;
292
293        this.hasSidebar = true;
294
295        this.sidebarElement = document.createElement("div");
296        this.sidebarElement.className = "sidebar";
297        parentElement.appendChild(this.sidebarElement);
298
299        this.sidebarResizeElement = document.createElement("div");
300        this.sidebarResizeElement.className = "sidebar-resizer-vertical";
301        this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false);
302        resizerParentElement.appendChild(this.sidebarResizeElement);
303
304        this.sidebarTreeElement = document.createElement("ol");
305        this.sidebarTreeElement.className = "sidebar-tree";
306        this.sidebarElement.appendChild(this.sidebarTreeElement);
307
308        this.sidebarTree = new TreeOutline(this.sidebarTreeElement);
309    },
310
311    _startSidebarDragging: function(event)
312    {
313        WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize");
314    },
315
316    _sidebarDragging: function(event)
317    {
318        this.updateSidebarWidth(event.pageX);
319
320        event.preventDefault();
321    },
322
323    _endSidebarDragging: function(event)
324    {
325        WebInspector.elementDragEnd(event);
326    },
327
328    updateSidebarWidth: function(width)
329    {
330        if (!this.hasSidebar)
331            return;
332
333        if (this.sidebarElement.offsetWidth <= 0) {
334            // The stylesheet hasn't loaded yet or the window is closed,
335            // so we can't calculate what is need. Return early.
336            return;
337        }
338
339        if (!("_currentSidebarWidth" in this))
340            this._currentSidebarWidth = this.sidebarElement.offsetWidth;
341
342        if (typeof width === "undefined")
343            width = this._currentSidebarWidth;
344
345        width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2);
346
347        this._currentSidebarWidth = width;
348        this.setSidebarWidth(width);
349
350        this.updateMainViewWidth(width);
351    },
352
353    setSidebarWidth: function(width)
354    {
355        this.sidebarElement.style.width = width + "px";
356        this.sidebarResizeElement.style.left = (width - 3) + "px";
357    },
358
359    updateMainViewWidth: function(width)
360    {
361        // Should be implemented by ancestors.
362    },
363
364    resize: function()
365    {
366        var visibleView = this.visibleView;
367        if (visibleView && "resize" in visibleView)
368            visibleView.resize();
369    },
370
371    canShowSourceLineForURL: function(url)
372    {
373        return false;
374    },
375
376    showSourceLineForURL: function(url, line)
377    {
378        return false;
379    }
380}
381
382WebInspector.Panel.prototype.__proto__ = WebInspector.View.prototype;
383