tizen beta release
[profile/ivi/webkit-efl.git] / debian / tmp / usr / share / ewebkit-0 / webinspector / Popover.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  * @param {WebInspector.PopoverHelper=} popoverHelper
34  */
35 WebInspector.Popover = function(popoverHelper)
36 {
37     this.element = document.createElement("div");
38     this.element.className = "popover custom-popup-vertical-scroll custom-popup-horizontal-scroll";
39
40     this._popupArrowElement = document.createElement("div");
41     this._popupArrowElement.className = "arrow";
42     this.element.appendChild(this._popupArrowElement);
43
44     this._contentDiv = document.createElement("div");
45     this._contentDiv.className = "content";
46     this._visible = false;
47     this._popoverHelper = popoverHelper;
48 }
49
50 WebInspector.Popover.prototype = {
51     show: function(contentElement, anchor, preferredWidth, preferredHeight)
52     {
53         if (this._disposed)
54             return;
55         this.contentElement = contentElement;
56
57         // This should not happen, but we hide previous popup to be on the safe side.
58         if (WebInspector.Popover._popoverElement)
59             document.body.removeChild(WebInspector.Popover._popoverElement);
60         WebInspector.Popover._popoverElement = this.element;
61
62         // Temporarily attach in order to measure preferred dimensions.
63         this.contentElement.positionAt(0, 0);
64         document.body.appendChild(this.contentElement);
65         preferredWidth = preferredWidth || this.contentElement.offsetWidth;
66         preferredHeight = preferredHeight || this.contentElement.offsetHeight;
67
68         this._contentDiv.appendChild(this.contentElement);
69         this.element.appendChild(this._contentDiv);
70         document.body.appendChild(this.element);
71         this._positionElement(anchor, preferredWidth, preferredHeight);
72         this._visible = true;
73         if (this._popoverHelper)
74             contentElement.addEventListener("mousemove", this._popoverHelper._killHidePopoverTimer.bind(this._popoverHelper), true);
75     },
76
77     hide: function()
78     {
79         if (WebInspector.Popover._popoverElement) {
80             delete WebInspector.Popover._popoverElement;
81             document.body.removeChild(this.element);
82         }
83         this._visible = false;
84     },
85
86     get visible()
87     {
88         return this._visible;
89     },
90
91     get disposed()
92     {
93         return this._disposed;
94     },
95
96     dispose: function()
97     {
98         if (this.visible)
99             this.hide();
100         this._disposed = true;
101     },
102
103     _positionElement: function(anchorElement, preferredWidth, preferredHeight)
104     {
105         const borderWidth = 25;
106         const scrollerWidth = 11;
107         const arrowHeight = 15;
108         const arrowOffset = 10;
109         const borderRadius = 10;
110
111         // Skinny tooltips are not pretty, their arrow location is not nice.
112         preferredWidth = Math.max(preferredWidth, 50);
113         const totalWidth = window.innerWidth;
114         const totalHeight = window.innerHeight;
115
116         var anchorBox = anchorElement.boxInWindow(window);
117         var newElementPosition = { x: 0, y: 0, width: preferredWidth + scrollerWidth, height: preferredHeight };
118
119         var verticalAlignment;
120         var roomAbove = anchorBox.y;
121         var roomBelow = totalHeight - anchorBox.y - anchorBox.height;
122
123         if (roomAbove > roomBelow) {
124             // Positioning above the anchor.
125             if (anchorBox.y > newElementPosition.height + arrowHeight + borderRadius)
126                 newElementPosition.y = anchorBox.y - newElementPosition.height - arrowHeight;
127             else {
128                 newElementPosition.y = borderRadius * 2;
129                 newElementPosition.height = anchorBox.y - borderRadius * 2 - arrowHeight;
130             }
131             verticalAlignment = "bottom";
132         } else {
133             // Positioning below the anchor.
134             newElementPosition.y = anchorBox.y + anchorBox.height + arrowHeight;
135             if (newElementPosition.y + newElementPosition.height + arrowHeight - borderWidth >= totalHeight)
136                 newElementPosition.height = totalHeight - anchorBox.y - anchorBox.height - borderRadius * 2 - arrowHeight;
137             // Align arrow.
138             verticalAlignment = "top";
139         }
140
141         var horizontalAlignment;
142         if (anchorBox.x + newElementPosition.width < totalWidth) {
143             newElementPosition.x = Math.max(borderRadius, anchorBox.x - borderRadius - arrowOffset);
144             horizontalAlignment = "left";
145         } else if (newElementPosition.width + borderRadius * 2 < totalWidth) {
146             newElementPosition.x = totalWidth - newElementPosition.width - borderRadius;
147             horizontalAlignment = "right";
148             // Position arrow accurately.
149             var arrowRightPosition = Math.max(0, totalWidth - anchorBox.x - anchorBox.width - borderRadius - arrowOffset);
150             arrowRightPosition += anchorBox.width / 2;
151             this._popupArrowElement.style.right = arrowRightPosition + "px";
152         } else {
153             newElementPosition.x = borderRadius;
154             newElementPosition.width = totalWidth - borderRadius * 2;
155             newElementPosition.height += scrollerWidth;
156             horizontalAlignment = "left";
157             if (verticalAlignment === "bottom")
158                 newElementPosition.y -= scrollerWidth;
159             // Position arrow accurately.
160             this._popupArrowElement.style.left = Math.max(0, anchorBox.x - borderRadius * 2 - arrowOffset) + "px";
161             this._popupArrowElement.style.left += anchorBox.width / 2;
162         }
163
164         this.element.className = "popover custom-popup-vertical-scroll custom-popup-horizontal-scroll " + verticalAlignment + "-" + horizontalAlignment + "-arrow";
165         this.element.positionAt(newElementPosition.x - borderWidth, newElementPosition.y - borderWidth);
166         this.element.style.width = newElementPosition.width + borderWidth * 2 + "px";
167         this.element.style.height = newElementPosition.height + borderWidth * 2 + "px";
168     }
169 }
170
171 /**
172  * @constructor
173  * @param {function()=} onHide
174  * @param {boolean=} disableOnClick
175  */
176 WebInspector.PopoverHelper = function(panelElement, getAnchor, showPopover, onHide, disableOnClick)
177 {
178     this._panelElement = panelElement;
179     this._getAnchor = getAnchor;
180     this._showPopover = showPopover;
181     this._onHide = onHide;
182     this._disableOnClick = !!disableOnClick;
183     panelElement.addEventListener("mousedown", this._mouseDown.bind(this), false);
184     panelElement.addEventListener("mousemove", this._mouseMove.bind(this), false);
185     this.setTimeout(1000);
186 }
187
188 WebInspector.PopoverHelper.prototype = {
189     setTimeout: function(timeout)
190     {
191         this._timeout = timeout;
192     },
193
194     _mouseDown: function(event)
195     {
196         if (this._disableOnClick)
197             this.hidePopover();
198         else {
199             this._killHidePopoverTimer();
200             this._handleMouseAction(event, true);
201         }
202     },
203
204     _mouseMove: function(event)
205     {
206         // Pretend that nothing has happened.
207         if (this._hoverElement === event.target || (this._hoverElement && this._hoverElement.isAncestor(event.target)))
208             return;
209
210         // User has 500ms (this._timeout / 2) to reach the popup.
211         if (this._popover && !this._hidePopoverTimer) {
212             var self = this;
213             function doHide()
214             {
215                 self._hidePopover();
216                 delete self._hidePopoverTimer;
217             }
218             this._hidePopoverTimer = setTimeout(doHide, this._timeout / 2);
219         }
220
221         this._handleMouseAction(event, false);
222     },
223
224     _handleMouseAction: function(event, isMouseDown)
225     {
226         this._resetHoverTimer();
227         if (event.which && this._disableOnClick)
228             return;
229         this._hoverElement = this._getAnchor(event.target);
230         if (!this._hoverElement)
231             return;
232         const toolTipDelay = isMouseDown ? 0 : (this._popup ? this._timeout * 0.6 : this._timeout);
233         this._hoverTimer = setTimeout(this._mouseHover.bind(this, this._hoverElement), toolTipDelay);
234     },
235
236     _resetHoverTimer: function()
237     {
238         if (this._hoverTimer) {
239             clearTimeout(this._hoverTimer);
240             delete this._hoverTimer;
241         }
242     },
243
244     hidePopover: function()
245     {
246         this._resetHoverTimer();
247         this._hidePopover();
248     },
249
250     _hidePopover: function()
251     {
252         if (!this._popover)
253             return;
254
255         if (this._onHide)
256             this._onHide();
257
258         this._popover.dispose();
259         delete this._popover;
260     },
261
262     _mouseHover: function(element)
263     {
264         delete this._hoverTimer;
265
266         this._hidePopover();
267         this._popover = new WebInspector.Popover(this);
268         this._showPopover(element, this._popover);
269     },
270
271     _killHidePopoverTimer: function()
272     {
273         if (this._hidePopoverTimer) {
274             clearTimeout(this._hidePopoverTimer);
275             delete this._hidePopoverTimer;
276
277             // We know that we reached the popup, but we might have moved over other elements.
278             // Discard pending command.
279             this._resetHoverTimer();
280         }
281     }
282 }