• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {string} elementType
35 */
36WebInspector.StatusBarItem = function(elementType)
37{
38    this.element = document.createElement(elementType);
39    this._enabled = true;
40    this._visible = true;
41}
42
43WebInspector.StatusBarItem.prototype = {
44    /**
45     * @param {boolean} value
46     */
47    setEnabled: function(value)
48    {
49        if (this._enabled === value)
50            return;
51        this._enabled = value;
52        this._applyEnabledState();
53    },
54
55    /**
56     * @protected
57     */
58    _applyEnabledState: function()
59    {
60        this.element.disabled = !this._enabled;
61    },
62
63    get visible()
64    {
65        return this._visible;
66    },
67
68    set visible(x)
69    {
70        if (this._visible === x)
71            return;
72        this.element.classList.toggle("hidden", !x);
73        this._visible = x;
74    },
75
76    __proto__: WebInspector.Object.prototype
77}
78
79/**
80 * @constructor
81 * @extends {WebInspector.StatusBarItem}
82 * @param {string} text
83 * @param {string=} className
84 */
85WebInspector.StatusBarText = function(text, className)
86{
87    WebInspector.StatusBarItem.call(this, "span");
88    this.element.className = "status-bar-item status-bar-text";
89    if (className)
90        this.element.classList.add(className);
91    this.element.textContent = text;
92}
93
94WebInspector.StatusBarText.prototype = {
95    /**
96     * @param {string} text
97     */
98    setText: function(text)
99    {
100        this.element.textContent = text;
101    },
102
103    __proto__: WebInspector.StatusBarItem.prototype
104}
105
106/**
107 * @constructor
108 * @extends {WebInspector.StatusBarItem}
109 * @param {string=} placeholder
110 * @param {number=} width
111 */
112WebInspector.StatusBarInput = function(placeholder, width)
113{
114    WebInspector.StatusBarItem.call(this, "input");
115    this.element.className = "status-bar-item";
116    this.element.addEventListener("input", this._onChangeCallback.bind(this), false);
117    if (width)
118        this.element.style.width = width + "px";
119    if (placeholder)
120        this.element.setAttribute("placeholder", placeholder);
121    this._value = "";
122}
123
124WebInspector.StatusBarInput.Event = {
125    TextChanged: "TextChanged"
126};
127
128WebInspector.StatusBarInput.prototype = {
129    /**
130     * @param {string} value
131     */
132    setValue: function(value)
133    {
134        this._value = value;
135        this.element.value = value;
136    },
137
138    /**
139     * @return {string}
140     */
141    value: function()
142    {
143        return this.element.value;
144    },
145
146    _onChangeCallback: function()
147    {
148        this.dispatchEventToListeners(WebInspector.StatusBarInput.Event.TextChanged, this.element.value);
149    },
150
151    __proto__: WebInspector.StatusBarItem.prototype
152}
153
154/**
155 * @constructor
156 * @extends {WebInspector.StatusBarItem}
157 * @param {string} title
158 * @param {string} className
159 * @param {number=} states
160 */
161WebInspector.StatusBarButton = function(title, className, states)
162{
163    WebInspector.StatusBarItem.call(this, "button");
164    this.element.className = className + " status-bar-item";
165    this.element.addEventListener("click", this._clicked.bind(this), false);
166
167    this.glyph = document.createElement("div");
168    this.glyph.className = "glyph";
169    this.element.appendChild(this.glyph);
170
171    this.glyphShadow = document.createElement("div");
172    this.glyphShadow.className = "glyph shadow";
173    this.element.appendChild(this.glyphShadow);
174
175    this.states = states;
176    if (!states)
177        this.states = 2;
178
179    if (states == 2)
180        this._state = false;
181    else
182        this._state = 0;
183
184    this.title = title;
185    this.className = className;
186}
187
188WebInspector.StatusBarButton.prototype = {
189    _clicked: function()
190    {
191        this.dispatchEventToListeners("click");
192        if (this._longClickInterval) {
193            clearInterval(this._longClickInterval);
194            delete this._longClickInterval;
195        }
196    },
197
198    /**
199     * @override
200     */
201    _applyEnabledState: function()
202    {
203        this.element.disabled = !this._enabled;
204        if (this._longClickInterval) {
205            clearInterval(this._longClickInterval);
206            delete this._longClickInterval;
207        }
208    },
209
210    /**
211     * @return {boolean}
212     */
213    enabled: function()
214    {
215        return this._enabled;
216    },
217
218    get title()
219    {
220        return this._title;
221    },
222
223    set title(x)
224    {
225        if (this._title === x)
226            return;
227        this._title = x;
228        this.element.title = x;
229    },
230
231    get state()
232    {
233        return this._state;
234    },
235
236    set state(x)
237    {
238        if (this._state === x)
239            return;
240
241        if (this.states === 2)
242            this.element.classList.toggle("toggled-on", x);
243        else {
244            this.element.classList.remove("toggled-" + this._state);
245            if (x !== 0)
246                this.element.classList.add("toggled-" + x);
247        }
248        this._state = x;
249    },
250
251    get toggled()
252    {
253        if (this.states !== 2)
254            throw("Only used toggled when there are 2 states, otherwise, use state");
255        return this.state;
256    },
257
258    set toggled(x)
259    {
260        if (this.states !== 2)
261            throw("Only used toggled when there are 2 states, otherwise, use state");
262        this.state = x;
263    },
264
265    makeLongClickEnabled: function()
266    {
267        var boundMouseDown = mouseDown.bind(this);
268        var boundMouseUp = mouseUp.bind(this);
269
270        this.element.addEventListener("mousedown", boundMouseDown, false);
271        this.element.addEventListener("mouseout", boundMouseUp, false);
272        this.element.addEventListener("mouseup", boundMouseUp, false);
273
274        var longClicks = 0;
275
276        this._longClickData = { mouseUp: boundMouseUp, mouseDown: boundMouseDown };
277
278        /**
279         * @param {?Event} e
280         * @this {WebInspector.StatusBarButton}
281         */
282        function mouseDown(e)
283        {
284            if (e.which !== 1)
285                return;
286            longClicks = 0;
287            this._longClickInterval = setInterval(longClicked.bind(this), 200);
288        }
289
290        /**
291         * @param {?Event} e
292         * @this {WebInspector.StatusBarButton}
293         */
294        function mouseUp(e)
295        {
296            if (e.which !== 1)
297                return;
298            if (this._longClickInterval) {
299                clearInterval(this._longClickInterval);
300                delete this._longClickInterval;
301            }
302        }
303
304        /**
305         * @this {WebInspector.StatusBarButton}
306         */
307        function longClicked()
308        {
309            ++longClicks;
310            this.dispatchEventToListeners(longClicks === 1 ? "longClickDown" : "longClickPress");
311        }
312    },
313
314    unmakeLongClickEnabled: function()
315    {
316        if (!this._longClickData)
317            return;
318        this.element.removeEventListener("mousedown", this._longClickData.mouseDown, false);
319        this.element.removeEventListener("mouseout", this._longClickData.mouseUp, false);
320        this.element.removeEventListener("mouseup", this._longClickData.mouseUp, false);
321        delete this._longClickData;
322    },
323
324    /**
325     * @param {?function():!Array.<!WebInspector.StatusBarButton>} buttonsProvider
326     */
327    setLongClickOptionsEnabled: function(buttonsProvider)
328    {
329        if (buttonsProvider) {
330            if (!this._longClickOptionsData) {
331                this.makeLongClickEnabled();
332
333                this.longClickGlyph = document.createElement("div");
334                this.longClickGlyph.className = "fill long-click-glyph";
335                this.element.appendChild(this.longClickGlyph);
336
337                this.longClickGlyphShadow = document.createElement("div");
338                this.longClickGlyphShadow.className = "fill long-click-glyph shadow";
339                this.element.appendChild(this.longClickGlyphShadow);
340
341                var longClickDownListener = this._showOptions.bind(this);
342                this.addEventListener("longClickDown", longClickDownListener, this);
343
344                this._longClickOptionsData = {
345                    glyphElement: this.longClickGlyph,
346                    glyphShadowElement: this.longClickGlyphShadow,
347                    longClickDownListener: longClickDownListener
348                };
349            }
350            this._longClickOptionsData.buttonsProvider = buttonsProvider;
351        } else {
352            if (!this._longClickOptionsData)
353                return;
354            this.element.removeChild(this._longClickOptionsData.glyphElement);
355            this.element.removeChild(this._longClickOptionsData.glyphShadowElement);
356
357            this.removeEventListener("longClickDown", this._longClickOptionsData.longClickDownListener, this);
358            delete this._longClickOptionsData;
359
360            this.unmakeLongClickEnabled();
361        }
362    },
363
364    _showOptions: function()
365    {
366        var buttons = this._longClickOptionsData.buttonsProvider();
367        var mainButtonClone = new WebInspector.StatusBarButton(this.title, this.className, this.states);
368        mainButtonClone.addEventListener("click", this._clicked, this);
369        mainButtonClone.state = this.state;
370        buttons.push(mainButtonClone);
371
372        document.documentElement.addEventListener("mouseup", mouseUp, false);
373
374        var optionsGlassPane = new WebInspector.GlassPane();
375        var optionsBarElement = optionsGlassPane.element.createChild("div", "alternate-status-bar-buttons-bar");
376        const buttonHeight = 23;
377
378        var hostButtonPosition = this.element.totalOffset();
379
380        var topNotBottom = hostButtonPosition.top + buttonHeight * buttons.length < document.documentElement.offsetHeight;
381
382        if (topNotBottom)
383            buttons = buttons.reverse();
384
385        optionsBarElement.style.height = (buttonHeight * buttons.length) + "px";
386        if (topNotBottom)
387            optionsBarElement.style.top = (hostButtonPosition.top + 1) + "px";
388        else
389            optionsBarElement.style.top = (hostButtonPosition.top - (buttonHeight * (buttons.length - 1))) + "px";
390        optionsBarElement.style.left = (hostButtonPosition.left + 1) + "px";
391
392        for (var i = 0; i < buttons.length; ++i) {
393            buttons[i].element.addEventListener("mousemove", mouseOver, false);
394            buttons[i].element.addEventListener("mouseout", mouseOut, false);
395            optionsBarElement.appendChild(buttons[i].element);
396        }
397        var hostButtonIndex = topNotBottom ? 0 : buttons.length - 1;
398        buttons[hostButtonIndex].element.classList.add("emulate-active");
399
400        function mouseOver(e)
401        {
402            if (e.which !== 1)
403                return;
404            var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
405            buttonElement.classList.add("emulate-active");
406        }
407
408        function mouseOut(e)
409        {
410            if (e.which !== 1)
411                return;
412            var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
413            buttonElement.classList.remove("emulate-active");
414        }
415
416        function mouseUp(e)
417        {
418            if (e.which !== 1)
419                return;
420            optionsGlassPane.dispose();
421            document.documentElement.removeEventListener("mouseup", mouseUp, false);
422
423            for (var i = 0; i < buttons.length; ++i) {
424                if (buttons[i].element.classList.contains("emulate-active")) {
425                    buttons[i].element.classList.remove("emulate-active");
426                    buttons[i]._clicked();
427                    break;
428                }
429            }
430        }
431    },
432
433    __proto__: WebInspector.StatusBarItem.prototype
434}
435
436/**
437 * @interface
438 */
439WebInspector.StatusBarButton.Provider = function()
440{
441}
442
443WebInspector.StatusBarButton.Provider.prototype = {
444    /**
445     * @return {?WebInspector.StatusBarButton}
446     */
447    button: function() {}
448}
449
450/**
451 * @constructor
452 * @extends {WebInspector.StatusBarItem}
453 * @param {?function(?Event)} changeHandler
454 * @param {string=} className
455 */
456WebInspector.StatusBarComboBox = function(changeHandler, className)
457{
458    WebInspector.StatusBarItem.call(this, "span");
459    this.element.className = "status-bar-select-container";
460
461    this._selectElement = this.element.createChild("select", "status-bar-item");
462    this.element.createChild("div", "status-bar-select-arrow");
463    if (changeHandler)
464        this._selectElement.addEventListener("change", changeHandler, false);
465    if (className)
466        this._selectElement.classList.add(className);
467}
468
469WebInspector.StatusBarComboBox.prototype = {
470    /**
471     * @return {!Element}
472     */
473    selectElement: function()
474    {
475        return this._selectElement;
476    },
477
478    /**
479     * @return {number}
480     */
481    size: function()
482    {
483        return this._selectElement.childElementCount;
484    },
485
486    /**
487     * @param {!Element} option
488     */
489    addOption: function(option)
490    {
491        this._selectElement.appendChild(option);
492    },
493
494    /**
495     * @param {string} label
496     * @param {string=} title
497     * @param {string=} value
498     * @return {!Element}
499     */
500    createOption: function(label, title, value)
501    {
502        var option = this._selectElement.createChild("option");
503        option.text = label;
504        if (title)
505            option.title = title;
506        if (typeof value !== "undefined")
507            option.value = value;
508        return option;
509    },
510
511    /**
512     * @override
513     */
514    _applyEnabledState: function()
515    {
516        this._selectElement.disabled = !this._enabled;
517    },
518
519    /**
520     * @param {!Element} option
521     */
522    removeOption: function(option)
523    {
524        this._selectElement.removeChild(option);
525    },
526
527    removeOptions: function()
528    {
529        this._selectElement.removeChildren();
530    },
531
532    /**
533     * @return {?Element}
534     */
535    selectedOption: function()
536    {
537        if (this._selectElement.selectedIndex >= 0)
538            return this._selectElement[this._selectElement.selectedIndex];
539        return null;
540    },
541
542    /**
543     * @param {!Element} option
544     */
545    select: function(option)
546    {
547        this._selectElement.selectedIndex = Array.prototype.indexOf.call(/** @type {?} */ (this._selectElement), option);
548    },
549
550    /**
551     * @param {number} index
552     */
553    setSelectedIndex: function(index)
554    {
555        this._selectElement.selectedIndex = index;
556    },
557
558    /**
559     * @return {number}
560     */
561    selectedIndex: function()
562    {
563        return this._selectElement.selectedIndex;
564    },
565
566    __proto__: WebInspector.StatusBarItem.prototype
567}
568
569/**
570 * @constructor
571 * @extends {WebInspector.StatusBarItem}
572 * @param {string} title
573 */
574WebInspector.StatusBarCheckbox = function(title)
575{
576    WebInspector.StatusBarItem.call(this, "label");
577    this.element.classList.add("status-bar-item", "checkbox");
578    this.inputElement = this.element.createChild("input");
579    this.inputElement.type = "checkbox";
580    this.element.createTextChild(title);
581}
582
583WebInspector.StatusBarCheckbox.prototype = {
584    /**
585     * @return {boolean}
586     */
587    checked: function()
588    {
589        return this.inputElement.checked;
590    },
591
592    __proto__: WebInspector.StatusBarItem.prototype
593}
594
595/**
596 * @constructor
597 * @extends {WebInspector.StatusBarButton}
598 * @param {string} className
599 * @param {!Array.<string>} states
600 * @param {!Array.<string>} titles
601 * @param {string} initialState
602 * @param {!WebInspector.Setting} currentStateSetting
603 * @param {!WebInspector.Setting} lastStateSetting
604 * @param {?function(string)} stateChangedCallback
605 */
606WebInspector.StatusBarStatesSettingButton = function(className, states, titles, initialState, currentStateSetting, lastStateSetting, stateChangedCallback)
607{
608    WebInspector.StatusBarButton.call(this, "", className, states.length);
609
610    var onClickBound = this._onClick.bind(this);
611    this.addEventListener("click", onClickBound, this);
612
613    this._states = states;
614    this._buttons = [];
615    for (var index = 0; index < states.length; index++) {
616        var button = new WebInspector.StatusBarButton(titles[index], className, states.length);
617        button.state = this._states[index];
618        button.addEventListener("click", onClickBound, this);
619        this._buttons.push(button);
620    }
621
622    this._currentStateSetting = currentStateSetting;
623    this._lastStateSetting = lastStateSetting;
624    this._stateChangedCallback = stateChangedCallback;
625    this.setLongClickOptionsEnabled(this._createOptions.bind(this));
626
627    this._currentState = null;
628    this.toggleState(initialState);
629}
630
631WebInspector.StatusBarStatesSettingButton.prototype = {
632    /**
633     * @param {!WebInspector.Event} e
634     */
635    _onClick: function(e)
636    {
637        this.toggleState(e.target.state);
638    },
639
640    /**
641     * @param {string} state
642     */
643    toggleState: function(state)
644    {
645        if (this._currentState === state)
646            return;
647
648        if (this._currentState)
649            this._lastStateSetting.set(this._currentState);
650        this._currentState = state;
651        this._currentStateSetting.set(this._currentState);
652
653        if (this._stateChangedCallback)
654            this._stateChangedCallback(state);
655
656        var defaultState = this._defaultState();
657        this.state = defaultState;
658        this.title = this._buttons[this._states.indexOf(defaultState)].title;
659    },
660
661    /**
662     * @return {string}
663     */
664    _defaultState: function()
665    {
666        var lastState = this._lastStateSetting.get();
667        if (lastState && this._states.indexOf(lastState) >= 0 && lastState != this._currentState)
668            return lastState;
669        if (this._states.length > 1 && this._currentState === this._states[0])
670            return this._states[1];
671        return this._states[0];
672    },
673
674    /**
675     * @return {!Array.<!WebInspector.StatusBarButton>}
676     */
677    _createOptions: function()
678    {
679        var options = [];
680        for (var index = 0; index < this._states.length; index++) {
681            if (this._states[index] !== this.state && this._states[index] !== this._currentState)
682                options.push(this._buttons[index]);
683        }
684        return options;
685    },
686
687    __proto__: WebInspector.StatusBarButton.prototype
688}
689