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 WebInspector.Drawer = function() 31 { 32 WebInspector.View.call(this, document.getElementById("drawer")); 33 34 this._savedHeight = 200; // Default. 35 this.state = WebInspector.Drawer.State.Hidden; 36 this.fullPanel = false; 37 38 this.mainElement = document.getElementById("main"); 39 this.toolbarElement = document.getElementById("toolbar"); 40 this.mainStatusBar = document.getElementById("main-status-bar"); 41 this.mainStatusBar.addEventListener("mousedown", this._startStatusBarDragging.bind(this), true); 42 this.viewStatusBar = document.getElementById("other-drawer-status-bar-items"); 43 } 44 45 WebInspector.Drawer.prototype = { 46 get visibleView() 47 { 48 return this._visibleView; 49 }, 50 51 set visibleView(x) 52 { 53 if (this._visibleView === x) { 54 if (this.visible && this.fullPanel) 55 return; 56 this.visible = !this.visible; 57 return; 58 } 59 60 var firstTime = !this._visibleView; 61 if (this._visibleView) 62 this._visibleView.hide(); 63 64 this._visibleView = x; 65 66 if (x && !firstTime) { 67 this._safelyRemoveChildren(); 68 this.viewStatusBar.removeChildren(); // optimize this? call old.detach() 69 x.attach(this.element, this.viewStatusBar); 70 x.show(); 71 this.visible = true; 72 } 73 }, 74 75 get savedHeight() 76 { 77 var height = this._savedHeight || this.element.offsetHeight; 78 return Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - this.mainElement.totalOffsetTop - Preferences.minConsoleHeight); 79 }, 80 81 showView: function(view) 82 { 83 if (!this.visible || this.visibleView !== view) 84 this.visibleView = view; 85 }, 86 87 show: function() 88 { 89 if (this._animating || this.visible) 90 return; 91 92 if (this.visibleView) 93 this.visibleView.show(); 94 95 WebInspector.View.prototype.show.call(this); 96 97 this._animating = true; 98 99 document.body.addStyleClass("drawer-visible"); 100 101 var anchoredItems = document.getElementById("anchored-status-bar-items"); 102 var height = (this.fullPanel ? window.innerHeight - this.toolbarElement.offsetHeight : this.savedHeight); 103 var animations = [ 104 {element: this.element, end: {height: height}}, 105 {element: document.getElementById("main"), end: {bottom: height}}, 106 {element: document.getElementById("main-status-bar"), start: {"padding-left": anchoredItems.offsetWidth - 1}, end: {"padding-left": 0}}, 107 {element: document.getElementById("other-drawer-status-bar-items"), start: {opacity: 0}, end: {opacity: 1}} 108 ]; 109 110 var drawerStatusBar = document.getElementById("drawer-status-bar"); 111 drawerStatusBar.insertBefore(anchoredItems, drawerStatusBar.firstChild); 112 113 function animationFinished() 114 { 115 if ("updateStatusBarItems" in WebInspector.currentPanel) 116 WebInspector.currentPanel.updateStatusBarItems(); 117 if (this.visibleView.afterShow) 118 this.visibleView.afterShow(); 119 delete this._animating; 120 delete this._currentAnimationInterval; 121 this.state = (this.fullPanel ? WebInspector.Drawer.State.Full : WebInspector.Drawer.State.Variable); 122 } 123 124 this._currentAnimationInterval = WebInspector.animateStyle(animations, this._animationDuration(), animationFinished.bind(this)); 125 }, 126 127 hide: function() 128 { 129 if (this._animating || !this.visible) 130 return; 131 132 WebInspector.View.prototype.hide.call(this); 133 134 if (this.visibleView) 135 this.visibleView.hide(); 136 137 this._animating = true; 138 139 if (!this.fullPanel) 140 this._savedHeight = this.element.offsetHeight; 141 142 if (this.element === WebInspector.currentFocusElement || this.element.isAncestor(WebInspector.currentFocusElement)) 143 WebInspector.currentFocusElement = WebInspector.previousFocusElement; 144 145 var anchoredItems = document.getElementById("anchored-status-bar-items"); 146 147 // Temporarily set properties and classes to mimic the post-animation values so panels 148 // like Elements in their updateStatusBarItems call will size things to fit the final location. 149 this.mainStatusBar.style.setProperty("padding-left", (anchoredItems.offsetWidth - 1) + "px"); 150 document.body.removeStyleClass("drawer-visible"); 151 if ("updateStatusBarItems" in WebInspector.currentPanel) 152 WebInspector.currentPanel.updateStatusBarItems(); 153 document.body.addStyleClass("drawer-visible"); 154 155 var animations = [ 156 {element: document.getElementById("main"), end: {bottom: 0}}, 157 {element: document.getElementById("main-status-bar"), start: {"padding-left": 0}, end: {"padding-left": anchoredItems.offsetWidth - 1}}, 158 {element: document.getElementById("other-drawer-status-bar-items"), start: {opacity: 1}, end: {opacity: 0}} 159 ]; 160 161 function animationFinished() 162 { 163 WebInspector.currentPanel.resize(); 164 var mainStatusBar = document.getElementById("main-status-bar"); 165 mainStatusBar.insertBefore(anchoredItems, mainStatusBar.firstChild); 166 mainStatusBar.style.removeProperty("padding-left"); 167 document.body.removeStyleClass("drawer-visible"); 168 delete this._animating; 169 delete this._currentAnimationInterval; 170 this.state = WebInspector.Drawer.State.Hidden; 171 } 172 173 this._currentAnimationInterval = WebInspector.animateStyle(animations, this._animationDuration(), animationFinished.bind(this)); 174 }, 175 176 resize: function() 177 { 178 if (this.state === WebInspector.Drawer.State.Hidden) 179 return; 180 181 var height; 182 var mainElement = document.getElementById("main"); 183 if (this.state === WebInspector.Drawer.State.Variable) { 184 height = parseInt(this.element.style.height); 185 height = Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - mainElement.totalOffsetTop - Preferences.minConsoleHeight); 186 } else 187 height = window.innerHeight - this.toolbarElement.offsetHeight; 188 189 mainElement.style.bottom = height + "px"; 190 this.element.style.height = height + "px"; 191 }, 192 193 enterPanelMode: function() 194 { 195 this._cancelAnimationIfNeeded(); 196 this.fullPanel = true; 197 198 if (this.visible) { 199 this._savedHeight = this.element.offsetHeight; 200 var height = window.innerHeight - this.toolbarElement.offsetHeight; 201 this._animateDrawerHeight(height, WebInspector.Drawer.State.Full); 202 } 203 }, 204 205 exitPanelMode: function() 206 { 207 this._cancelAnimationIfNeeded(); 208 this.fullPanel = false; 209 210 if (this.visible) { 211 // If this animation gets cancelled, we want the state of the drawer to be Variable, 212 // so that the new animation can't do an immediate transition between Hidden/Full states. 213 this.state = WebInspector.Drawer.State.Variable; 214 var height = this.savedHeight; 215 this._animateDrawerHeight(height, WebInspector.Drawer.State.Variable); 216 } 217 }, 218 219 immediatelyExitPanelMode: function() 220 { 221 this.visible = false; 222 this.fullPanel = false; 223 }, 224 225 _cancelAnimationIfNeeded: function() 226 { 227 if (this._animating) { 228 clearInterval(this._currentAnimationInterval); 229 delete this._animating; 230 delete this._currentAnimationInterval; 231 } 232 }, 233 234 _animateDrawerHeight: function(height, finalState) 235 { 236 this._animating = true; 237 var animations = [ 238 {element: this.element, end: {height: height}}, 239 {element: document.getElementById("main"), end: {bottom: height}} 240 ]; 241 242 function animationFinished() 243 { 244 delete this._animating; 245 delete this._currentAnimationInterval; 246 this.state = finalState; 247 } 248 249 this._currentAnimationInterval = WebInspector.animateStyle(animations, this._animationDuration(), animationFinished.bind(this)); 250 }, 251 252 _animationDuration: function() 253 { 254 // Immediate if going between Hidden and Full in full panel mode 255 if (this.fullPanel && (this.state === WebInspector.Drawer.State.Hidden || this.state === WebInspector.Drawer.State.Full)) 256 return 0; 257 258 return (window.event && window.event.shiftKey ? 2000 : 250); 259 }, 260 261 _safelyRemoveChildren: function() 262 { 263 var child = this.element.firstChild; 264 while (child) { 265 if (child.id !== "drawer-status-bar") { 266 var moveTo = child.nextSibling; 267 this.element.removeChild(child); 268 child = moveTo; 269 } else 270 child = child.nextSibling; 271 } 272 }, 273 274 _startStatusBarDragging: function(event) 275 { 276 if (!this.visible || event.target !== this.mainStatusBar) 277 return; 278 279 WebInspector.elementDragStart(this.mainStatusBar, this._statusBarDragging.bind(this), this._endStatusBarDragging.bind(this), event, "row-resize"); 280 281 this._statusBarDragOffset = event.pageY - this.element.totalOffsetTop; 282 283 event.stopPropagation(); 284 }, 285 286 _statusBarDragging: function(event) 287 { 288 var mainElement = document.getElementById("main"); 289 var height = window.innerHeight - event.pageY + this._statusBarDragOffset; 290 height = Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - mainElement.totalOffsetTop - Preferences.minConsoleHeight); 291 292 mainElement.style.bottom = height + "px"; 293 this.element.style.height = height + "px"; 294 295 event.preventDefault(); 296 event.stopPropagation(); 297 }, 298 299 _endStatusBarDragging: function(event) 300 { 301 WebInspector.elementDragEnd(event); 302 303 this._savedHeight = this.element.offsetHeight; 304 delete this._statusBarDragOffset; 305 306 event.stopPropagation(); 307 } 308 } 309 310 WebInspector.Drawer.prototype.__proto__ = WebInspector.View.prototype; 311 312 WebInspector.Drawer.State = { 313 Hidden: 0, 314 Variable: 1, 315 Full: 2 316 }; 317