Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / ui / SplitView.js
1 /*
2  * Copyright (C) 2012 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  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20  * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /**
30  * @constructor
31  * @extends {WebInspector.View}
32  * @param {boolean} isVertical
33  * @param {boolean} secondIsSidebar
34  * @param {string=} settingName
35  * @param {number=} defaultSidebarWidth
36  * @param {number=} defaultSidebarHeight
37  */
38 WebInspector.SplitView = function(isVertical, secondIsSidebar, settingName, defaultSidebarWidth, defaultSidebarHeight)
39 {
40     WebInspector.View.call(this);
41
42     this.registerRequiredCSS("splitView.css");
43     this.element.classList.add("split-view");
44
45     this._mainView = new WebInspector.VBox();
46     this._mainElement = this._mainView.element;
47     this._mainElement.className = "split-view-contents scroll-target split-view-main vbox"; // Override
48
49     this._sidebarView = new WebInspector.VBox();
50     this._sidebarElement = this._sidebarView.element;
51     this._sidebarElement.className = "split-view-contents scroll-target split-view-sidebar vbox"; // Override
52
53     this._resizerElement = this.element.createChild("div", "split-view-resizer");
54     this._resizerElement.createChild("div", "split-view-resizer-border");
55     if (secondIsSidebar) {
56         this._mainView.show(this.element);
57         this._sidebarView.show(this.element);
58     } else {
59         this._sidebarView.show(this.element);
60         this._mainView.show(this.element);
61     }
62
63     this._resizerWidget = new WebInspector.ResizerWidget();
64     this._resizerWidget.setEnabled(true);
65     this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeStart, this._onResizeStart, this);
66     this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeUpdate, this._onResizeUpdate, this);
67     this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeEnd, this._onResizeEnd, this);
68
69     this._defaultSidebarWidth = defaultSidebarWidth || 200;
70     this._defaultSidebarHeight = defaultSidebarHeight || this._defaultSidebarWidth;
71     this._settingName = settingName;
72
73     this.setSecondIsSidebar(secondIsSidebar);
74
75     this._innerSetVertical(isVertical);
76     this._showMode = WebInspector.SplitView.ShowMode.Both;
77
78     // Should be called after isVertical has the right value.
79     this.installResizer(this._resizerElement);
80 }
81
82 /** @typedef {{showMode: string, size: number}} */
83 WebInspector.SplitView.SettingForOrientation;
84
85 WebInspector.SplitView.ShowMode = {
86     Both: "Both",
87     OnlyMain: "OnlyMain",
88     OnlySidebar: "OnlySidebar"
89 }
90
91 WebInspector.SplitView.Events = {
92     SidebarSizeChanged: "SidebarSizeChanged",
93     ShowModeChanged: "ShowModeChanged"
94 }
95
96 WebInspector.SplitView.MinPadding = 20;
97
98 WebInspector.SplitView.prototype = {
99     /**
100      * @return {boolean}
101      */
102     isVertical: function()
103     {
104         return this._isVertical;
105     },
106
107     /**
108      * @param {boolean} isVertical
109      */
110     setVertical: function(isVertical)
111     {
112         if (this._isVertical === isVertical)
113             return;
114
115         this._innerSetVertical(isVertical);
116
117         if (this.isShowing())
118             this._updateLayout();
119     },
120
121     /**
122      * @param {boolean} isVertical
123      */
124     _innerSetVertical: function(isVertical)
125     {
126         this.element.classList.remove(this._isVertical ? "hbox" : "vbox");
127         this._isVertical = isVertical;
128         this.element.classList.add(this._isVertical ? "hbox" : "vbox");
129         delete this._resizerElementSize;
130         this._sidebarSize = -1;
131         this._restoreSidebarSizeFromSettings();
132         if (this._shouldSaveShowMode)
133             this._restoreAndApplyShowModeFromSettings();
134         this._updateShowHideSidebarButton();
135         // FIXME: reverse SplitView.isVertical meaning.
136         this._resizerWidget.setVertical(!isVertical);
137         this.invalidateConstraints();
138     },
139
140     /**
141      * @param {boolean=} animate
142      */
143     _updateLayout: function(animate)
144     {
145         delete this._totalSize; // Lazy update.
146         delete this._totalSizeOtherDimension;
147
148         // Remove properties that might affect total size calculation.
149         this._mainElement.style.removeProperty("width");
150         this._mainElement.style.removeProperty("height");
151         this._sidebarElement.style.removeProperty("width");
152         this._sidebarElement.style.removeProperty("height");
153
154         this._innerSetSidebarSize(this._preferredSidebarSize(), !!animate);
155     },
156
157     /**
158      * @return {!Element}
159      */
160     mainElement: function()
161     {
162         return this._mainElement;
163     },
164
165     /**
166      * @return {!Element}
167      */
168     sidebarElement: function()
169     {
170         return this._sidebarElement;
171     },
172
173     /**
174      * @return {boolean}
175      */
176     isSidebarSecond: function()
177     {
178         return this._secondIsSidebar;
179     },
180
181     enableShowModeSaving: function()
182     {
183         this._shouldSaveShowMode = true;
184         this._restoreAndApplyShowModeFromSettings();
185     },
186
187     /**
188      * @return {string}
189      */
190     showMode: function()
191     {
192         return this._showMode;
193     },
194
195     /**
196      * @param {boolean} secondIsSidebar
197      */
198     setSecondIsSidebar: function(secondIsSidebar)
199     {
200         this._mainElement.classList.toggle("split-view-contents-first", secondIsSidebar);
201         this._mainElement.classList.toggle("split-view-contents-second", !secondIsSidebar);
202         this._sidebarElement.classList.toggle("split-view-contents-first", !secondIsSidebar);
203         this._sidebarElement.classList.toggle("split-view-contents-second", secondIsSidebar);
204
205         // Make sure second is last in the children array.
206         if (secondIsSidebar) {
207             if (this._sidebarElement.parentElement && this._sidebarElement.nextSibling)
208                 this.element.appendChild(this._sidebarElement);
209         } else {
210             if (this._mainElement.parentElement && this._mainElement.nextSibling)
211                 this.element.appendChild(this._mainElement);
212         }
213
214         this._secondIsSidebar = secondIsSidebar;
215     },
216
217     /**
218      * @return {?string}
219      */
220     sidebarSide: function()
221     {
222         if (this._showMode !== WebInspector.SplitView.ShowMode.Both)
223             return null;
224         return this._isVertical ?
225             (this._secondIsSidebar ? "right" : "left") :
226             (this._secondIsSidebar ? "bottom" : "top");
227     },
228
229     /**
230      * @return {number}
231      */
232     preferredSidebarSize: function()
233     {
234         return this._preferredSidebarSize();
235     },
236
237     /**
238      * @return {!Element}
239      */
240     resizerElement: function()
241     {
242         return this._resizerElement;
243     },
244
245     /**
246      * @param {boolean=} animate
247      */
248     hideMain: function(animate)
249     {
250         this._showOnly(this._sidebarView, this._mainView, animate);
251         this._updateShowMode(WebInspector.SplitView.ShowMode.OnlySidebar);
252     },
253
254     /**
255      * @param {boolean=} animate
256      */
257     hideSidebar: function(animate)
258     {
259         this._showOnly(this._mainView, this._sidebarView, animate);
260         this._updateShowMode(WebInspector.SplitView.ShowMode.OnlyMain);
261     },
262
263     /**
264      * @override
265      */
266     detachChildViews: function()
267     {
268         this._mainView.detachChildViews();
269         this._sidebarView.detachChildViews();
270     },
271
272     /**
273      * @param {!WebInspector.View} sideToShow
274      * @param {!WebInspector.View} sideToHide
275      * @param {boolean=} animate
276      */
277     _showOnly: function(sideToShow, sideToHide, animate)
278     {
279         this._cancelAnimation();
280
281         /**
282          * @this {WebInspector.SplitView}
283          */
284         function callback()
285         {
286             sideToShow.show(this.element);
287             sideToHide.detach();
288             sideToShow.element.classList.add("maximized");
289             sideToHide.element.classList.remove("maximized");
290             this._resizerElement.classList.add("hidden");
291             this._removeAllLayoutProperties();
292         }
293
294         if (animate) {
295             this._animate(true, callback.bind(this));
296         } else {
297             callback.call(this);
298             this.doResize();
299         }
300
301         this._sidebarSize = -1;
302         this.setResizable(false);
303     },
304
305     _removeAllLayoutProperties: function()
306     {
307         this._sidebarElement.style.removeProperty("flexBasis");
308
309         this._mainElement.style.removeProperty("width");
310         this._mainElement.style.removeProperty("height");
311         this._sidebarElement.style.removeProperty("width");
312         this._sidebarElement.style.removeProperty("height");
313
314         this._resizerElement.style.removeProperty("left");
315         this._resizerElement.style.removeProperty("right");
316         this._resizerElement.style.removeProperty("top");
317         this._resizerElement.style.removeProperty("bottom");
318
319         this._resizerElement.style.removeProperty("margin-left");
320         this._resizerElement.style.removeProperty("margin-right");
321         this._resizerElement.style.removeProperty("margin-top");
322         this._resizerElement.style.removeProperty("margin-bottom");
323     },
324
325     /**
326      * @param {boolean=} animate
327      */
328     showBoth: function(animate)
329     {
330        if (this._showMode === WebInspector.SplitView.ShowMode.Both)
331             animate = false;
332
333         this._cancelAnimation();
334         this._mainElement.classList.remove("maximized");
335         this._sidebarElement.classList.remove("maximized");
336         this._resizerElement.classList.remove("hidden");
337
338         this._mainView.show(this.element);
339         this._sidebarView.show(this.element);
340         // Order views in DOM properly.
341         this.setSecondIsSidebar(this._secondIsSidebar);
342
343         this._sidebarSize = -1;
344         this.setResizable(true);
345         this._updateShowMode(WebInspector.SplitView.ShowMode.Both);
346         this._updateLayout(animate);
347     },
348
349     /**
350      * @param {boolean} resizable
351      */
352     setResizable: function(resizable)
353     {
354         this._resizerWidget.setEnabled(resizable);
355     },
356
357     /**
358      * @param {number} size
359      */
360     setSidebarSize: function(size)
361     {
362         this._savedSidebarSize = size;
363         this._saveSetting();
364         this._innerSetSidebarSize(size, false, true);
365     },
366
367     /**
368      * @return {number}
369      */
370     sidebarSize: function()
371     {
372         return Math.max(0, this._sidebarSize);
373     },
374
375     /**
376      * Returns total size in DIP.
377      * @return {number}
378      */
379     _totalSizeDIP: function()
380     {
381         if (!this._totalSize) {
382             this._totalSize = this._isVertical ? this.element.offsetWidth : this.element.offsetHeight;
383             this._totalSizeOtherDimension = this._isVertical ? this.element.offsetHeight : this.element.offsetWidth;
384         }
385         return this._totalSize * WebInspector.zoomManager.zoomFactor();
386     },
387
388     /**
389      * @param {string} showMode
390      */
391     _updateShowMode: function(showMode)
392     {
393         this._showMode = showMode;
394         this._saveShowModeToSettings();
395         this._updateShowHideSidebarButton();
396         this.dispatchEventToListeners(WebInspector.SplitView.Events.ShowModeChanged, showMode);
397         this.invalidateConstraints();
398     },
399
400     /**
401      * @param {number} size
402      * @param {boolean} animate
403      * @param {boolean=} userAction
404      */
405     _innerSetSidebarSize: function(size, animate, userAction)
406     {
407         if (this._showMode !== WebInspector.SplitView.ShowMode.Both || !this.isShowing())
408             return;
409
410         size = this._applyConstraints(size, userAction);
411         if (this._sidebarSize === size)
412             return;
413
414         if (!this._resizerElementSize)
415             this._resizerElementSize = this._isVertical ? this._resizerElement.offsetWidth : this._resizerElement.offsetHeight;
416
417         // Invalidate layout below.
418
419         this._removeAllLayoutProperties();
420
421         // this._totalSize is available below since we successfully applied constraints.
422         var sidebarSizeValue = (size / WebInspector.zoomManager.zoomFactor()) + "px";
423         var mainSizeValue = (this._totalSize - size / WebInspector.zoomManager.zoomFactor()) + "px";
424         this.sidebarElement().style.flexBasis = sidebarSizeValue;
425
426         // Make both sides relayout boundaries.
427         if (this._isVertical) {
428             this._sidebarElement.style.width = sidebarSizeValue;
429             this._mainElement.style.width = mainSizeValue;
430             this._sidebarElement.style.height = this._totalSizeOtherDimension + "px";
431             this._mainElement.style.height = this._totalSizeOtherDimension + "px";
432         } else {
433             this._sidebarElement.style.height = sidebarSizeValue;
434             this._mainElement.style.height = mainSizeValue;
435             this._sidebarElement.style.width = this._totalSizeOtherDimension + "px";
436             this._mainElement.style.width = this._totalSizeOtherDimension + "px";
437         }
438
439         // Position resizer.
440         if (this._isVertical) {
441             if (this._secondIsSidebar) {
442                 this._resizerElement.style.right = sidebarSizeValue;
443                 this._resizerElement.style.marginRight = -this._resizerElementSize / 2 + "px";
444             } else {
445                 this._resizerElement.style.left = sidebarSizeValue;
446                 this._resizerElement.style.marginLeft = -this._resizerElementSize / 2 + "px";
447             }
448         } else {
449             if (this._secondIsSidebar) {
450                 this._resizerElement.style.bottom = sidebarSizeValue;
451                 this._resizerElement.style.marginBottom = -this._resizerElementSize / 2 + "px";
452             } else {
453                 this._resizerElement.style.top = sidebarSizeValue;
454                 this._resizerElement.style.marginTop = -this._resizerElementSize / 2 + "px";
455             }
456         }
457
458         this._sidebarSize = size;
459
460         // Force layout.
461
462         if (animate) {
463             this._animate(false);
464         } else {
465             // No need to recalculate this._sidebarSize and this._totalSize again.
466             this.doResize();
467             this.dispatchEventToListeners(WebInspector.SplitView.Events.SidebarSizeChanged, this.sidebarSize());
468         }
469     },
470
471     /**
472      * @param {boolean} reverse
473      * @param {function()=} callback
474      */
475     _animate: function(reverse, callback)
476     {
477         var animationTime = 50;
478         this._animationCallback = callback;
479
480         var animatedMarginPropertyName;
481         if (this._isVertical)
482             animatedMarginPropertyName = this._secondIsSidebar ? "margin-right" : "margin-left";
483         else
484             animatedMarginPropertyName = this._secondIsSidebar ? "margin-bottom" : "margin-top";
485
486         var zoomFactor = WebInspector.zoomManager.zoomFactor();
487         var marginFrom = reverse ? "0" : "-" + (this._sidebarSize / zoomFactor) + "px";
488         var marginTo = reverse ? "-" + (this._sidebarSize / zoomFactor) + "px" : "0";
489
490         // This order of things is important.
491         // 1. Resize main element early and force layout.
492         this.element.style.setProperty(animatedMarginPropertyName, marginFrom);
493         if (!reverse) {
494             suppressUnused(this._mainElement.offsetWidth);
495             suppressUnused(this._sidebarElement.offsetWidth);
496         }
497
498         // 2. Issue onresize to the sidebar element, its size won't change.
499         if (!reverse)
500             this._sidebarView.doResize();
501
502         // 3. Configure and run animation
503         this.element.style.setProperty("transition", animatedMarginPropertyName + " " + animationTime + "ms linear");
504
505         var boundAnimationFrame;
506         var startTime;
507         /**
508          * @this {WebInspector.SplitView}
509          */
510         function animationFrame()
511         {
512             delete this._animationFrameHandle;
513
514             if (!startTime) {
515                 // Kick animation on first frame.
516                 this.element.style.setProperty(animatedMarginPropertyName, marginTo);
517                 startTime = window.performance.now();
518             } else if (window.performance.now() < startTime + animationTime) {
519                 // Process regular animation frame.
520                 this._mainView.doResize();
521             } else {
522                 // Complete animation.
523                 this._cancelAnimation();
524                 this._mainView.doResize();
525                 this.dispatchEventToListeners(WebInspector.SplitView.Events.SidebarSizeChanged, this.sidebarSize());
526                 return;
527             }
528             this._animationFrameHandle = window.requestAnimationFrame(boundAnimationFrame);
529         }
530         boundAnimationFrame = animationFrame.bind(this);
531         this._animationFrameHandle = window.requestAnimationFrame(boundAnimationFrame);
532     },
533
534     _cancelAnimation: function()
535     {
536         this.element.style.removeProperty("margin-top");
537         this.element.style.removeProperty("margin-right");
538         this.element.style.removeProperty("margin-bottom");
539         this.element.style.removeProperty("margin-left");
540         this.element.style.removeProperty("transition");
541
542         if (this._animationFrameHandle) {
543             window.cancelAnimationFrame(this._animationFrameHandle);
544             delete this._animationFrameHandle;
545         }
546         if (this._animationCallback) {
547             this._animationCallback();
548             delete this._animationCallback;
549         }
550     },
551
552     /**
553      * @param {number} sidebarSize
554      * @param {boolean=} userAction
555      * @return {number}
556      */
557     _applyConstraints: function(sidebarSize, userAction)
558     {
559         var totalSize = this._totalSizeDIP();
560
561         var constraints = this._sidebarView.constraints();
562         var minSidebarSize = this.isVertical() ? constraints.minimum.width : constraints.minimum.height;
563         if (!minSidebarSize)
564             minSidebarSize = WebInspector.SplitView.MinPadding;
565
566         var preferredSidebarSize = this.isVertical() ? constraints.preferred.width : constraints.preferred.height;
567         if (!preferredSidebarSize)
568             preferredSidebarSize = WebInspector.SplitView.MinPadding;
569         // Allow sidebar to be less than preferred by explicit user action.
570         if (sidebarSize < preferredSidebarSize)
571             preferredSidebarSize = Math.max(sidebarSize, minSidebarSize);
572
573         constraints = this._mainView.constraints();
574         var minMainSize = this.isVertical() ? constraints.minimum.width : constraints.minimum.height;
575         if (!minMainSize)
576             minMainSize = WebInspector.SplitView.MinPadding;
577
578         var preferredMainSize = this.isVertical() ? constraints.preferred.width : constraints.preferred.height;
579         if (!preferredMainSize)
580             preferredMainSize = WebInspector.SplitView.MinPadding;
581         var savedMainSize = this.isVertical() ? this._savedVerticalMainSize : this._savedHorizontalMainSize;
582         if (typeof savedMainSize !== "undefined")
583             preferredMainSize = Math.min(preferredMainSize, savedMainSize);
584         if (userAction)
585             preferredMainSize = minMainSize;
586
587         // Enough space for preferred.
588         var totalPreferred = preferredMainSize + preferredSidebarSize;
589         if (totalPreferred <= totalSize)
590             return Number.constrain(sidebarSize, preferredSidebarSize, totalSize - preferredMainSize);
591
592         // Enough space for minimum.
593         if (minMainSize + minSidebarSize <= totalSize) {
594             var delta = totalPreferred - totalSize;
595             var sidebarDelta = delta * preferredSidebarSize / totalPreferred;
596             sidebarSize = preferredSidebarSize - sidebarDelta;
597             return Number.constrain(sidebarSize, minSidebarSize, totalSize - minMainSize);
598         }
599
600         // Not enough space even for minimum sizes.
601         return Math.max(0, totalSize - minMainSize);
602     },
603
604     wasShown: function()
605     {
606         this._forceUpdateLayout();
607         WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
608     },
609
610     willHide: function()
611     {
612         WebInspector.zoomManager.removeEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
613     },
614
615     onResize: function()
616     {
617         this._updateLayout();
618     },
619
620     onLayout: function()
621     {
622         this._updateLayout();
623     },
624
625     /**
626      * @return {!Constraints}
627      */
628     calculateConstraints: function()
629     {
630         if (this._showMode === WebInspector.SplitView.ShowMode.OnlyMain)
631             return this._mainView.constraints();
632         if (this._showMode === WebInspector.SplitView.ShowMode.OnlySidebar)
633             return this._sidebarView.constraints();
634
635         var mainConstraints = this._mainView.constraints();
636         var sidebarConstraints = this._sidebarView.constraints();
637         var min = WebInspector.SplitView.MinPadding;
638         if (this._isVertical) {
639             mainConstraints = mainConstraints.widthToMax(min);
640             sidebarConstraints = sidebarConstraints.widthToMax(min);
641             return mainConstraints.addWidth(sidebarConstraints).heightToMax(sidebarConstraints);
642         } else {
643             mainConstraints = mainConstraints.heightToMax(min);
644             sidebarConstraints = sidebarConstraints.heightToMax(min);
645             return mainConstraints.widthToMax(sidebarConstraints).addHeight(sidebarConstraints);
646         }
647     },
648
649     /**
650      * @param {!WebInspector.Event} event
651      */
652     _onResizeStart: function(event)
653     {
654         this._resizeStartSize = this._sidebarSize;
655     },
656
657     /**
658      * @param {!WebInspector.Event} event
659      */
660     _onResizeUpdate: function(event)
661     {
662         var cssOffset = event.data.currentPosition - event.data.startPosition;
663         var dipOffset = cssOffset * WebInspector.zoomManager.zoomFactor();
664         var newSize = this._secondIsSidebar ? this._resizeStartSize - dipOffset : this._resizeStartSize + dipOffset;
665         var constrainedSize = this._applyConstraints(newSize, true);
666         this._savedSidebarSize = constrainedSize;
667         this._saveSetting();
668         this._innerSetSidebarSize(constrainedSize, false, true);
669         if (this.isVertical())
670             this._savedVerticalMainSize = this._totalSizeDIP() - this._sidebarSize;
671         else
672             this._savedHorizontalMainSize = this._totalSizeDIP() - this._sidebarSize;
673     },
674
675     /**
676      * @param {!WebInspector.Event} event
677      */
678     _onResizeEnd: function(event)
679     {
680         delete this._resizeStartSize;
681     },
682
683     hideDefaultResizer: function()
684     {
685         this.uninstallResizer(this._resizerElement);
686     },
687
688     /**
689      * @param {!Element} resizerElement
690      */
691     installResizer: function(resizerElement)
692     {
693         this._resizerWidget.addElement(resizerElement);
694     },
695
696     /**
697      * @param {!Element} resizerElement
698      */
699     uninstallResizer: function(resizerElement)
700     {
701         this._resizerWidget.removeElement(resizerElement);
702     },
703
704     /**
705      * @return {boolean}
706      */
707     hasCustomResizer: function()
708     {
709         var elements = this._resizerWidget.elements();
710         return elements.length > 1 || (elements.length == 1 && elements[0] !== this._resizerElement);
711     },
712
713     /**
714      * @param {!Element} resizer
715      * @param {boolean} on
716      */
717     toggleResizer: function(resizer, on)
718     {
719         if (on)
720             this.installResizer(resizer);
721         else
722             this.uninstallResizer(resizer);
723     },
724
725     /**
726      * @return {?WebInspector.Setting}
727      */
728     _setting: function()
729     {
730         if (!this._settingName)
731             return null;
732
733         if (!WebInspector.settings[this._settingName])
734             WebInspector.settings[this._settingName] = WebInspector.settings.createSetting(this._settingName, {});
735
736         return WebInspector.settings[this._settingName];
737     },
738
739     /**
740      * @return {?WebInspector.SplitView.SettingForOrientation}
741      */
742     _settingForOrientation: function()
743     {
744         var state = this._setting() ? this._setting().get() : {};
745         return this._isVertical ? state.vertical : state.horizontal;
746     },
747
748     /**
749      * @return {number}
750      */
751     _preferredSidebarSize: function()
752     {
753         var size = this._savedSidebarSize;
754         if (!size) {
755             size = this._isVertical ? this._defaultSidebarWidth : this._defaultSidebarHeight;
756             // If we have default value in percents, calculate it on first use.
757             if (0 < size && size < 1)
758                 size *= this._totalSizeDIP();
759         }
760         return size;
761     },
762
763     _restoreSidebarSizeFromSettings: function()
764     {
765         var settingForOrientation = this._settingForOrientation();
766         this._savedSidebarSize = settingForOrientation ? settingForOrientation.size : 0;
767     },
768
769     _restoreAndApplyShowModeFromSettings: function()
770     {
771         var orientationState = this._settingForOrientation();
772         this._savedShowMode = orientationState ? orientationState.showMode : WebInspector.SplitView.ShowMode.Both;
773         this._showMode = this._savedShowMode;
774
775         switch (this._savedShowMode) {
776         case WebInspector.SplitView.ShowMode.Both:
777             this.showBoth();
778             break;
779         case WebInspector.SplitView.ShowMode.OnlyMain:
780             this.hideSidebar();
781             break;
782         case WebInspector.SplitView.ShowMode.OnlySidebar:
783             this.hideMain();
784             break;
785         }
786     },
787
788     _saveShowModeToSettings: function()
789     {
790         this._savedShowMode = this._showMode;
791         this._saveSetting();
792     },
793
794     _saveSetting: function()
795     {
796         var setting = this._setting();
797         if (!setting)
798             return;
799         var state = setting.get();
800         var orientationState = (this._isVertical ? state.vertical : state.horizontal) || {};
801
802         orientationState.size = this._savedSidebarSize;
803         if (this._shouldSaveShowMode)
804             orientationState.showMode = this._savedShowMode;
805
806         if (this._isVertical)
807             state.vertical = orientationState;
808         else
809             state.horizontal = orientationState;
810         setting.set(state);
811     },
812
813     _forceUpdateLayout: function()
814     {
815         // Force layout even if sidebar size does not change.
816         this._sidebarSize = -1;
817         this._updateLayout();
818     },
819
820     /**
821      * @param {!WebInspector.Event} event
822      */
823     _onZoomChanged: function(event)
824     {
825         this._forceUpdateLayout();
826     },
827
828     /**
829      * @param {string} title
830      * @param {string} className
831      * @return {!WebInspector.StatusBarButton}
832      */
833     createShowHideSidebarButton: function(title, className)
834     {
835         console.assert(this.isVertical(), "Buttons for split view with horizontal split are not supported yet.");
836
837         this._showHideSidebarButtonTitle = WebInspector.UIString(title);
838         this._showHideSidebarButton = new WebInspector.StatusBarButton("", "sidebar-show-hide-button " + className, 3);
839         this._showHideSidebarButton.addEventListener("click", buttonClicked.bind(this));
840         this._updateShowHideSidebarButton();
841
842         /**
843          * @this {WebInspector.SplitView}
844          * @param {!WebInspector.Event} event
845          */
846         function buttonClicked(event)
847         {
848             if (this._showMode !== WebInspector.SplitView.ShowMode.Both)
849                 this.showBoth(true);
850             else
851                 this.hideSidebar(true);
852         }
853
854         return this._showHideSidebarButton;
855     },
856
857     _updateShowHideSidebarButton: function()
858     {
859         if (!this._showHideSidebarButton)
860             return;
861         var sidebarHidden = this._showMode === WebInspector.SplitView.ShowMode.OnlyMain;
862         this._showHideSidebarButton.state = sidebarHidden ? "show" : "hide";
863         this._showHideSidebarButton.element.classList.toggle("top-sidebar-show-hide-button", !this.isVertical() && !this.isSidebarSecond());
864         this._showHideSidebarButton.element.classList.toggle("right-sidebar-show-hide-button", this.isVertical() && this.isSidebarSecond());
865         this._showHideSidebarButton.element.classList.toggle("bottom-sidebar-show-hide-button", !this.isVertical() && this.isSidebarSecond());
866         this._showHideSidebarButton.element.classList.toggle("left-sidebar-show-hide-button", this.isVertical() && !this.isSidebarSecond());
867         this._showHideSidebarButton.title = sidebarHidden ? WebInspector.UIString("Show %s", this._showHideSidebarButtonTitle) : WebInspector.UIString("Hide %s", this._showHideSidebarButtonTitle);
868     },
869
870     __proto__: WebInspector.View.prototype
871 }