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