1/*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @constructor
33 * @extends {WebInspector.Object}
34 * @param {!Element} element
35 */
36WebInspector.StatusBarItem = function(element)
37{
38    this.element = element;
39    this._enabled = true;
40}
41
42WebInspector.StatusBarItem.prototype = {
43    /**
44     * @param {boolean} value
45     */
46    setEnabled: function(value)
47    {
48        if (this._enabled === value)
49            return;
50        this._enabled = value;
51        this._applyEnabledState();
52    },
53
54    /**
55     * @protected
56     */
57    _applyEnabledState: function()
58    {
59        this.element.disabled = !this._enabled;
60    },
61
62    __proto__: WebInspector.Object.prototype
63}
64
65/**
66 * @constructor
67 * @extends {WebInspector.StatusBarItem}
68 * @param {string} title
69 * @param {string} className
70 * @param {number=} states
71 */
72WebInspector.StatusBarButton = function(title, className, states)
73{
74    WebInspector.StatusBarItem.call(this, document.createElement("button"));
75    this.element.className = className + " status-bar-item";
76    this.element.addEventListener("click", this._clicked.bind(this), false);
77
78    this.glyph = document.createElement("div");
79    this.glyph.className = "glyph";
80    this.element.appendChild(this.glyph);
81
82    this.glyphShadow = document.createElement("div");
83    this.glyphShadow.className = "glyph shadow";
84    this.element.appendChild(this.glyphShadow);
85
86    this.states = states;
87    if (!states)
88        this.states = 2;
89
90    if (states == 2)
91        this._state = false;
92    else
93        this._state = 0;
94
95    this.title = title;
96    this.className = className;
97    this._visible = true;
98}
99
100WebInspector.StatusBarButton.prototype = {
101    _clicked: function()
102    {
103        this.dispatchEventToListeners("click");
104        if (this._showOptionsTimer)
105            clearTimeout(this._showOptionsTimer);
106    },
107
108    /**
109     * @return {boolean}
110     */
111    enabled: function()
112    {
113        return this._enabled;
114    },
115
116    get title()
117    {
118        return this._title;
119    },
120
121    set title(x)
122    {
123        if (this._title === x)
124            return;
125        this._title = x;
126        this.element.title = x;
127    },
128
129    get state()
130    {
131        return this._state;
132    },
133
134    set state(x)
135    {
136        if (this._state === x)
137            return;
138
139        if (this.states === 2)
140            this.element.enableStyleClass("toggled-on", x);
141        else {
142            this.element.removeStyleClass("toggled-" + this._state);
143            if (x !== 0)
144                this.element.addStyleClass("toggled-" + x);
145        }
146        this._state = x;
147    },
148
149    get toggled()
150    {
151        if (this.states !== 2)
152            throw("Only used toggled when there are 2 states, otherwise, use state");
153        return this.state;
154    },
155
156    set toggled(x)
157    {
158        if (this.states !== 2)
159            throw("Only used toggled when there are 2 states, otherwise, use state");
160        this.state = x;
161    },
162
163    get visible()
164    {
165        return this._visible;
166    },
167
168    set visible(x)
169    {
170        if (this._visible === x)
171            return;
172
173        this.element.enableStyleClass("hidden", !x);
174        this._visible = x;
175    },
176
177    /**
178     * @param {function():Array.<WebInspector.StatusBarButton>} buttonsProvider
179     */
180    makeLongClickEnabled: function(buttonsProvider)
181    {
182        this.longClickGlyph = document.createElement("div");
183        this.longClickGlyph.className = "fill long-click-glyph";
184        this.element.appendChild(this.longClickGlyph);
185
186        this.longClickGlyphShadow = document.createElement("div");
187        this.longClickGlyphShadow.className = "fill long-click-glyph shadow";
188        this.element.appendChild(this.longClickGlyphShadow);
189
190        this.element.addEventListener("mousedown", mouseDown.bind(this), false);
191        this.element.addEventListener("mouseout", mouseUp.bind(this), false);
192        this.element.addEventListener("mouseup", mouseUp.bind(this), false);
193
194        function mouseDown(e)
195        {
196            if (e.which !== 1)
197                return;
198            this._showOptionsTimer = setTimeout(this._showOptions.bind(this, buttonsProvider), 200);
199        }
200
201        function mouseUp(e)
202        {
203            if (e.which !== 1)
204                return;
205            if (this._showOptionsTimer)
206                clearTimeout(this._showOptionsTimer);
207        }
208    },
209
210    /**
211     * @param {function():Array.<WebInspector.StatusBarButton>} buttonsProvider
212     */
213    _showOptions: function(buttonsProvider)
214    {
215        var buttons = buttonsProvider();
216        var mainButtonClone = new WebInspector.StatusBarButton(this.title, this.className, this.states);
217        mainButtonClone.addEventListener("click", this._clicked, this);
218        mainButtonClone.state = this.state;
219        buttons.push(mainButtonClone);
220
221        var mouseUpListener = mouseUp.bind(this);
222        document.documentElement.addEventListener("mouseup", mouseUpListener, false);
223
224        var optionsGlassPane = new WebInspector.GlassPane();
225        var optionsBarElement = optionsGlassPane.element.createChild("div", "alternate-status-bar-buttons-bar");
226        const buttonHeight = 24;
227        optionsBarElement.style.height = (buttonHeight * buttons.length) + "px";
228        optionsBarElement.style.left = (this.element.offsetLeft + 1) + "px";
229
230        var boundMouseOver = mouseOver.bind(this);
231        var boundMouseOut = mouseOut.bind(this);
232        for (var i = 0; i < buttons.length; ++i) {
233            buttons[i].element.addEventListener("mousemove", boundMouseOver, false);
234            buttons[i].element.addEventListener("mouseout", boundMouseOut, false);
235            optionsBarElement.appendChild(buttons[i].element);
236        }
237        buttons[buttons.length - 1].element.addStyleClass("emulate-active");
238
239        function mouseOver(e)
240        {
241            if (e.which !== 1)
242                return;
243            var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
244            buttonElement.addStyleClass("emulate-active");
245        }
246
247        function mouseOut(e)
248        {
249            if (e.which !== 1)
250                return;
251            var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
252            buttonElement.removeStyleClass("emulate-active");
253        }
254
255        function mouseUp(e)
256        {
257            if (e.which !== 1)
258                return;
259            optionsGlassPane.dispose();
260            document.documentElement.removeEventListener("mouseup", mouseUpListener, false);
261
262            for (var i = 0; i < buttons.length; ++i) {
263                if (buttons[i].element.hasStyleClass("emulate-active"))
264                    buttons[i]._clicked();
265            }
266        }
267    },
268
269    __proto__: WebInspector.StatusBarItem.prototype
270}
271
272/**
273 * @constructor
274 * @extends {WebInspector.StatusBarItem}
275 * @param {?function(Event)} changeHandler
276 * @param {string=} className
277 */
278WebInspector.StatusBarComboBox = function(changeHandler, className)
279{
280    WebInspector.StatusBarItem.call(this, document.createElement("span"));
281    this.element.className = "status-bar-select-container";
282
283    this._selectElement = this.element.createChild("select", "status-bar-item");
284    if (changeHandler)
285        this._selectElement.addEventListener("change", changeHandler, false);
286    if (className)
287        this._selectElement.addStyleClass(className);
288}
289
290WebInspector.StatusBarComboBox.prototype = {
291    /**
292     * @return {number}
293     */
294    size: function()
295    {
296        return this._selectElement.childElementCount;
297    },
298
299    /**
300     * @param {!Element} option
301     */
302    addOption: function(option)
303    {
304        this._selectElement.appendChild(option);
305    },
306
307    /**
308     * @param {string} label
309     * @param {string=} title
310     * @param {string=} value
311     * @return {!Element}
312     */
313    createOption: function(label, title, value)
314    {
315        var option = this._selectElement.createChild("option");
316        option.text = label;
317        if (title)
318            option.title = title;
319        if (typeof value !== "undefined")
320            option.value = value;
321        return option;
322    },
323
324    /**
325     * @override
326     */
327    _applyEnabledState: function()
328    {
329        this._selectElement.disabled = !this._enabled;
330    },
331
332    /**
333     * @param {!Element} option
334     */
335    removeOption: function(option)
336    {
337        this._selectElement.removeChild(option);
338    },
339
340    removeOptions: function()
341    {
342        this._selectElement.removeChildren();
343    },
344
345    /**
346     * @return {?Element}
347     */
348    selectedOption: function()
349    {
350        if (this._selectElement.selectedIndex >= 0)
351            return this._selectElement[this._selectElement.selectedIndex];
352        return null;
353    },
354
355    /**
356     * @param {Element} option
357     */
358    select: function(option)
359    {
360        this._selectElement.selectedIndex = Array.prototype.indexOf.call(this._selectElement, option);
361    },
362
363    __proto__: WebInspector.StatusBarItem.prototype
364}
365