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