/* * jQuery UI Position @VERSION * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Position */ (function( $, undefined ) { $.ui = $.ui || {}; var rhorizontal = /left|center|right/, rvertical = /top|center|bottom/, roffset = /[+-]\d+%?/, rposition = /^\w+/, rpercent = /%$/, center = "center", _position = $.fn.position; $.position = { scrollbarWidth: function() { var w1, w2, div = $( "
" ), innerDiv = div.children()[0]; $( "body" ).append( div ); w1 = innerDiv.offsetWidth; div.css( "overflow", "scroll" ); w2 = innerDiv.offsetWidth; if ( w1 === w2 ) { w2 = div[0].clientWidth; } div.remove(); return w1 - w2; }, getScrollInfo: function(within) { var notWindow = within[0] !== window, overflowX = notWindow ? within.css( "overflow-x" ) : "", overflowY = notWindow ? within.css( "overflow-y" ) : "", scrollbarWidth = overflowX === "auto" || overflowX === "scroll" ? $.position.scrollbarWidth() : 0, scrollbarHeight = overflowY === "auto" || overflowY === "scroll" ? $.position.scrollbarWidth() : 0; return { height: within.height() < within[0].scrollHeight ? scrollbarHeight : 0, width: within.width() < within[0].scrollWidth ? scrollbarWidth : 0 }; } }; $.fn.position = function( options ) { if ( !options || !options.of ) { return _position.apply( this, arguments ); } // make a copy, we don't want to modify arguments options = $.extend( {}, options ); var target = $( options.of ), within = $( options.within || window ), targetElem = target[0], collision = ( options.collision || "flip" ).split( " " ), offsets = {}, atOffset, targetWidth, targetHeight, basePosition; if ( targetElem.nodeType === 9 ) { targetWidth = target.width(); targetHeight = target.height(); basePosition = { top: 0, left: 0 }; } else if ( $.isWindow( targetElem ) ) { targetWidth = target.width(); targetHeight = target.height(); basePosition = { top: target.scrollTop(), left: target.scrollLeft() }; } else if ( targetElem.preventDefault ) { // force left top to allow flipping options.at = "left top"; targetWidth = targetHeight = 0; basePosition = { top: options.of.pageY, left: options.of.pageX }; } else { targetWidth = target.outerWidth(); targetHeight = target.outerHeight(); basePosition = target.offset(); } // force my and at to have valid horizontal and vertical positions // if a value is missing or invalid, it will be converted to center $.each( [ "my", "at" ], function() { var pos = ( options[ this ] || "" ).split( " " ), horizontalOffset, verticalOffset; if ( pos.length === 1) { pos = rhorizontal.test( pos[ 0 ] ) ? pos.concat( [ center ] ) : rvertical.test( pos[ 0 ] ) ? [ center ].concat( pos ) : [ center, center ]; } pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : center; pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : center; // calculate offsets horizontalOffset = roffset.exec( pos[ 0 ] ); verticalOffset = roffset.exec( pos[ 1 ] ); offsets[ this ] = [ horizontalOffset ? horizontalOffset[ 0 ] : 0, verticalOffset ? verticalOffset[ 0 ] : 0 ]; // reduce to just the positions without the offsets options[ this ] = [ rposition.exec( pos[ 0 ] )[ 0 ], rposition.exec( pos[ 1 ] )[ 0 ] ]; }); // normalize collision option if ( collision.length === 1 ) { collision[ 1 ] = collision[ 0 ]; } if ( options.at[ 0 ] === "right" ) { basePosition.left += targetWidth; } else if ( options.at[ 0 ] === center ) { basePosition.left += targetWidth / 2; } if ( options.at[ 1 ] === "bottom" ) { basePosition.top += targetHeight; } else if ( options.at[ 1 ] === center ) { basePosition.top += targetHeight / 2; } atOffset = [ parseInt( offsets.at[ 0 ], 10 ) * ( rpercent.test( offsets.at[ 0 ] ) ? targetWidth / 100 : 1 ), parseInt( offsets.at[ 1 ], 10 ) * ( rpercent.test( offsets.at[ 1 ] ) ? targetHeight / 100 : 1 ) ]; basePosition.left += atOffset[ 0 ]; basePosition.top += atOffset[ 1 ]; return this.each(function() { var elem = $( this ), elemWidth = elem.outerWidth(), elemHeight = elem.outerHeight(), marginLeft = parseInt( $.css( this, "marginLeft" ) ) || 0, marginTop = parseInt( $.css( this, "marginTop" ) ) || 0, scrollInfo = $.position.getScrollInfo( within ), collisionWidth = elemWidth + marginLeft + ( parseInt( $.css( this, "marginRight" ) ) || 0 ) + scrollInfo.width, collisionHeight = elemHeight + marginTop + ( parseInt( $.css( this, "marginBottom" ) ) || 0 ) + scrollInfo.height, position = $.extend( {}, basePosition ), myOffset = [ parseInt( offsets.my[ 0 ], 10 ) * ( rpercent.test( offsets.my[ 0 ] ) ? elem.outerWidth() / 100 : 1 ), parseInt( offsets.my[ 1 ], 10 ) * ( rpercent.test( offsets.my[ 1 ] ) ? elem.outerHeight() / 100 : 1 ) ], collisionPosition; if ( options.my[ 0 ] === "right" ) { position.left -= elemWidth; } else if ( options.my[ 0 ] === center ) { position.left -= elemWidth / 2; } if ( options.my[ 1 ] === "bottom" ) { position.top -= elemHeight; } else if ( options.my[ 1 ] === center ) { position.top -= elemHeight / 2; } position.left += myOffset[ 0 ]; position.top += myOffset[ 1 ]; // if the browser doesn't support fractions, then round for consistent results if ( !$.support.offsetFractions ) { position.left = Math.round( position.left ); position.top = Math.round( position.top ); } collisionPosition = { marginLeft: marginLeft, marginTop: marginTop }; $.each( [ "left", "top" ], function( i, dir ) { if ( $.ui.position[ collision[ i ] ] ) { $.ui.position[ collision[ i ] ][ dir ]( position, { targetWidth: targetWidth, targetHeight: targetHeight, elemWidth: elemWidth, elemHeight: elemHeight, collisionPosition: collisionPosition, collisionWidth: collisionWidth, collisionHeight: collisionHeight, offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], my: options.my, at: options.at, within: within, elem : elem }); } }); if ( $.fn.bgiframe ) { elem.bgiframe(); } elem.offset( $.extend( position, { using: options.using } ) ); }); }; $.ui.position = { fit: { left: function( position, data ) { var within = data.within, win = $( window ), isWindow = $.isWindow( data.within[0] ), withinOffset = isWindow ? win.scrollLeft() : within.offset().left, outerWidth = isWindow ? win.width() : within.outerWidth(), collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = withinOffset - collisionPosLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, newOverRight, newOverLeft; // element is wider than within if ( data.collisionWidth > outerWidth ) { // element is initially over the left side of within if ( overLeft > 0 && overRight <= 0 ) { newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; position.left += overLeft - newOverRight; // element is initially over right side of within } else if ( overRight > 0 && overLeft <= 0 ) { position.left = withinOffset; // element is initially over both left and right sides of within } else { if ( overLeft > overRight ) { position.left = withinOffset + outerWidth - data.collisionWidth; } else { position.left = withinOffset; } } // too far left -> align with left edge } else if ( overLeft > 0 ) { position.left += overLeft; // too far right -> align with right edge } else if ( overRight > 0 ) { position.left -= overRight; // adjust based on position and margin } else { position.left = Math.max( position.left - collisionPosLeft, position.left ); } }, top: function( position, data ) { var within = data.within, win = $( window ), isWindow = $.isWindow( data.within[0] ), withinOffset = isWindow ? win.scrollTop() : within.offset().top, outerHeight = isWindow ? win.height() : within.outerHeight(), collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = withinOffset - collisionPosTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, newOverTop, newOverBottom; // element is taller than within if ( data.collisionHeight > outerHeight ) { // element is initially over the top of within if ( overTop > 0 && overBottom <= 0 ) { newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; position.top += overTop - newOverBottom; // element is initially over bottom of within } else if ( overBottom > 0 && overTop <= 0 ) { position.top = withinOffset; // element is initially over both top and bottom of within } else { if ( overTop > overBottom ) { position.top = withinOffset + outerHeight - data.collisionHeight; } else { position.top = withinOffset; } } // too far up -> align with top } else if ( overTop > 0 ) { position.top += overTop; // too far down -> align with bottom edge } else if ( overBottom > 0 ) { position.top -= overBottom; // adjust based on position and margin } else { position.top = Math.max( position.top - collisionPosTop, position.top ); } } }, flip: { left: function( position, data ) { if ( data.at[ 0 ] === center ) { return; } data.elem .removeClass( "ui-flipped-left ui-flipped-right" ); var within = data.within, win = $( window ), isWindow = $.isWindow( data.within[0] ), withinOffset = ( isWindow ? 0 : within.offset().left ) + within.scrollLeft(), outerWidth = isWindow ? within.width() : within.outerWidth(), collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = collisionPosLeft - withinOffset, overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, left = data.my[ 0 ] === "left", myOffset = data.my[ 0 ] === "left" ? -data.elemWidth : data.my[ 0 ] === "right" ? data.elemWidth : 0, atOffset = data.at[ 0 ] === "left" ? data.targetWidth : -data.targetWidth, offset = -2 * data.offset[ 0 ], newOverRight, newOverLeft; if ( overLeft < 0 ) { newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; if ( newOverRight < 0 || newOverRight < Math.abs( overLeft ) ) { data.elem .addClass( "ui-flipped-right" ); position.left += myOffset + atOffset + offset; } } else if ( overRight > 0 ) { newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - withinOffset; if ( newOverLeft > 0 || Math.abs( newOverLeft ) < overRight ) { data.elem .addClass( "ui-flipped-left" ); position.left += myOffset + atOffset + offset; } } }, top: function( position, data ) { if ( data.at[ 1 ] === center ) { return; } data.elem .removeClass( "ui-flipped-top ui-flipped-bottom" ); var within = data.within, win = $( window ), isWindow = $.isWindow( data.within[0] ), withinOffset = ( isWindow ? 0 : within.offset().top ) + within.scrollTop(), outerHeight = isWindow ? within.height() : within.outerHeight(), collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = collisionPosTop - withinOffset, overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, top = data.my[ 1 ] === "top", myOffset = top ? -data.elemHeight : data.my[ 1 ] === "bottom" ? data.elemHeight : 0, atOffset = data.at[ 1 ] === "top" ? data.targetHeight : -data.targetHeight, offset = -2 * data.offset[ 1 ], newOverTop, newOverBottom; if ( overTop < 0 ) { newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < Math.abs( overTop ) ) ) { data.elem .addClass( "ui-flipped-bottom" ); position.top += myOffset + atOffset + offset; } } else if ( overBottom > 0 ) { newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - withinOffset; if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || Math.abs( newOverTop ) < overBottom ) ) { data.elem .addClass( "ui-flipped-top" ); position.top += myOffset + atOffset + offset; } } } }, flipfit: { left: function() { $.ui.position.flip.left.apply( this, arguments ); $.ui.position.fit.left.apply( this, arguments ); }, top: function() { $.ui.position.flip.top.apply( this, arguments ); $.ui.position.fit.top.apply( this, arguments ); } } }; // fraction support test (function () { var testElement, testElementParent, testElementStyle, offsetLeft, i, body = document.getElementsByTagName( "body" )[ 0 ], div = document.createElement( "div" ); //Create a "fake body" for testing based on method used in jQuery.support testElement = document.createElement( body ? "div" : "body" ); testElementStyle = { visibility: "hidden", width: 0, height: 0, border: 0, margin: 0, background: "none" }; if ( body ) { $.extend( testElementStyle, { position: "absolute", left: "-1000px", top: "-1000px" }); } for ( i in testElementStyle ) { testElement.style[ i ] = testElementStyle[ i ]; } testElement.appendChild( div ); testElementParent = body || document.documentElement; testElementParent.insertBefore( testElement, testElementParent.firstChild ); div.style.cssText = "position: absolute; left: 10.7432222px;"; offsetLeft = $( div ).offset().left; $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11; testElement.innerHTML = ""; testElementParent.removeChild( testElement ); })(); // DEPRECATED if ( $.uiBackCompat !== false ) { // offset option (function( $ ) { var _position = $.fn.position; $.fn.position = function( options ) { if ( !options || !options.offset ) { return _position.call( this, options ); } var offset = options.offset.split( " " ), at = options.at.split( " " ); if ( offset.length === 1 ) { offset[ 1 ] = offset[ 0 ]; } if ( /^\d/.test( offset[ 0 ] ) ) { offset[ 0 ] = "+" + offset[ 0 ]; } if ( /^\d/.test( offset[ 1 ] ) ) { offset[ 1 ] = "+" + offset[ 1 ]; } if ( at.length === 1 ) { if ( /left|center|right/.test( at[ 0 ] ) ) { at[ 1 ] = "center"; } else { at[ 1 ] = at[ 0 ]; at[ 0 ] = "center"; } } return _position.call( this, $.extend( options, { at: at[ 0 ] + offset[ 0 ] + " " + at[ 1 ] + offset[ 1 ], offset: undefined } ) ); } }( jQuery ) ); } }( jQuery ) );