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