UPDATE 4.4.0.0
[phpmyadmin.git] / js / jquery / src / jquery-ui / position.js
blob73be3f87469953503a32bc7ef9f212f08cf6c90e
1 /*!
2  * jQuery UI Position 1.11.2
3  * http://jqueryui.com
4  *
5  * Copyright 2014 jQuery Foundation and other contributors
6  * Released under the MIT license.
7  * http://jquery.org/license
8  *
9  * http://api.jqueryui.com/position/
10  */
11 (function( factory ) {
12         if ( typeof define === "function" && define.amd ) {
14                 // AMD. Register as an anonymous module.
15                 define( [ "jquery" ], factory );
16         } else {
18                 // Browser globals
19                 factory( jQuery );
20         }
21 }(function( $ ) {
22 (function() {
24 $.ui = $.ui || {};
26 var cachedScrollbarWidth, supportsOffsetFractions,
27         max = Math.max,
28         abs = Math.abs,
29         round = Math.round,
30         rhorizontal = /left|center|right/,
31         rvertical = /top|center|bottom/,
32         roffset = /[\+\-]\d+(\.[\d]+)?%?/,
33         rposition = /^\w+/,
34         rpercent = /%$/,
35         _position = $.fn.position;
37 function getOffsets( offsets, width, height ) {
38         return [
39                 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
40                 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
41         ];
44 function parseCss( element, property ) {
45         return parseInt( $.css( element, property ), 10 ) || 0;
48 function getDimensions( elem ) {
49         var raw = elem[0];
50         if ( raw.nodeType === 9 ) {
51                 return {
52                         width: elem.width(),
53                         height: elem.height(),
54                         offset: { top: 0, left: 0 }
55                 };
56         }
57         if ( $.isWindow( raw ) ) {
58                 return {
59                         width: elem.width(),
60                         height: elem.height(),
61                         offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
62                 };
63         }
64         if ( raw.preventDefault ) {
65                 return {
66                         width: 0,
67                         height: 0,
68                         offset: { top: raw.pageY, left: raw.pageX }
69                 };
70         }
71         return {
72                 width: elem.outerWidth(),
73                 height: elem.outerHeight(),
74                 offset: elem.offset()
75         };
78 $.position = {
79         scrollbarWidth: function() {
80                 if ( cachedScrollbarWidth !== undefined ) {
81                         return cachedScrollbarWidth;
82                 }
83                 var w1, w2,
84                         div = $( "<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
85                         innerDiv = div.children()[0];
87                 $( "body" ).append( div );
88                 w1 = innerDiv.offsetWidth;
89                 div.css( "overflow", "scroll" );
91                 w2 = innerDiv.offsetWidth;
93                 if ( w1 === w2 ) {
94                         w2 = div[0].clientWidth;
95                 }
97                 div.remove();
99                 return (cachedScrollbarWidth = w1 - w2);
100         },
101         getScrollInfo: function( within ) {
102                 var overflowX = within.isWindow || within.isDocument ? "" :
103                                 within.element.css( "overflow-x" ),
104                         overflowY = within.isWindow || within.isDocument ? "" :
105                                 within.element.css( "overflow-y" ),
106                         hasOverflowX = overflowX === "scroll" ||
107                                 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
108                         hasOverflowY = overflowY === "scroll" ||
109                                 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
110                 return {
111                         width: hasOverflowY ? $.position.scrollbarWidth() : 0,
112                         height: hasOverflowX ? $.position.scrollbarWidth() : 0
113                 };
114         },
115         getWithinInfo: function( element ) {
116                 var withinElement = $( element || window ),
117                         isWindow = $.isWindow( withinElement[0] ),
118                         isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9;
119                 return {
120                         element: withinElement,
121                         isWindow: isWindow,
122                         isDocument: isDocument,
123                         offset: withinElement.offset() || { left: 0, top: 0 },
124                         scrollLeft: withinElement.scrollLeft(),
125                         scrollTop: withinElement.scrollTop(),
127                         // support: jQuery 1.6.x
128                         // jQuery 1.6 doesn't support .outerWidth/Height() on documents or windows
129                         width: isWindow || isDocument ? withinElement.width() : withinElement.outerWidth(),
130                         height: isWindow || isDocument ? withinElement.height() : withinElement.outerHeight()
131                 };
132         }
135 $.fn.position = function( options ) {
136         if ( !options || !options.of ) {
137                 return _position.apply( this, arguments );
138         }
140         // make a copy, we don't want to modify arguments
141         options = $.extend( {}, options );
143         var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
144                 target = $( options.of ),
145                 within = $.position.getWithinInfo( options.within ),
146                 scrollInfo = $.position.getScrollInfo( within ),
147                 collision = ( options.collision || "flip" ).split( " " ),
148                 offsets = {};
150         dimensions = getDimensions( target );
151         if ( target[0].preventDefault ) {
152                 // force left top to allow flipping
153                 options.at = "left top";
154         }
155         targetWidth = dimensions.width;
156         targetHeight = dimensions.height;
157         targetOffset = dimensions.offset;
158         // clone to reuse original targetOffset later
159         basePosition = $.extend( {}, targetOffset );
161         // force my and at to have valid horizontal and vertical positions
162         // if a value is missing or invalid, it will be converted to center
163         $.each( [ "my", "at" ], function() {
164                 var pos = ( options[ this ] || "" ).split( " " ),
165                         horizontalOffset,
166                         verticalOffset;
168                 if ( pos.length === 1) {
169                         pos = rhorizontal.test( pos[ 0 ] ) ?
170                                 pos.concat( [ "center" ] ) :
171                                 rvertical.test( pos[ 0 ] ) ?
172                                         [ "center" ].concat( pos ) :
173                                         [ "center", "center" ];
174                 }
175                 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
176                 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
178                 // calculate offsets
179                 horizontalOffset = roffset.exec( pos[ 0 ] );
180                 verticalOffset = roffset.exec( pos[ 1 ] );
181                 offsets[ this ] = [
182                         horizontalOffset ? horizontalOffset[ 0 ] : 0,
183                         verticalOffset ? verticalOffset[ 0 ] : 0
184                 ];
186                 // reduce to just the positions without the offsets
187                 options[ this ] = [
188                         rposition.exec( pos[ 0 ] )[ 0 ],
189                         rposition.exec( pos[ 1 ] )[ 0 ]
190                 ];
191         });
193         // normalize collision option
194         if ( collision.length === 1 ) {
195                 collision[ 1 ] = collision[ 0 ];
196         }
198         if ( options.at[ 0 ] === "right" ) {
199                 basePosition.left += targetWidth;
200         } else if ( options.at[ 0 ] === "center" ) {
201                 basePosition.left += targetWidth / 2;
202         }
204         if ( options.at[ 1 ] === "bottom" ) {
205                 basePosition.top += targetHeight;
206         } else if ( options.at[ 1 ] === "center" ) {
207                 basePosition.top += targetHeight / 2;
208         }
210         atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
211         basePosition.left += atOffset[ 0 ];
212         basePosition.top += atOffset[ 1 ];
214         return this.each(function() {
215                 var collisionPosition, using,
216                         elem = $( this ),
217                         elemWidth = elem.outerWidth(),
218                         elemHeight = elem.outerHeight(),
219                         marginLeft = parseCss( this, "marginLeft" ),
220                         marginTop = parseCss( this, "marginTop" ),
221                         collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
222                         collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
223                         position = $.extend( {}, basePosition ),
224                         myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
226                 if ( options.my[ 0 ] === "right" ) {
227                         position.left -= elemWidth;
228                 } else if ( options.my[ 0 ] === "center" ) {
229                         position.left -= elemWidth / 2;
230                 }
232                 if ( options.my[ 1 ] === "bottom" ) {
233                         position.top -= elemHeight;
234                 } else if ( options.my[ 1 ] === "center" ) {
235                         position.top -= elemHeight / 2;
236                 }
238                 position.left += myOffset[ 0 ];
239                 position.top += myOffset[ 1 ];
241                 // if the browser doesn't support fractions, then round for consistent results
242                 if ( !supportsOffsetFractions ) {
243                         position.left = round( position.left );
244                         position.top = round( position.top );
245                 }
247                 collisionPosition = {
248                         marginLeft: marginLeft,
249                         marginTop: marginTop
250                 };
252                 $.each( [ "left", "top" ], function( i, dir ) {
253                         if ( $.ui.position[ collision[ i ] ] ) {
254                                 $.ui.position[ collision[ i ] ][ dir ]( position, {
255                                         targetWidth: targetWidth,
256                                         targetHeight: targetHeight,
257                                         elemWidth: elemWidth,
258                                         elemHeight: elemHeight,
259                                         collisionPosition: collisionPosition,
260                                         collisionWidth: collisionWidth,
261                                         collisionHeight: collisionHeight,
262                                         offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
263                                         my: options.my,
264                                         at: options.at,
265                                         within: within,
266                                         elem: elem
267                                 });
268                         }
269                 });
271                 if ( options.using ) {
272                         // adds feedback as second argument to using callback, if present
273                         using = function( props ) {
274                                 var left = targetOffset.left - position.left,
275                                         right = left + targetWidth - elemWidth,
276                                         top = targetOffset.top - position.top,
277                                         bottom = top + targetHeight - elemHeight,
278                                         feedback = {
279                                                 target: {
280                                                         element: target,
281                                                         left: targetOffset.left,
282                                                         top: targetOffset.top,
283                                                         width: targetWidth,
284                                                         height: targetHeight
285                                                 },
286                                                 element: {
287                                                         element: elem,
288                                                         left: position.left,
289                                                         top: position.top,
290                                                         width: elemWidth,
291                                                         height: elemHeight
292                                                 },
293                                                 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
294                                                 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
295                                         };
296                                 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
297                                         feedback.horizontal = "center";
298                                 }
299                                 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
300                                         feedback.vertical = "middle";
301                                 }
302                                 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
303                                         feedback.important = "horizontal";
304                                 } else {
305                                         feedback.important = "vertical";
306                                 }
307                                 options.using.call( this, props, feedback );
308                         };
309                 }
311                 elem.offset( $.extend( position, { using: using } ) );
312         });
315 $.ui.position = {
316         fit: {
317                 left: function( position, data ) {
318                         var within = data.within,
319                                 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
320                                 outerWidth = within.width,
321                                 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
322                                 overLeft = withinOffset - collisionPosLeft,
323                                 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
324                                 newOverRight;
326                         // element is wider than within
327                         if ( data.collisionWidth > outerWidth ) {
328                                 // element is initially over the left side of within
329                                 if ( overLeft > 0 && overRight <= 0 ) {
330                                         newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
331                                         position.left += overLeft - newOverRight;
332                                 // element is initially over right side of within
333                                 } else if ( overRight > 0 && overLeft <= 0 ) {
334                                         position.left = withinOffset;
335                                 // element is initially over both left and right sides of within
336                                 } else {
337                                         if ( overLeft > overRight ) {
338                                                 position.left = withinOffset + outerWidth - data.collisionWidth;
339                                         } else {
340                                                 position.left = withinOffset;
341                                         }
342                                 }
343                         // too far left -> align with left edge
344                         } else if ( overLeft > 0 ) {
345                                 position.left += overLeft;
346                         // too far right -> align with right edge
347                         } else if ( overRight > 0 ) {
348                                 position.left -= overRight;
349                         // adjust based on position and margin
350                         } else {
351                                 position.left = max( position.left - collisionPosLeft, position.left );
352                         }
353                 },
354                 top: function( position, data ) {
355                         var within = data.within,
356                                 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
357                                 outerHeight = data.within.height,
358                                 collisionPosTop = position.top - data.collisionPosition.marginTop,
359                                 overTop = withinOffset - collisionPosTop,
360                                 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
361                                 newOverBottom;
363                         // element is taller than within
364                         if ( data.collisionHeight > outerHeight ) {
365                                 // element is initially over the top of within
366                                 if ( overTop > 0 && overBottom <= 0 ) {
367                                         newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
368                                         position.top += overTop - newOverBottom;
369                                 // element is initially over bottom of within
370                                 } else if ( overBottom > 0 && overTop <= 0 ) {
371                                         position.top = withinOffset;
372                                 // element is initially over both top and bottom of within
373                                 } else {
374                                         if ( overTop > overBottom ) {
375                                                 position.top = withinOffset + outerHeight - data.collisionHeight;
376                                         } else {
377                                                 position.top = withinOffset;
378                                         }
379                                 }
380                         // too far up -> align with top
381                         } else if ( overTop > 0 ) {
382                                 position.top += overTop;
383                         // too far down -> align with bottom edge
384                         } else if ( overBottom > 0 ) {
385                                 position.top -= overBottom;
386                         // adjust based on position and margin
387                         } else {
388                                 position.top = max( position.top - collisionPosTop, position.top );
389                         }
390                 }
391         },
392         flip: {
393                 left: function( position, data ) {
394                         var within = data.within,
395                                 withinOffset = within.offset.left + within.scrollLeft,
396                                 outerWidth = within.width,
397                                 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
398                                 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
399                                 overLeft = collisionPosLeft - offsetLeft,
400                                 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
401                                 myOffset = data.my[ 0 ] === "left" ?
402                                         -data.elemWidth :
403                                         data.my[ 0 ] === "right" ?
404                                                 data.elemWidth :
405                                                 0,
406                                 atOffset = data.at[ 0 ] === "left" ?
407                                         data.targetWidth :
408                                         data.at[ 0 ] === "right" ?
409                                                 -data.targetWidth :
410                                                 0,
411                                 offset = -2 * data.offset[ 0 ],
412                                 newOverRight,
413                                 newOverLeft;
415                         if ( overLeft < 0 ) {
416                                 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
417                                 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
418                                         position.left += myOffset + atOffset + offset;
419                                 }
420                         } else if ( overRight > 0 ) {
421                                 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
422                                 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
423                                         position.left += myOffset + atOffset + offset;
424                                 }
425                         }
426                 },
427                 top: function( position, data ) {
428                         var within = data.within,
429                                 withinOffset = within.offset.top + within.scrollTop,
430                                 outerHeight = within.height,
431                                 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
432                                 collisionPosTop = position.top - data.collisionPosition.marginTop,
433                                 overTop = collisionPosTop - offsetTop,
434                                 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
435                                 top = data.my[ 1 ] === "top",
436                                 myOffset = top ?
437                                         -data.elemHeight :
438                                         data.my[ 1 ] === "bottom" ?
439                                                 data.elemHeight :
440                                                 0,
441                                 atOffset = data.at[ 1 ] === "top" ?
442                                         data.targetHeight :
443                                         data.at[ 1 ] === "bottom" ?
444                                                 -data.targetHeight :
445                                                 0,
446                                 offset = -2 * data.offset[ 1 ],
447                                 newOverTop,
448                                 newOverBottom;
449                         if ( overTop < 0 ) {
450                                 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
451                                 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
452                                         position.top += myOffset + atOffset + offset;
453                                 }
454                         } else if ( overBottom > 0 ) {
455                                 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
456                                 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
457                                         position.top += myOffset + atOffset + offset;
458                                 }
459                         }
460                 }
461         },
462         flipfit: {
463                 left: function() {
464                         $.ui.position.flip.left.apply( this, arguments );
465                         $.ui.position.fit.left.apply( this, arguments );
466                 },
467                 top: function() {
468                         $.ui.position.flip.top.apply( this, arguments );
469                         $.ui.position.fit.top.apply( this, arguments );
470                 }
471         }
474 // fraction support test
475 (function() {
476         var testElement, testElementParent, testElementStyle, offsetLeft, i,
477                 body = document.getElementsByTagName( "body" )[ 0 ],
478                 div = document.createElement( "div" );
480         //Create a "fake body" for testing based on method used in jQuery.support
481         testElement = document.createElement( body ? "div" : "body" );
482         testElementStyle = {
483                 visibility: "hidden",
484                 width: 0,
485                 height: 0,
486                 border: 0,
487                 margin: 0,
488                 background: "none"
489         };
490         if ( body ) {
491                 $.extend( testElementStyle, {
492                         position: "absolute",
493                         left: "-1000px",
494                         top: "-1000px"
495                 });
496         }
497         for ( i in testElementStyle ) {
498                 testElement.style[ i ] = testElementStyle[ i ];
499         }
500         testElement.appendChild( div );
501         testElementParent = body || document.documentElement;
502         testElementParent.insertBefore( testElement, testElementParent.firstChild );
504         div.style.cssText = "position: absolute; left: 10.7432222px;";
506         offsetLeft = $( div ).offset().left;
507         supportsOffsetFractions = offsetLeft > 10 && offsetLeft < 11;
509         testElement.innerHTML = "";
510         testElementParent.removeChild( testElement );
511 })();
513 })();
515 return $.ui.position;
517 }));