2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * @param {!WebInspector.InspectorView} inspectorView
34 WebInspector.Drawer = function(inspectorView)
36 this._inspectorView = inspectorView;
38 this.element = this._inspectorView.devtoolsElement().createChild("div", "drawer");
39 this.element.style.flexBasis = 0;
41 this._savedHeight = 200; // Default.
43 this._drawerContentsElement = this.element.createChild("div");
44 this._drawerContentsElement.id = "drawer-contents";
46 this._toggleDrawerButton = new WebInspector.StatusBarButton(WebInspector.UIString("Show drawer."), "console-status-bar-item");
47 this._toggleDrawerButton.addEventListener("click", this.toggle, this);
49 this._tabbedPane = new WebInspector.TabbedPane();
50 this._tabbedPane.closeableTabs = false;
51 this._tabbedPane.markAsRoot();
52 this._tabbedPane.setRetainTabOrder(true, WebInspector.moduleManager.orderComparator(WebInspector.Drawer.ViewFactory, "name", "order"));
54 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._updateTabStrip, this);
55 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
56 WebInspector.installDragHandle(this._tabbedPane.headerElement(), this._startStatusBarDragging.bind(this), this._statusBarDragging.bind(this), this._endStatusBarDragging.bind(this), "ns-resize");
57 this._tabbedPane.element.createChild("div", "drawer-resizer");
58 this._showDrawerOnLoadSetting = WebInspector.settings.createSetting("WebInspector.Drawer.showOnLoad", false);
59 this._lastSelectedViewSetting = WebInspector.settings.createSetting("WebInspector.Drawer.lastSelectedView", "console");
63 WebInspector.Drawer.prototype = {
64 _initialize: function()
66 this._viewFactories = {};
67 var extensions = WebInspector.moduleManager.extensions(WebInspector.Drawer.ViewFactory);
69 for (var i = 0; i < extensions.length; ++i) {
70 var descriptor = extensions[i].descriptor();
71 var id = descriptor["name"];
72 var title = WebInspector.UIString(descriptor["title"]);
73 var settingName = descriptor["setting"];
74 var setting = settingName ? /** @type {!WebInspector.Setting|undefined} */ (WebInspector.settings[settingName]) : null;
76 this._viewFactories[id] = extensions[i];
79 setting.addChangeListener(this._toggleSettingBasedView.bind(this, id, title, setting));
81 this._tabbedPane.appendTab(id, title, new WebInspector.View());
83 this._tabbedPane.appendTab(id, title, new WebInspector.View());
90 * @param {string} title
91 * @param {!WebInspector.Setting} setting
93 _toggleSettingBasedView: function(id, title, setting)
95 this._tabbedPane.closeTab(id);
97 this._tabbedPane.appendTab(id, title, new WebInspector.View());
103 toggleButtonElement: function()
105 return this._toggleDrawerButton.element;
108 _constrainHeight: function(height)
110 return Number.constrain(height, Preferences.minConsoleHeight, this._inspectorView.devtoolsElement().offsetHeight - Preferences.minConsoleHeight);
118 return this._isHiding;
122 * @param {string} tabId
123 * @param {string} title
124 * @param {!WebInspector.View} view
126 _addView: function(tabId, title, view)
128 if (!this._tabbedPane.hasTab(tabId)) {
129 this._tabbedPane.appendTab(tabId, title, view, undefined, false);
131 this._tabbedPane.changeTabTitle(tabId, title);
132 this._tabbedPane.changeTabView(tabId, view);
139 closeView: function(id)
141 this._tabbedPane.closeTab(id);
146 * @param {boolean=} immediately
148 showView: function(id, immediately)
150 if (!this._toggleDrawerButton.enabled())
152 var viewFactory = this._viewFactory(id);
154 this._tabbedPane.changeTabView(id, viewFactory.createView());
155 this._innerShow(immediately);
156 this._tabbedPane.selectTab(id, true);
157 // In case this id is already selected, anyways persist it as the last saved value.
158 this._lastSelectedViewSetting.set(id);
159 this._updateTabStrip();
164 * @param {string} title
165 * @param {!WebInspector.View} view
167 showCloseableView: function(id, title, view)
169 if (!this._toggleDrawerButton.enabled())
171 if (!this._tabbedPane.hasTab(id)) {
172 this._tabbedPane.appendTab(id, title, view, undefined, false, true);
174 this._tabbedPane.changeTabView(id, view);
175 this._tabbedPane.changeTabTitle(id, title);
178 this._tabbedPane.selectTab(id, true);
179 this._updateTabStrip();
183 * @param {boolean=} immediately
185 show: function(immediately)
187 this.showView(this._lastSelectedViewSetting.get(), immediately);
190 showOnLoadIfNecessary: function()
192 if (this._showDrawerOnLoadSetting.get())
193 this.showView(this._lastSelectedViewSetting.get(), true);
197 * @param {boolean=} immediately
199 _innerShow: function(immediately)
201 this._immediatelyFinishAnimation();
203 if (this._toggleDrawerButton.toggled)
205 this._showDrawerOnLoadSetting.set(true);
206 this._toggleDrawerButton.toggled = true;
207 this._toggleDrawerButton.title = WebInspector.UIString("Hide drawer.");
209 document.body.classList.add("drawer-visible");
210 this._tabbedPane.show(this._drawerContentsElement);
212 var height = this._constrainHeight(this._savedHeight);
213 // While loading, window may be zero height. Do not corrupt desired drawer height in this case.
214 // FIXME: making Drawer a view and placing it inside SplitView eliminates the need for this.
215 if (window.innerHeight == 0)
216 height = this._savedHeight;
218 {element: this.element, start: {"flex-basis": 23}, end: {"flex-basis": height}},
222 * @param {boolean} finished
223 * @this {WebInspector.Drawer}
225 function animationCallback(finished)
227 if (this._inspectorView.currentPanel())
228 this._inspectorView.currentPanel().doResize();
231 this._updateTabStrip();
232 if (this._visibleView()) {
233 // Get console content back
234 this._tabbedPane.changeTabView(this._tabbedPane.selectedTabId, this._visibleView());
235 this._visibleView().focus();
237 delete this._currentAnimation;
240 this._currentAnimation = WebInspector.animateStyle(animations, this._animationDuration(immediately), animationCallback.bind(this));
243 this._currentAnimation.forceComplete();
247 * @param {boolean=} immediately
249 hide: function(immediately)
251 this._immediatelyFinishAnimation();
253 if (!this._toggleDrawerButton.toggled)
255 this._showDrawerOnLoadSetting.set(false);
256 this._toggleDrawerButton.toggled = false;
257 this._toggleDrawerButton.title = WebInspector.UIString("Show console.");
259 this._isHiding = true;
260 this._savedHeight = this.element.offsetHeight;
262 WebInspector.restoreFocusFromElement(this.element);
264 // Temporarily set properties and classes to mimic the post-animation values so panels
265 // like Elements in their updateStatusBarItems call will size things to fit the final location.
266 document.body.classList.remove("drawer-visible");
267 this._inspectorView.currentPanel().statusBarResized();
268 document.body.classList.add("drawer-visible");
271 {element: this.element, start: {"flex-basis": this.element.offsetHeight }, end: {"flex-basis": 23}},
275 * @param {boolean} finished
276 * @this {WebInspector.Drawer}
278 function animationCallback(finished)
280 var panel = this._inspectorView.currentPanel();
285 this._tabbedPane.detach();
286 this._drawerContentsElement.removeChildren();
287 document.body.classList.remove("drawer-visible");
289 delete this._currentAnimation;
290 delete this._isHiding;
293 this._currentAnimation = WebInspector.animateStyle(animations, this._animationDuration(immediately), animationCallback.bind(this));
296 this._currentAnimation.forceComplete();
301 if (!this._toggleDrawerButton.toggled)
304 this._visibleView().storeScrollPositions();
305 var height = this._constrainHeight(this.element.offsetHeight);
306 this.element.style.flexBasis = height + "px";
307 this._tabbedPane.doResize();
310 _immediatelyFinishAnimation: function()
312 if (this._currentAnimation)
313 this._currentAnimation.forceComplete();
317 * @param {boolean=} immediately
320 _animationDuration: function(immediately)
322 return immediately ? 0 : 50;
328 _startStatusBarDragging: function(event)
330 if (!this._toggleDrawerButton.toggled || event.target !== this._tabbedPane.headerElement())
333 this._visibleView().storeScrollPositions();
334 this._statusBarDragOffset = event.pageY - this.element.totalOffsetTop();
338 _statusBarDragging: function(event)
340 var height = window.innerHeight - event.pageY + this._statusBarDragOffset;
341 height = Number.constrain(height, Preferences.minConsoleHeight, this._inspectorView.devtoolsElement().offsetHeight - Preferences.minConsoleHeight);
343 this.element.style.flexBasis = height + "px";
344 if (this._inspectorView.currentPanel())
345 this._inspectorView.currentPanel().doResize();
346 this._tabbedPane.doResize();
351 _endStatusBarDragging: function(event)
353 this._savedHeight = this.element.offsetHeight;
354 delete this._statusBarDragOffset;
360 * @return {!WebInspector.View} view
362 _visibleView: function()
364 return this._tabbedPane.visibleView;
367 _updateTabStrip: function()
369 this._tabbedPane.onResize();
370 this._tabbedPane.doResize();
374 * @param {!WebInspector.Event} event
376 _tabSelected: function(event)
378 var tabId = this._tabbedPane.selectedTabId;
379 if (event.data["isUserGesture"] && !this._tabbedPane.isTabCloseable(tabId))
380 this._lastSelectedViewSetting.set(tabId);
381 var viewFactory = this._viewFactory(tabId);
383 this._tabbedPane.changeTabView(tabId, viewFactory.createView());
388 if (this._toggleDrawerButton.toggled)
399 return this._toggleDrawerButton.toggled;
405 selectedViewId: function()
407 return this._tabbedPane.selectedTabId;
411 * @return {?WebInspector.Drawer.ViewFactory}
413 _viewFactory: function(id)
415 return this._viewFactories[id] ? /** @type {!WebInspector.Drawer.ViewFactory} */ (this._viewFactories[id].instance()) : null;
422 WebInspector.Drawer.ViewFactory = function()
426 WebInspector.Drawer.ViewFactory.prototype = {
428 * @return {!WebInspector.View}
430 createView: function() {}
435 * @implements {WebInspector.Drawer.ViewFactory}
436 * @param {function(new:T)} constructor
439 WebInspector.Drawer.SingletonViewFactory = function(constructor)
441 this._constructor = constructor;
444 WebInspector.Drawer.SingletonViewFactory.prototype = {
446 * @return {!WebInspector.View}
448 createView: function()
451 this._instance = /** @type {!WebInspector.View} */(new this._constructor());
452 return this._instance;