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 */
33WebInspector.Drawer = function()
34{
35    this.element = document.getElementById("drawer");
36    this._savedHeight = 200; // Default.
37    this._mainElement = document.getElementById("main");
38    this._toolbarElement = document.getElementById("toolbar");
39
40    this._floatingStatusBarContainer = document.getElementById("floating-status-bar-container");
41    WebInspector.installDragHandle(this._floatingStatusBarContainer, this._startStatusBarDragging.bind(this), this._statusBarDragging.bind(this), this._endStatusBarDragging.bind(this), "row-resize");
42
43    this._drawerContentsElement = document.createElement("div");
44    this._drawerContentsElement.id = "drawer-contents";
45    this._drawerContentsElement.className = "drawer-contents";
46    this.element.appendChild(this._drawerContentsElement);
47    this._viewStatusBar = document.createElement("div");
48    this._viewStatusBar.addEventListener("webkitTransitionEnd", this.immediatelyFinishAnimation.bind(this), false);
49    this._viewStatusBar.style.opacity = 0;
50    this._bottomStatusBar = document.getElementById("bottom-status-bar-container");
51}
52
53WebInspector.Drawer.AnimationType = {
54        Immediately: 0,
55        Normal: 1,
56        Slow: 2
57}
58
59WebInspector.Drawer.prototype = {
60    get visible()
61    {
62        return !!this._view;
63    },
64
65    _constrainHeight: function(height)
66    {
67        return Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - this._mainElement.totalOffsetTop() - Preferences.minConsoleHeight);
68    },
69
70    show: function(view, animationType)
71    {
72        this.immediatelyFinishAnimation();
73
74        var drawerWasVisible = this.visible;
75
76        if (this._view) {
77            this._view.detach();
78            this._drawerContentsElement.removeChildren();
79        }
80
81        this._view = view;
82
83        var statusBarItems = this._view.statusBarItems() || [];
84        this._viewStatusBar.removeChildren();
85        for (var i = 0; i < statusBarItems.length; ++i)
86            this._viewStatusBar.appendChild(statusBarItems[i]);
87
88        document.body.addStyleClass("drawer-visible");
89        this._floatingStatusBarContainer.insertBefore(document.getElementById("panel-status-bar"), this._floatingStatusBarContainer.firstElementChild);
90        this._bottomStatusBar.appendChild(this._viewStatusBar);
91        this._view.detach();
92        this._view.markAsRoot();
93        this._view.show(this._drawerContentsElement);
94
95        if (drawerWasVisible)
96            return;
97
98        var height = this._constrainHeight(this._savedHeight || this.element.offsetHeight);
99
100        this._floatingStatusBarContainer.style.paddingLeft = this._bottomStatusBar.offsetLeft + "px";
101
102        this._getAnimationStyles(animationType).forEach(document.body.addStyleClass, document.body);
103
104        function animationFinished()
105        {
106            WebInspector.inspectorView.currentPanel().doResize();
107            if (this._view && this._view.afterShow)
108                this._view.afterShow();
109        }
110
111        this._animationFinished = animationFinished.bind(this);
112
113        // Assert that transition will be done and we receive transitionEnd event
114        console.assert(this._viewStatusBar.style.opacity === "0");
115
116        if (animationType === WebInspector.Drawer.AnimationType.Immediately)
117            this.immediatelyFinishAnimation();
118
119        this.element.style.height = height + "px";
120        this._mainElement.style.bottom = height + "px";
121        this._floatingStatusBarContainer.style.paddingLeft = 0;
122        this._viewStatusBar.style.opacity = 1;
123    },
124
125    hide: function(animationType)
126    {
127        this.immediatelyFinishAnimation();
128        if (!this.visible)
129            return;
130
131        this._savedHeight = this.element.offsetHeight;
132
133        WebInspector.restoreFocusFromElement(this.element);
134
135        // Temporarily set properties and classes to mimic the post-animation values so panels
136        // like Elements in their updateStatusBarItems call will size things to fit the final location.
137        document.body.removeStyleClass("drawer-visible");
138        WebInspector.inspectorView.currentPanel().statusBarResized();
139        document.body.addStyleClass("drawer-visible");
140
141        this._getAnimationStyles(animationType).forEach(document.body.addStyleClass, document.body);
142
143        function animationFinished()
144        {
145            WebInspector.inspectorView.currentPanel().doResize();
146            this._view.detach();
147            delete this._view;
148            this._bottomStatusBar.removeChildren();
149            this._bottomStatusBar.appendChild(document.getElementById("panel-status-bar"));
150            this._drawerContentsElement.removeChildren();
151            document.body.removeStyleClass("drawer-visible");
152        }
153
154        this._animationFinished = animationFinished.bind(this);
155
156        // Assert that transition will be done and we receive transitionEnd event
157        console.assert(this._viewStatusBar.style.opacity === "1");
158
159        if (animationType === WebInspector.Drawer.AnimationType.Immediately)
160            this.immediatelyFinishAnimation();
161
162        this.element.style.height = 0;
163        this._mainElement.style.bottom = 0;
164        this._floatingStatusBarContainer.style.paddingLeft = this._bottomStatusBar.offsetLeft + "px";
165        this._viewStatusBar.style.opacity = 0;
166    },
167
168    resize: function()
169    {
170        if (!this.visible)
171            return;
172
173        this._view.storeScrollPositions();
174        var height = this._constrainHeight(parseInt(this.element.style.height, 10));
175        this._mainElement.style.bottom = height + "px";
176        this.element.style.height = height + "px";
177        this._view.doResize();
178    },
179
180    immediatelyFinishAnimation: function()
181    {
182        document.body.removeStyleClass("animate");
183        document.body.removeStyleClass("animate-slow");
184        if (this._animationFinished) {
185            this._animationFinished();
186            delete this._animationFinished;
187        }
188    },
189
190    _getAnimationStyles: function(animationType)
191    {
192        switch (animationType) {
193        case WebInspector.Drawer.AnimationType.Slow:
194            return ["animate", "animate-slow"];
195        case WebInspector.Drawer.AnimationType.Normal:
196            return ["animate"];
197        default:
198            return [];
199        }
200    },
201
202    /**
203     * @return {boolean}
204     */
205    _startStatusBarDragging: function(event)
206    {
207        if (!this.visible || event.target !== this._floatingStatusBarContainer)
208            return false;
209
210        this._view.storeScrollPositions();
211        this._statusBarDragOffset = event.pageY - this.element.totalOffsetTop();
212        return true;
213    },
214
215    _statusBarDragging: function(event)
216    {
217        var height = window.innerHeight - event.pageY + this._statusBarDragOffset;
218        height = Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - this._mainElement.totalOffsetTop() - Preferences.minConsoleHeight);
219
220        this._mainElement.style.bottom = height + "px";
221        this.element.style.height = height + "px";
222        if (WebInspector.inspectorView.currentPanel())
223            WebInspector.inspectorView.currentPanel().doResize();
224        this._view.doResize();
225
226        event.consume(true);
227    },
228
229    _endStatusBarDragging: function(event)
230    {
231        this._savedHeight = this.element.offsetHeight;
232        delete this._statusBarDragOffset;
233
234        event.consume();
235    }
236}
237
238/**
239 * @type {WebInspector.Drawer}
240 */
241WebInspector.drawer = null;
242