]> git.wh0rd.org - 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 });