]> git.wh0rd.org Git - tt-rss.git/blob - lib/dijit/place.js.uncompressed.js
upgrade dojo to 1.8.3 (refs #570)
[tt-rss.git] / 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){
10
11         // module:
12         //              dijit/place
13
14
15         function _place(/*DomNode*/ node, choices, layoutNode, aroundNodeCoords){
16                 // summary:
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
19                 // choices: Array
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}
31
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);
35
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);
41                 }
42
43                 var best = null;
44                 array.some(choices, function(choice){
45                         var corner = choice.corner;
46                         var pos = choice.pos;
47                         var overflow = 0;
48
49                         // calculate amount of space available given specified position of node
50                         var spaceAvailable = {
51                                 w: {
52                                         'L': view.l + view.w - pos.x,
53                                         'R': pos.x - view.l,
54                                         'M': view.w
55                                    }[corner.charAt(1)],
56                                 h: {
57                                         'T': view.t + view.h - pos.y,
58                                         'B': pos.y - view.t,
59                                         'M': view.h
60                                    }[corner.charAt(0)]
61                         };
62
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
65                         var s = node.style;
66                         s.left = s.right = "auto";
67
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)
71                         if(layoutNode){
72                                 var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords);
73                                 overflow = typeof res == "undefined" ? 0 : res;
74                         }
75
76                         // get node's size
77                         var style = node.style;
78                         var oldDisplay = style.display;
79                         var oldVis = style.visibility;
80                         if(style.display == "none"){
81                                 style.visibility = "hidden";
82                                 style.display = "";
83                         }
84                         var bb = domGeometry.position(node);
85                         style.display = oldDisplay;
86                         style.visibility = oldVis;
87
88                         // coordinates and size of node with specified corner placed at pos,
89                         // and clipped by viewport
90                         var
91                                 startXpos = {
92                                         'L': pos.x,
93                                         'R': pos.x - bb.w,
94                                         'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (bb.w >> 1)) - bb.w) // M orientation is more flexible
95                                 }[corner.charAt(1)],
96                                 startYpos = {
97                                         'T': pos.y,
98                                         'B': pos.y - bb.h,
99                                         'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (bb.h >> 1)) - bb.h)
100                                 }[corner.charAt(0)],
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;
107
108                         overflow += (bb.w - width) + (bb.h - height);
109
110                         if(best == null || overflow < best.overflow){
111                                 best = {
112                                         corner: corner,
113                                         aroundCorner: choice.aroundCorner,
114                                         x: startX,
115                                         y: startY,
116                                         w: width,
117                                         h: height,
118                                         overflow: overflow,
119                                         spaceAvailable: spaceAvailable
120                                 };
121                         }
122
123                         return !overflow;
124                 });
125
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);
130                 }
131
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).
136                 //
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),
140                         s = node.style;
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
144
145                 return best;
146         }
147
148         var place = {
149                 // summary:
150                 //              Code to place a DOMNode relative to another DOMNode.
151                 //              Load using require(["dijit/place"], function(place){ ... }).
152
153                 at: function(node, pos, corners, padding){
154                         // summary:
155                         //              Positions one of the node's corners at specified position
156                         //              such that node is fully visible in viewport.
157                         // description:
158                         //              NOTE: node is assumed to be absolutely or relatively positioned.
159                         // node: DOMNode
160                         //              The node to position
161                         // pos: dijit/place.__Position
162                         //              Object like {x: 10, y: 20}
163                         // corners: String[]
164                         //              Array of Strings representing order to try corners in, like ["TR", "BL"].
165                         //              Possible values are:
166                         //
167                         //              - "BL" - bottom left
168                         //              - "BR" - bottom right
169                         //              - "TL" - top left
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.
173                         // example:
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} };
180                                 if(padding){
181                                         c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x;
182                                         c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y;
183                                 }
184                                 return c;
185                         });
186
187                         return _place(node, choices);
188                 },
189
190                 around: function(
191                         /*DomNode*/             node,
192                         /*DomNode|dijit/place.__Rectangle*/ anchor,
193                         /*String[]*/    positions,
194                         /*Boolean*/             leftToRight,
195                         /*Function?*/   layoutNode){
196
197                         // summary:
198                         //              Position node adjacent or kitty-corner to anchor
199                         //              such that it's fully visible in viewport.
200                         // description:
201                         //              Place node such that corner of node touches a corner of
202                         //              aroundNode, and that node is fully visible.
203                         // anchor:
204                         //              Either a DOMNode or a rectangle (object with x, y, width, height).
205                         // positions:
206                         //              Ordered list of positions to try matching up.
207                         //
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.
227                         // leftToRight:
228                         //              True if widget is LTR, false if widget is RTL.   Affects the behavior of "above" and "below"
229                         //              positions slightly.
230                         // example:
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)
237                         //
238
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)
242                                 : anchor;
243
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;
254                                         }
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;
262                                         }
263                                         if(pcs.position == "absolute"){
264                                                 sawPosAbsolute = true;
265                                         }
266                                         parent = parent.parentNode;
267                                 }
268                         }                       
269
270                         var x = aroundNodePos.x,
271                                 y = aroundNodePos.y,
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);
274
275                         // Convert positions arguments into choices argument for _place()
276                         var choices = [];
277                         function push(aroundCorner, corner){
278                                 choices.push({
279                                         aroundCorner: aroundCorner,
280                                         corner: corner,
281                                         pos: {
282                                                 x: {
283                                                         'L': x,
284                                                         'R': x + width,
285                                                         'M': x + (width >> 1)
286                                                    }[aroundCorner.charAt(1)],
287                                                 y: {
288                                                         'T': y,
289                                                         'B': y + height,
290                                                         'M': y + (height >> 1)
291                                                    }[aroundCorner.charAt(0)]
292                                         }
293                                 })
294                         }
295                         array.forEach(positions, function(pos){
296                                 var ltr =  leftToRight;
297                                 switch(pos){
298                                         case "above-centered":
299                                                 push("TM", "BM");
300                                                 break;
301                                         case "below-centered":
302                                                 push("BM", "TM");
303                                                 break;
304                                         case "after-centered":
305                                                 ltr = !ltr;
306                                                 // fall through
307                                         case "before-centered":
308                                                 push(ltr ? "ML" : "MR", ltr ? "MR" : "ML");
309                                                 break;
310                                         case "after":
311                                                 ltr = !ltr;
312                                                 // fall through
313                                         case "before":
314                                                 push(ltr ? "TL" : "TR", ltr ? "TR" : "TL");
315                                                 push(ltr ? "BL" : "BR", ltr ? "BR" : "BL");
316                                                 break;
317                                         case "below-alt":
318                                                 ltr = !ltr;
319                                                 // fall through
320                                         case "below":
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");
324                                                 break;
325                                         case "above-alt":
326                                                 ltr = !ltr;
327                                                 // fall through
328                                         case "above":
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");
332                                                 break;
333                                         default:
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);
337                                 }
338                         });
339
340                         var position = _place(node, choices, layoutNode, {w: width, h: height});
341                         position.aroundNodePos = aroundNodePos;
342
343                         return position;
344                 }
345         };
346
347         /*=====
348         place.__Position = {
349                 // x: Integer
350                 //              horizontal coordinate in pixels, relative to document body
351                 // y: Integer
352                 //              vertical coordinate in pixels, relative to document body
353         };
354         place.__Rectangle = {
355                 // x: Integer
356                 //              horizontal offset in pixels, relative to document body
357                 // y: Integer
358                 //              vertical offset in pixels, relative to document body
359                 // w: Integer
360                 //              width in pixels.   Can also be specified as "width" for backwards-compatibility.
361                 // h: Integer
362                 //              height in pixels.   Can also be specified as "height" for backwards-compatibility.
363         };
364         =====*/
365
366         return dijit.place = place;     // setting dijit.place for back-compat, remove for 2.0
367 });