mirror of
https://github.com/jquery/jquery-ui.git
synced 2024-11-21 11:04:24 +00:00
493 lines
15 KiB
JavaScript
493 lines
15 KiB
JavaScript
/*!
|
|
* 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 = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></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" ), 10 ) || 0,
|
|
marginTop = parseInt( $.css( this, "marginTop" ), 10 ) || 0,
|
|
scrollInfo = $.position.getScrollInfo( within ),
|
|
collisionWidth = elemWidth + marginLeft +
|
|
( parseInt( $.css( this, "marginRight" ), 10 ) || 0 ) + scrollInfo.width,
|
|
collisionHeight = elemHeight + marginTop +
|
|
( parseInt( $.css( this, "marginBottom" ), 10 ) || 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(),
|
|
offsetLeft = isWindow ? 0 : within.offset().left,
|
|
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
|
|
overLeft = collisionPosLeft - offsetLeft,
|
|
overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
|
|
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 - offsetLeft;
|
|
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(),
|
|
offsetTop = isWindow ? 0 : within.offset().top,
|
|
collisionPosTop = position.top - data.collisionPosition.marginTop,
|
|
overTop = collisionPosTop - offsetTop,
|
|
overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
|
|
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 - offsetTop;
|
|
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 ) );
|