1 /* 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2009 Joseph Pecoraro 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 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. 17 * 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. 28 */ 29 30 /** 31 * @constructor 32 */ 33 WebInspector.Drawer = function() 34 { 35 this.element = document.getElementById("drawer"); 36 this.element.style.height = 0; 37 38 this._savedHeight = 200; // Default. 39 this._mainElement = document.getElementById("main"); 40 this._toolbarElement = document.getElementById("toolbar"); 41 42 this._floatingStatusBarContainer = document.getElementById("floating-status-bar-container"); 43 WebInspector.installDragHandle(this._floatingStatusBarContainer, this._startStatusBarDragging.bind(this), this._statusBarDragging.bind(this), this._endStatusBarDragging.bind(this), "row-resize"); 44 45 this._drawerBodyElement = this.element.createChild("div"); 46 this._drawerBodyElement.id = "drawer-body"; 47 48 this._drawerContentsElement = this._drawerBodyElement.createChild("div"); 49 this._drawerContentsElement.id = "drawer-contents"; 50 51 this._footerElementContainer = this._drawerBodyElement.createChild("div", "status-bar hidden"); 52 this._footerElementContainer.id = "drawer-footer"; 53 54 this._viewStatusBar = document.createElement("div"); 55 this._viewStatusBar.style.opacity = 0; 56 this._bottomStatusBar = document.getElementById("bottom-status-bar-container"); 57 58 var drawerIsOverlay = WebInspector.experimentsSettings.drawerOverlay.isEnabled(); 59 this._elementToAdjust = drawerIsOverlay ? this._floatingStatusBarContainer : this._mainElement; 60 61 document.body.enableStyleClass("drawer-overlay", drawerIsOverlay); 62 } 63 64 WebInspector.Drawer.AnimationType = { 65 Immediately: 0, 66 Normal: 1, 67 Slow: 2 68 } 69 70 WebInspector.Drawer.prototype = { 71 get visible() 72 { 73 return !!this._view; 74 }, 75 76 _constrainHeight: function(height) 77 { 78 return Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - this._mainElement.totalOffsetTop() - Preferences.minConsoleHeight); 79 }, 80 81 show: function(view, animationType) 82 { 83 WebInspector.searchController.cancelSearch(); 84 this.immediatelyFinishAnimation(); 85 86 var drawerWasVisible = this.visible; 87 88 if (this._view) { 89 this._view.detach(); 90 this._drawerContentsElement.removeChildren(); 91 } 92 93 this._view = view; 94 95 var statusBarItems = this._view.statusBarItems || []; 96 this._viewStatusBar.removeChildren(); 97 for (var i = 0; i < statusBarItems.length; ++i) 98 this._viewStatusBar.appendChild(statusBarItems[i]); 99 100 document.body.addStyleClass("drawer-visible"); 101 this._floatingStatusBarContainer.insertBefore(document.getElementById("panel-status-bar"), this._floatingStatusBarContainer.firstElementChild); 102 this._bottomStatusBar.appendChild(this._viewStatusBar); 103 this._view.detach(); 104 this._view.markAsRoot(); 105 this._view.show(this._drawerContentsElement); 106 107 if (drawerWasVisible) 108 return; 109 110 var height = this._constrainHeight(this._savedHeight || this.element.offsetHeight); 111 var animations = [ 112 {element: this.element, end: {height: height}}, 113 {element: this._floatingStatusBarContainer, start: {"padding-left": this._bottomStatusBar.offsetLeft}, end: {"padding-left": 0}}, 114 {element: this._viewStatusBar, start: {opacity: 0}, end: {opacity: 1}}, 115 {element: this._elementToAdjust, start: {bottom: 0}, end: {bottom: height}} 116 ]; 117 118 function animationCallback(finished) 119 { 120 if (WebInspector.inspectorView.currentPanel()) 121 WebInspector.inspectorView.currentPanel().doResize(); 122 if (!finished) 123 return; 124 if (this._view && this._view.afterShow) 125 this._view.afterShow(); 126 delete this._currentAnimation; 127 } 128 129 this._currentAnimation = WebInspector.animateStyle(animations, this._animationDuration(animationType), animationCallback.bind(this)); 130 131 if (animationType === WebInspector.Drawer.AnimationType.Immediately) 132 this._currentAnimation.forceComplete(); 133 }, 134 135 hide: function(animationType) 136 { 137 WebInspector.searchController.cancelSearch(); 138 this.immediatelyFinishAnimation(); 139 if (!this.visible) 140 return; 141 142 this._savedHeight = this.element.offsetHeight; 143 144 WebInspector.restoreFocusFromElement(this.element); 145 146 // Temporarily set properties and classes to mimic the post-animation values so panels 147 // like Elements in their updateStatusBarItems call will size things to fit the final location. 148 document.body.removeStyleClass("drawer-visible"); 149 WebInspector.inspectorView.currentPanel().statusBarResized(); 150 document.body.addStyleClass("drawer-visible"); 151 152 var animations = [ 153 {element: this.element, end: {height: 0}}, 154 {element: this._floatingStatusBarContainer, start: {"padding-left": 0}, end: {"padding-left": this._bottomStatusBar.offsetLeft} }, 155 {element: this._viewStatusBar, start: {opacity: 1}, end: {opacity: 0}}, 156 {element: this._elementToAdjust, end: {bottom: 0}} 157 ]; 158 159 function animationCallback(finished) 160 { 161 if (WebInspector.inspectorView.currentPanel()) 162 WebInspector.inspectorView.currentPanel().doResize(); 163 if (!finished) 164 return; 165 this._view.detach(); 166 delete this._view; 167 this._bottomStatusBar.removeChildren(); 168 this._bottomStatusBar.appendChild(document.getElementById("panel-status-bar")); 169 this._drawerContentsElement.removeChildren(); 170 document.body.removeStyleClass("drawer-visible"); 171 delete this._currentAnimation; 172 this._elementToAdjust.style.bottom = 0; 173 } 174 175 this._currentAnimation = WebInspector.animateStyle(animations, this._animationDuration(animationType), animationCallback.bind(this)); 176 177 if (animationType === WebInspector.Drawer.AnimationType.Immediately) 178 this._currentAnimation.forceComplete(); 179 }, 180 181 resize: function() 182 { 183 if (!this.visible) 184 return; 185 186 this._view.storeScrollPositions(); 187 var height = this._constrainHeight(parseInt(this.element.style.height, 10)); 188 this._elementToAdjust.style.bottom = height + "px"; 189 this.element.style.height = height + "px"; 190 this._view.doResize(); 191 }, 192 193 immediatelyFinishAnimation: function() 194 { 195 if (this._currentAnimation) 196 this._currentAnimation.forceComplete(); 197 }, 198 199 _animationDuration: function(animationType) 200 { 201 switch (animationType) { 202 case WebInspector.Drawer.AnimationType.Slow: 203 return 2000; 204 case WebInspector.Drawer.AnimationType.Normal: 205 return 100; 206 default: 207 return 0; 208 } 209 }, 210 211 /** 212 * @return {boolean} 213 */ 214 _startStatusBarDragging: function(event) 215 { 216 if (!this.visible || event.target !== this._floatingStatusBarContainer) 217 return false; 218 219 this._view.storeScrollPositions(); 220 this._statusBarDragOffset = event.pageY - this.element.totalOffsetTop(); 221 return true; 222 }, 223 224 _statusBarDragging: function(event) 225 { 226 var height = window.innerHeight - event.pageY + this._statusBarDragOffset; 227 height = Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - this._mainElement.totalOffsetTop() - Preferences.minConsoleHeight); 228 229 this._elementToAdjust.style.bottom = height + "px"; 230 this.element.style.height = height + "px"; 231 if (WebInspector.inspectorView.currentPanel()) 232 WebInspector.inspectorView.currentPanel().doResize(); 233 this._view.doResize(); 234 235 event.consume(true); 236 }, 237 238 _endStatusBarDragging: function(event) 239 { 240 this._savedHeight = this.element.offsetHeight; 241 delete this._statusBarDragOffset; 242 243 event.consume(); 244 }, 245 246 /** 247 * @param {Element} element 248 */ 249 setFooterElement: function(element) 250 { 251 if (element) { 252 this._footerElementContainer.removeStyleClass("hidden"); 253 this._footerElementContainer.appendChild(element); 254 this._drawerContentsElement.style.bottom = this._footerElementContainer.offsetHeight + "px"; 255 } else { 256 this._footerElementContainer.addStyleClass("hidden"); 257 this._footerElementContainer.removeChildren(); 258 this._drawerContentsElement.style.bottom = 0; 259 } 260 this._view.doResize(); 261 }, 262 263 /** 264 * @returns {WebInspector.Searchable} 265 */ 266 getSearchProvider: function() 267 { 268 if (this._view && this._view.performSearch) 269 return this._view; 270 271 return null; 272 } 273 } 274 275 /** 276 * @type {WebInspector.Drawer} 277 */ 278 WebInspector.drawer = null; 279