]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/place.js.uncompressed.js
1 define("dijit/place", [
2 "dojo/_base/array", // array.forEach array.map array.some
3 "dojo/dom-geometry", // domGeometry.position
4 "dojo/dom-style", // domStyle.getComputedStyle
5 "dojo/_base/kernel", // kernel.deprecated
6 "dojo/_base/window", // win.body
7 "dojo/window", // winUtils.getBox
8 "./main" // dijit (defining dijit.place to match API doc)
9 ], function(array
, domGeometry
, domStyle
, kernel
, win
, winUtils
, dijit
){
15 function _place(/*DomNode*/ node
, choices
, layoutNode
, aroundNodeCoords
){
17 // Given a list of spots to put node, put it at the first spot where it fits,
18 // of if it doesn't fit anywhere then the place with the least overflow
20 // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
21 // Above example says to put the top-left corner of the node at (10,20)
22 // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
23 // for things like tooltip, they are displayed differently (and have different dimensions)
24 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
25 // It also passes in the available size for the popup, which is useful for tooltips to
26 // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
27 // how much the popup had to be modified to fit into the available space. This is used to determine
28 // what the best placement is.
29 // aroundNodeCoords: Object
30 // Size of aroundNode, ex: {w: 200, h: 50}
32 // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
33 // viewport over document
34 var view
= winUtils
.getBox(node
.ownerDocument
);
36 // This won't work if the node is inside a <div style="position: relative">,
37 // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong
38 // and also it might get cutoff)
39 if(!node
.parentNode
|| String(node
.parentNode
.tagName
).toLowerCase() != "body"){
40 win
.body(node
.ownerDocument
).appendChild(node
);
44 array
.some(choices
, function(choice
){
45 var corner
= choice
.corner
;
49 // calculate amount of space available given specified position of node
50 var spaceAvailable
= {
52 'L': view
.l
+ view
.w
- pos
.x
,
57 'T': view
.t
+ view
.h
- pos
.y
,
63 // Clear left/right position settings set earlier so they don't interfere with calculations,
64 // specifically when layoutNode() (a.k.a. Tooltip.orient()) measures natural width of Tooltip
66 s
.left
= s
.right
= "auto";
68 // configure node to be displayed in given position relative to button
69 // (need to do this in order to get an accurate size for the node, because
70 // a tooltip's size changes based on position, due to triangle)
72 var res
= layoutNode(node
, choice
.aroundCorner
, corner
, spaceAvailable
, aroundNodeCoords
);
73 overflow
= typeof res
== "undefined" ? 0 : res
;
77 var style
= node
.style
;
78 var oldDisplay
= style
.display
;
79 var oldVis
= style
.visibility
;
80 if(style
.display
== "none"){
81 style
.visibility
= "hidden";
84 var bb
= domGeometry
.position(node
);
85 style
.display
= oldDisplay
;
86 style
.visibility
= oldVis
;
88 // coordinates and size of node with specified corner placed at pos,
89 // and clipped by viewport
94 'M': Math
.max(view
.l
, Math
.min(view
.l
+ view
.w
, pos
.x
+ (bb
.w
>> 1)) - bb
.w
) // M orientation is more flexible
99 'M': Math
.max(view
.t
, Math
.min(view
.t
+ view
.h
, pos
.y
+ (bb
.h
>> 1)) - bb
.h
)
101 startX
= Math
.max(view
.l
, startXpos
),
102 startY
= Math
.max(view
.t
, startYpos
),
103 endX
= Math
.min(view
.l
+ view
.w
, startXpos
+ bb
.w
),
104 endY
= Math
.min(view
.t
+ view
.h
, startYpos
+ bb
.h
),
105 width
= endX
- startX
,
106 height
= endY
- startY
;
108 overflow
+= (bb
.w
- width
) + (bb
.h
- height
);
110 if(best
== null || overflow
< best
.overflow
){
113 aroundCorner
: choice
.aroundCorner
,
119 spaceAvailable
: spaceAvailable
126 // In case the best position is not the last one we checked, need to call
127 // layoutNode() again.
128 if(best
.overflow
&& layoutNode
){
129 layoutNode(node
, best
.aroundCorner
, best
.corner
, best
.spaceAvailable
, aroundNodeCoords
);
132 // And then position the node. Do this last, after the layoutNode() above
133 // has sized the node, due to browser quirks when the viewport is scrolled
134 // (specifically that a Tooltip will shrink to fit as though the window was
135 // scrolled to the left).
137 // In RTL mode, set style.right rather than style.left so in the common case,
138 // window resizes move the popup along with the aroundNode.
139 var l
= domGeometry
.isBodyLtr(node
.ownerDocument
),
141 s
.top
= best
.y
+ "px";
142 s
[l
? "left" : "right"] = (l
? best
.x
: view
.w
- best
.x
- best
.w
) + "px";
143 s
[l
? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
150 // Code to place a DOMNode relative to another DOMNode.
151 // Load using require(["dijit/place"], function(place){ ... }).
153 at: function(node
, pos
, corners
, padding
){
155 // Positions one of the node's corners at specified position
156 // such that node is fully visible in viewport.
158 // NOTE: node is assumed to be absolutely or relatively positioned.
160 // The node to position
161 // pos: dijit/place.__Position
162 // Object like {x: 10, y: 20}
164 // Array of Strings representing order to try corners in, like ["TR", "BL"].
165 // Possible values are:
167 // - "BL" - bottom left
168 // - "BR" - bottom right
170 // - "TR" - top right
171 // padding: dijit/place.__Position?
172 // optional param to set padding, to put some buffer around the element you want to position.
174 // Try to place node's top right corner at (10,20).
175 // If that makes node go (partially) off screen, then try placing
176 // bottom left corner at (10,20).
177 // | place(node, {x: 10, y: 20}, ["TR", "BL"])
178 var choices
= array
.map(corners
, function(corner
){
179 var c
= { corner
: corner
, pos
: {x
:pos
.x
,y
:pos
.y
} };
181 c
.pos
.x
+= corner
.charAt(1) == 'L' ? padding
.x
: -padding
.x
;
182 c
.pos
.y
+= corner
.charAt(0) == 'T' ? padding
.y
: -padding
.y
;
187 return _place(node
, choices
);
192 /*DomNode|dijit/place.__Rectangle*/ anchor,
193 /*String[]*/ positions
,
194 /*Boolean*/ leftToRight
,
195 /*Function?*/ layoutNode
){
198 // Position node adjacent or kitty-corner to anchor
199 // such that it's fully visible in viewport.
201 // Place node such that corner of node touches a corner of
202 // aroundNode, and that node is fully visible.
204 // Either a DOMNode or a rectangle (object with x, y, width, height).
206 // Ordered list of positions to try matching up.
208 // - before: places drop down to the left of the anchor node/widget, or to the right in the case
209 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
210 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
211 // - after: places drop down to the right of the anchor node/widget, or to the left in the case
212 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
213 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
214 // - before-centered: centers drop down to the left of the anchor node/widget, or to the right
215 // in the case of RTL scripts like Hebrew and Arabic
216 // - after-centered: centers drop down to the right of the anchor node/widget, or to the left
217 // in the case of RTL scripts like Hebrew and Arabic
218 // - above-centered: drop down is centered above anchor node
219 // - above: drop down goes above anchor node, left sides aligned
220 // - above-alt: drop down goes above anchor node, right sides aligned
221 // - below-centered: drop down is centered above anchor node
222 // - below: drop down goes below anchor node
223 // - below-alt: drop down goes below anchor node, right sides aligned
224 // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
225 // For things like tooltip, they are displayed differently (and have different dimensions)
226 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
228 // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below"
229 // positions slightly.
231 // | placeAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
232 // This will try to position node such that node's top-left corner is at the same position
233 // as the bottom left corner of the aroundNode (ie, put node below
234 // aroundNode, with left edges aligned). If that fails it will try to put
235 // the bottom-right corner of node where the top right corner of aroundNode is
236 // (ie, put node above aroundNode, with right edges aligned)
239 // if around is a DOMNode (or DOMNode id), convert to coordinates
240 var aroundNodePos
= (typeof anchor
== "string" || "offsetWidth" in anchor
)
241 ? domGeometry
.position(anchor
, true)
244 // Compute position and size of visible part of anchor (it may be partially hidden by ancestor nodes w/scrollbars)
245 if(anchor
.parentNode
){
246 // ignore nodes between position:relative and position:absolute
247 var sawPosAbsolute
= domStyle
.getComputedStyle(anchor
).position
== "absolute";
248 var parent
= anchor
.parentNode
;
249 while(parent
&& parent
.nodeType
== 1 && parent
.nodeName
!= "BODY"){ //ignoring the body will help performance
250 var parentPos
= domGeometry
.position(parent
, true),
251 pcs
= domStyle
.getComputedStyle(parent
);
252 if(/relative|absolute/.test(pcs
.position
)){
253 sawPosAbsolute
= false;
255 if(!sawPosAbsolute
&& /hidden|auto|scroll/.test(pcs
.overflow
)){
256 var bottomYCoord
= Math
.min(aroundNodePos
.y
+ aroundNodePos
.h
, parentPos
.y
+ parentPos
.h
);
257 var rightXCoord
= Math
.min(aroundNodePos
.x
+ aroundNodePos
.w
, parentPos
.x
+ parentPos
.w
);
258 aroundNodePos
.x
= Math
.max(aroundNodePos
.x
, parentPos
.x
);
259 aroundNodePos
.y
= Math
.max(aroundNodePos
.y
, parentPos
.y
);
260 aroundNodePos
.h
= bottomYCoord
- aroundNodePos
.y
;
261 aroundNodePos
.w
= rightXCoord
- aroundNodePos
.x
;
263 if(pcs
.position
== "absolute"){
264 sawPosAbsolute
= true;
266 parent
= parent
.parentNode
;
270 var x
= aroundNodePos
.x
,
272 width
= "w" in aroundNodePos
? aroundNodePos
.w
: (aroundNodePos
.w
= aroundNodePos
.width
),
273 height
= "h" in aroundNodePos
? aroundNodePos
.h
: (kernel
.deprecated("place.around: dijit/place.__Rectangle: { x:"+x
+", y:"+y
+", height:"+aroundNodePos
.height
+", width:"+width
+" } has been deprecated. Please use { x:"+x
+", y:"+y
+", h:"+aroundNodePos
.height
+", w:"+width
+" }", "", "2.0"), aroundNodePos
.h
= aroundNodePos
.height
);
275 // Convert positions arguments into choices argument for _place()
277 function push(aroundCorner
, corner
){
279 aroundCorner
: aroundCorner
,
285 'M': x
+ (width
>> 1)
286 }[aroundCorner
.charAt(1)],
290 'M': y
+ (height
>> 1)
291 }[aroundCorner
.charAt(0)]
295 array
.forEach(positions
, function(pos
){
296 var ltr
= leftToRight
;
298 case "above-centered":
301 case "below-centered":
304 case "after-centered":
307 case "before-centered":
308 push(ltr
? "ML" : "MR", ltr
? "MR" : "ML");
314 push(ltr
? "TL" : "TR", ltr
? "TR" : "TL");
315 push(ltr
? "BL" : "BR", ltr
? "BR" : "BL");
321 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
322 push(ltr
? "BL" : "BR", ltr
? "TL" : "TR");
323 push(ltr
? "BR" : "BL", ltr
? "TR" : "TL");
329 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
330 push(ltr
? "TL" : "TR", ltr
? "BL" : "BR");
331 push(ltr
? "TR" : "TL", ltr
? "BR" : "BL");
334 // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}.
335 // Not meant to be used directly.
336 push(pos
.aroundCorner
, pos
.corner
);
340 var position
= _place(node
, choices
, layoutNode
, {w
: width
, h
: height
});
341 position
.aroundNodePos
= aroundNodePos
;
350 // horizontal coordinate in pixels, relative to document body
352 // vertical coordinate in pixels, relative to document body
354 place.__Rectangle = {
356 // horizontal offset in pixels, relative to document body
358 // vertical offset in pixels, relative to document body
360 // width in pixels. Can also be specified as "width" for backwards-compatibility.
362 // height in pixels. Can also be specified as "height" for backwards-compatibility.
366 return dijit
.place
= place
; // setting dijit.place for back-compat, remove for 2.0