2 * jQuery UI Position @ dfe75e1
4 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
8 * http://docs.jquery.com/UI/Position
10 (function( $, undefined ) {
14 var rhorizontal = /left|center|right/,
15 rvertical = /top|center|bottom/,
16 roffset = /[+-]\d+%?/,
20 _position = $.fn.position;
23 scrollbarWidth: function() {
25 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
26 innerDiv = div.children()[0];
28 $( "body" ).append( div );
29 w1 = innerDiv.offsetWidth;
30 div.css( "overflow", "scroll" );
32 w2 = innerDiv.offsetWidth;
35 w2 = div[0].clientWidth;
42 getScrollInfo: function(within) {
43 var notWindow = within[0] !== window,
44 overflowX = notWindow ? within.css( "overflow-x" ) : "",
45 overflowY = notWindow ? within.css( "overflow-y" ) : "",
46 scrollbarWidth = overflowX === "auto" || overflowX === "scroll" ? $.position.scrollbarWidth() : 0,
47 scrollbarHeight = overflowY === "auto" || overflowY === "scroll" ? $.position.scrollbarWidth() : 0;
50 height: within.height() < within[0].scrollHeight ? scrollbarHeight : 0,
51 width: within.width() < within[0].scrollWidth ? scrollbarWidth : 0
56 $.fn.position = function( options ) {
57 if ( !options || !options.of ) {
58 return _position.apply( this, arguments );
61 // make a copy, we don't want to modify arguments
62 options = $.extend( {}, options );
64 var target = $( options.of ),
65 within = $( options.within || window ),
66 targetElem = target[0],
67 collision = ( options.collision || "flip" ).split( " " ),
74 if ( targetElem.nodeType === 9 ) {
75 targetWidth = target.width();
76 targetHeight = target.height();
77 basePosition = { top: 0, left: 0 };
78 } else if ( $.isWindow( targetElem ) ) {
79 targetWidth = target.width();
80 targetHeight = target.height();
81 basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
82 } else if ( targetElem.preventDefault ) {
83 // force left top to allow flipping
84 options.at = "left top";
85 targetWidth = targetHeight = 0;
86 basePosition = { top: options.of.pageY, left: options.of.pageX };
88 targetWidth = target.outerWidth();
89 targetHeight = target.outerHeight();
90 basePosition = target.offset();
93 // force my and at to have valid horizontal and vertical positions
94 // if a value is missing or invalid, it will be converted to center
95 $.each( [ "my", "at" ], function() {
96 var pos = ( options[ this ] || "" ).split( " " ),
100 if ( pos.length === 1) {
101 pos = rhorizontal.test( pos[ 0 ] ) ?
102 pos.concat( [ center ] ) :
103 rvertical.test( pos[ 0 ] ) ?
104 [ center ].concat( pos ) :
107 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : center;
108 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : center;
111 horizontalOffset = roffset.exec( pos[ 0 ] );
112 verticalOffset = roffset.exec( pos[ 1 ] );
114 horizontalOffset ? horizontalOffset[ 0 ] : 0,
115 verticalOffset ? verticalOffset[ 0 ] : 0
118 // reduce to just the positions without the offsets
120 rposition.exec( pos[ 0 ] )[ 0 ],
121 rposition.exec( pos[ 1 ] )[ 0 ]
125 // normalize collision option
126 if ( collision.length === 1 ) {
127 collision[ 1 ] = collision[ 0 ];
130 if ( options.at[ 0 ] === "right" ) {
131 basePosition.left += targetWidth;
132 } else if ( options.at[ 0 ] === center ) {
133 basePosition.left += targetWidth / 2;
136 if ( options.at[ 1 ] === "bottom" ) {
137 basePosition.top += targetHeight;
138 } else if ( options.at[ 1 ] === center ) {
139 basePosition.top += targetHeight / 2;
143 parseInt( offsets.at[ 0 ], 10 ) *
144 ( rpercent.test( offsets.at[ 0 ] ) ? targetWidth / 100 : 1 ),
145 parseInt( offsets.at[ 1 ], 10 ) *
146 ( rpercent.test( offsets.at[ 1 ] ) ? targetHeight / 100 : 1 )
148 basePosition.left += atOffset[ 0 ];
149 basePosition.top += atOffset[ 1 ];
151 return this.each(function() {
152 var elem = $( this ),
153 elemWidth = elem.outerWidth(),
154 elemHeight = elem.outerHeight(),
155 marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
156 marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
157 scrollInfo = $.position.getScrollInfo( within ),
158 collisionWidth = elemWidth + marginLeft +
159 ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ) + scrollInfo.width,
160 collisionHeight = elemHeight + marginTop +
161 ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ) + scrollInfo.height,
162 position = $.extend( {}, basePosition ),
164 parseInt( offsets.my[ 0 ], 10 ) *
165 ( rpercent.test( offsets.my[ 0 ] ) ? elem.outerWidth() / 100 : 1 ),
166 parseInt( offsets.my[ 1 ], 10 ) *
167 ( rpercent.test( offsets.my[ 1 ] ) ? elem.outerHeight() / 100 : 1 )
171 if ( options.my[ 0 ] === "right" ) {
172 position.left -= elemWidth;
173 } else if ( options.my[ 0 ] === center ) {
174 position.left -= elemWidth / 2;
177 if ( options.my[ 1 ] === "bottom" ) {
178 position.top -= elemHeight;
179 } else if ( options.my[ 1 ] === center ) {
180 position.top -= elemHeight / 2;
183 position.left += myOffset[ 0 ];
184 position.top += myOffset[ 1 ];
186 collisionPosition = {
187 marginLeft: marginLeft,
191 $.each( [ "left", "top" ], function( i, dir ) {
192 if ( $.ui.position[ collision[ i ] ] ) {
193 $.ui.position[ collision[ i ] ][ dir ]( position, {
194 targetWidth: targetWidth,
195 targetHeight: targetHeight,
196 elemWidth: elemWidth,
197 elemHeight: elemHeight,
198 collisionPosition: collisionPosition,
199 collisionWidth: collisionWidth,
200 collisionHeight: collisionHeight,
201 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
210 if ( $.fn.bgiframe ) {
213 elem.offset( $.extend( position, { using: options.using } ) );
219 left: function( position, data ) {
220 var within = data.within,
222 isWindow = $.isWindow( data.within[0] ),
223 withinOffset = isWindow ? win.scrollLeft() : within.offset().left,
224 outerWidth = isWindow ? win.width() : within.outerWidth(),
225 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
226 overLeft = withinOffset - collisionPosLeft,
227 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
231 // element is wider than within
232 if ( data.collisionWidth > outerWidth ) {
233 // element is initially over the left side of within
234 if ( overLeft > 0 && overRight <= 0 ) {
235 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
236 position.left += overLeft - newOverRight;
237 // element is initially over right side of within
238 } else if ( overRight > 0 && overLeft <= 0 ) {
239 position.left = withinOffset;
240 // element is initially over both left and right sides of within
242 if ( overLeft > overRight ) {
243 position.left = withinOffset + outerWidth - data.collisionWidth;
245 position.left = withinOffset;
248 // too far left -> align with left edge
249 } else if ( overLeft > 0 ) {
250 position.left += overLeft;
251 // too far right -> align with right edge
252 } else if ( overRight > 0 ) {
253 position.left -= overRight;
254 // adjust based on position and margin
256 position.left = Math.max( position.left - collisionPosLeft, position.left );
259 top: function( position, data ) {
260 var within = data.within,
262 isWindow = $.isWindow( data.within[0] ),
263 withinOffset = isWindow ? win.scrollTop() : within.offset().top,
264 outerHeight = isWindow ? win.height() : within.outerHeight(),
265 collisionPosTop = position.top - data.collisionPosition.marginTop,
266 overTop = withinOffset - collisionPosTop,
267 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
271 // element is taller than within
272 if ( data.collisionHeight > outerHeight ) {
273 // element is initially over the top of within
274 if ( overTop > 0 && overBottom <= 0 ) {
275 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
276 position.top += overTop - newOverBottom;
277 // element is initially over bottom of within
278 } else if ( overBottom > 0 && overTop <= 0 ) {
279 position.top = withinOffset;
280 // element is initially over both top and bottom of within
282 if ( overTop > overBottom ) {
283 position.top = withinOffset + outerHeight - data.collisionHeight;
285 position.top = withinOffset;
288 // too far up -> align with top
289 } else if ( overTop > 0 ) {
290 position.top += overTop;
291 // too far down -> align with bottom edge
292 } else if ( overBottom > 0 ) {
293 position.top -= overBottom;
294 // adjust based on position and margin
296 position.top = Math.max( position.top - collisionPosTop, position.top );
301 left: function( position, data ) {
302 if ( data.at[ 0 ] === center ) {
307 .removeClass( "ui-flipped-left ui-flipped-right" );
309 var within = data.within,
311 isWindow = $.isWindow( data.within[0] ),
312 withinOffset = ( isWindow ? 0 : within.offset().left ) + within.scrollLeft(),
313 outerWidth = isWindow ? within.width() : within.outerWidth(),
314 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
315 overLeft = collisionPosLeft - withinOffset,
316 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
317 left = data.my[ 0 ] === "left",
318 myOffset = data.my[ 0 ] === "left" ?
320 data.my[ 0 ] === "right" ?
323 atOffset = data.at[ 0 ] === "left" ?
326 offset = -2 * data.offset[ 0 ],
330 if ( overLeft < 0 ) {
331 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
332 if ( newOverRight < 0 || newOverRight < Math.abs( overLeft ) ) {
334 .addClass( "ui-flipped-right" );
336 position.left += myOffset + atOffset + offset;
339 else if ( overRight > 0 ) {
340 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - withinOffset;
341 if ( newOverLeft > 0 || Math.abs( newOverLeft ) < overRight ) {
343 .addClass( "ui-flipped-left" );
345 position.left += myOffset + atOffset + offset;
349 top: function( position, data ) {
350 if ( data.at[ 1 ] === center ) {
355 .removeClass( "ui-flipped-top ui-flipped-bottom" );
357 var within = data.within,
359 isWindow = $.isWindow( data.within[0] ),
360 withinOffset = ( isWindow ? 0 : within.offset().top ) + within.scrollTop(),
361 outerHeight = isWindow ? within.height() : within.outerHeight(),
362 collisionPosTop = position.top - data.collisionPosition.marginTop,
363 overTop = collisionPosTop - withinOffset,
364 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
365 top = data.my[ 1 ] === "top",
368 data.my[ 1 ] === "bottom" ?
371 atOffset = data.at[ 1 ] === "top" ?
374 offset = -2 * data.offset[ 1 ],
378 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
379 if ( newOverBottom < 0 || newOverBottom < Math.abs( overTop ) ) {
381 .addClass( "ui-flipped-bottom" );
383 position.top += myOffset + atOffset + offset;
386 else if ( overBottom > 0 ) {
387 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - withinOffset;
388 if ( newOverTop > 0 || Math.abs( newOverTop ) < overBottom ) {
390 .addClass( "ui-flipped-top" );
392 position.top += myOffset + atOffset + offset;
399 $.ui.position.flip.left.apply( this, arguments );
400 $.ui.position.fit.left.apply( this, arguments );
403 $.ui.position.flip.top.apply( this, arguments );
404 $.ui.position.fit.top.apply( this, arguments );
410 if ( $.uiBackCompat !== false ) {
413 var _position = $.fn.position;
414 $.fn.position = function( options ) {
415 if ( !options || !options.offset ) {
416 return _position.call( this, options );
418 var offset = options.offset.split( " " ),
419 at = options.at.split( " " );
420 if ( offset.length === 1 ) {
421 offset[ 1 ] = offset[ 0 ];
423 if ( /^\d/.test( offset[ 0 ] ) ) {
424 offset[ 0 ] = "+" + offset[ 0 ];
426 if ( /^\d/.test( offset[ 1 ] ) ) {
427 offset[ 1 ] = "+" + offset[ 1 ];
429 if ( at.length === 1 ) {
430 if ( /left|center|right/.test( at[ 0 ] ) ) {
437 return _position.call( this, $.extend( options, {
438 at: at[ 0 ] + offset[ 0 ] + " " + at[ 1 ] + offset[ 1 ],