Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Position: New notification API #630

Merged
merged 34 commits into from

3 participants

@jzaefferer
Owner

First draft for a new notification API, via using callback, telling you were the of-element is, not just when something flipped. New test page demonstrates usage.

This will make it possible to build widgets upon position that have callouts built-in. With the previous flipped classes all you could do was to flip your callout when you know exactly where it was supposed to be in the first place. That can be done only inside an application, not a widget.

Preview here: http://view.jqueryui.com/position-notification/tests/visual/position/position_feedback.html

Preview

For review only, for now.

@jzaefferer jzaefferer Position: First draft for a new notification API, via using callback,…
… telling you were the of-element is, not just when something flipped. New test page demonstrates usage
479530b
tests/visual/position/position.html
((14 lines not shown))
my: "left top+20",
at: "left bottom",
of: this,
+ using: function( position ) {
@scottgonzalez Owner

I'd like to see the additional data in a separate parameter so that users can continue to cleanly pass position to .css() or .animate().

@jzaefferer Owner

Fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -216,6 +216,27 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
+ var using = options.using;
+ if ( using ) {
+ // we have to proxy, as jQuery.offset.setOffset throws away other props then left/top
@scottgonzalez Owner

"other props then" -> "props other than"

@jzaefferer Owner

Fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -216,6 +216,27 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
+ var using = options.using;
@scottgonzalez Owner

single var declaration at the top of the function

@jzaefferer Owner

Fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -216,6 +216,27 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
+ var using = options.using;
+ if ( using ) {
+ // we have to proxy, as jQuery.offset.setOffset throws away other props then left/top
+ options.using = function( props ) {
+ // can't use basePosition, as that gets modified
+ var targetOffset = target.offset(),
+ left = targetOffset.left - props.left,
+ right = (targetOffset.left + targetWidth) - (props.left + elemWidth),
+ top = targetOffset.top - props.top,
+ bottom = (targetOffset.top + targetHeight) - (props.top + elemHeight);
@scottgonzalez Owner

Do we have this above? Would it make sense to just $.extend() it ahead of time? At least caching the offset would probably be good.

@jzaefferer Owner

We don't have the bottom and right values, but I've refactored it to reuse targetOffset, by cloning that once at the beginning. Leaves us with no extra DOM access, so performance overhead of this feature is very low.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -216,6 +216,27 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
+ var using = options.using;
+ if ( using ) {
+ // we have to proxy, as jQuery.offset.setOffset throws away other props then left/top
+ options.using = function( props ) {
+ // can't use basePosition, as that gets modified
+ var targetOffset = target.offset(),
+ left = targetOffset.left - props.left,
+ right = (targetOffset.left + targetWidth) - (props.left + elemWidth),
+ top = targetOffset.top - props.top,
+ bottom = (targetOffset.top + targetHeight) - (props.top + elemHeight);
+ var feedback = {};
@scottgonzalez Owner

single var

@jzaefferer Owner

Fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -216,6 +216,27 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
+ var using = options.using;
+ if ( using ) {
+ // we have to proxy, as jQuery.offset.setOffset throws away other props then left/top
+ options.using = function( props ) {
+ // can't use basePosition, as that gets modified
+ var targetOffset = target.offset(),
+ left = targetOffset.left - props.left,
+ right = (targetOffset.left + targetWidth) - (props.left + elemWidth),
+ top = targetOffset.top - props.top,
+ bottom = (targetOffset.top + targetHeight) - (props.top + elemHeight);
+ var feedback = {};
+ feedback.horizontal = right < 0 ? "left" : left > 0 ? "right" : "center";
@scottgonzalez Owner

These are small, why not just set them inline when defining feedback?

@jzaefferer Owner

Fixed, didn't see that when refactoring into a separate object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -216,6 +216,27 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
+ var using = options.using;
+ if ( using ) {
+ // we have to proxy, as jQuery.offset.setOffset throws away other props then left/top
+ options.using = function( props ) {
+ // can't use basePosition, as that gets modified
+ var targetOffset = target.offset(),
+ left = targetOffset.left - props.left,
+ right = (targetOffset.left + targetWidth) - (props.left + elemWidth),
+ top = targetOffset.top - props.top,
+ bottom = (targetOffset.top + targetHeight) - (props.top + elemHeight);
+ var feedback = {};
+ feedback.horizontal = right < 0 ? "left" : left > 0 ? "right" : "center";
+ feedback.vertical = bottom < 0 ? "top" : top > 0 ? "bottom" : "middle";
+ if (Math.max(Math.abs(left), Math.abs(right)) > Math.max(Math.abs(top), Math.abs(bottom))) {
@scottgonzalez Owner

spacing

@jzaefferer Owner

Fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jzaefferer
Owner

Addressed all comments. Units pass, test pages look the same. Added a screenshot to the PR description.

ui/jquery.ui.position.js
@@ -216,6 +220,27 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
+
+ if ( using ) {
+ // we have to proxy, as jQuery.offset.setOffset throws away props other than left/top
+ options.using = function( props ) {
@scottgonzalez Owner

Won't this proxy itself over and over for each element? We're inside a this.each() where each iteration is using the same options object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
jzaefferer added some commits
@jzaefferer jzaefferer Position: Mini refactoring, avoids proxying using callback multiple t…
…imes if more then one element is positioned
623e8e6
@jzaefferer jzaefferer Position: Extend feedback test page to include two mouse-positioned e…
…lements, highlights the 0px center/middle limitation

Also rename the demo file to match the variables names, 'feedback', instead of 'notification'
2602612
@jzaefferer jzaefferer Position: Improve feedback API by giving the center/middle position m…
…ore weight. Also removed themeswitcher from test pages, now load faster
d077f9b
@jzaefferer
Owner

The demo now includes four mouse-following elements. The issue the previous algorithm had is now gone, and the overhead ended up being really small. Just a little more inline math, nothing fancy.

@jzaefferer
Owner

See also https://gist.github.com/2366588

https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L160
calls https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L42
which may call https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L23
repeatedly without the return value of $.scrollbarWidth() ever changing - cache the value!

https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L146
offset calculation is done 4 times, enough to make a function of it?

https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L158
parseInt( $.css(), …) || 0 is done 4 times, enough to make a function of it?

https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L235
repeated calls to Math.abs et al may benefit from caching the reference to avoid scope lookups

https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L260
depending on the number of elements to align within one .position() call, offsets, widths, etc. are calculated over and over again. maybe these can be cached / provided by the callee to avoid multiple execution? (same for $.ui.position.fit.top and $.ui.position.flip.*, obviously)

https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L175
https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L177
why is one a literal string and the other a variable? consistency?

Regarding the using callback:
pass the target element so using() may calculate the angle between the two elements to rotate a pointer accordingly

@jzaefferer
Owner

@scottgonzalez @rodneyrehm I'm done with the refactorings, if you want to take another look

@rodneyrehm

https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L45
`$.position.scrollbarWidth()? is (potentially) still executed twice

https://github.com/jquery/jquery-ui/blob/position-notification/ui/jquery.ui.position.js#L83
if the scrollbarWidth is a global constant, make it one. Or does this expect different containers having differently sized scrollbars? (theoretically possible, I guess)

While we are taking flea-shit-optimiziation here, see http://jsperf.com/accessing-scopes in respect to always addressing functions' full namespace instead of "caching references".

@jzaefferer
Owner

@rodneyrehm while performance tuning that wasn't originally a goal of this PR, I've implemented most anyway, as long as it reduces DOM access. The accessor stuff is going a little too far.

Anyway, I've landed three more refactorings. Will now do at most one calculation of scrollbar width. getScrollInfo reuses other data that we calculate for within anyway.

Probably easier to follow along when you look at the last three commits, instead of the full diff.

@rodneyrehm

looking good!

Don't worry about the coordinate calculation thing. It's a "nice to have" anyways…

@jzaefferer
Owner

Oh, that's actually one more thing I wanted to look at. We've got the data to calculate the center of both elements at hand, so adding a tan2 call would be easy enough.

@rodneyrehm

I'm not sure the center of both elements is always the thing to go with.

Consider aligning an the "bottom left" of one element with "top right" of another. If both elements are of equal dimension, the angle would be 45°. and the center-based angle would be fine. Should the elements differ in size, though, the center-based angle would deviate from 45° - while that may still be the angle between the "top right" and "bottom left" coordinates.

With the above I argue that position() should not do any calculations of the sort. I'd rather see {top:123, left:123, width:123, height:123} returned (supplied to using). It would, in this case, also help if alignments and offsets parsed from at and my be provided to using. reconstructing "bottom right" from that data is trivial and can be done in the using callback (if and when required).

@jzaefferer jzaefferer Position: Expose target and element dimensions to allow further custo…
…mization, like calculating the angle between the two elements
a5fc9eb
@jzaefferer
Owner

Thanks for the feedback. The last commit added that data, allowing you to calculate whatever you want with very little overhead. I don't see a way to use dynamic transforms on before/after elements, so added this for now: http://view.jqueryui.com/position-notification/tests/visual/position/position_feedback_rotate.html

@rodneyrehm

muha? AWESOME!

jzaefferer added some commits
@jzaefferer jzaefferer Tooltip: Remove bad docs links from headers
5cf3f92
@jzaefferer jzaefferer Tooltip: Update custom-style demo, remove the ugly padding that was h…
…iding layout issues, use new position feedback API instead of position callout in more then just one place
db592b7
ui/jquery.ui.position.js
((28 lines not shown))
+ element: withinElement,
+ isWindow: isWindow,
+ offset: withinElement.offset(),
+ scrollLeft: withinElement.scrollLeft(),
+ scrollTop: withinElement.scrollTop(),
+ width: isWindow ? withinElement.width() : withinElement.outerWidth(),
+ height: isWindow ? withinElement.height() : withinElement.outerHeight()
+ };
+ },
+ getOffsets: function( offsets, width, height ) {
+ return [
+ parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
+ parseInt( offsets[ 1 ], 10 ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
+ ];
+ },
+ parseCss: function( element, property ) {
@scottgonzalez Owner

There's not really any reason to expose this. We should just move it into a local function. That's probably true of all of these methods.

@jzaefferer Owner

Made getOffsets and parseCss local methods. The others are used in unit tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
((7 lines not shown))
targetElem = target[0],
collision = ( options.collision || "flip" ).split( " " ),
offsets = {},
atOffset,
targetWidth,
targetHeight,
+ targetOffset,
basePosition;
@scottgonzalez Owner

All of the undefined vars should move to the first line of the var.

@scottgonzalez Owner

Yup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
((15 lines not shown))
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;
+ myOffset = $.position.getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ),
+ collisionPosition,
+ using;
@scottgonzalez Owner

Move undefined vars to first line.

@jzaefferer Owner

Done and done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
((29 lines not shown))
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,
+ withinOffset = ( within.isWindow ? 0 : within.offset.top ) + within.scrollTop,
@scottgonzalez Owner

Shouldn't it be safe to always access within.offset.top?

@jzaefferer Owner

Can't, as $(window).offset() returns null. Any thoughts? Otherwise I think I've addressed everything from this latest review.

@scottgonzalez Owner

I meant that the abstraction is a bit broken if it's not safe. Shouldn't getWithinInfo() handle this for us? Probably just offset: withinElement.offset() || { top: 0, left: 0 }.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
((12 lines not shown))
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,
+ withinOffset = ( within.isWindow ? 0 : within.offset.left ) + within.scrollLeft,
@scottgonzalez Owner

see comment on L406

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@scottgonzalez scottgonzalez commented on the diff
ui/jquery.ui.position.js
((14 lines not shown))
+ target: {
+ element: target,
+ left: targetOffset.left,
+ top: targetOffset.top,
+ width: targetWidth,
+ height: targetHeight
+ },
+ element: {
+ element: elem,
+ left: props.left,
+ top: props.top,
+ width: elemWidth,
+ height: elemHeight
+ },
+ horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
+ vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
@scottgonzalez Owner

If you're going to cache these, you should do it at the very top of the file so it only happens once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@scottgonzalez scottgonzalez commented on the diff
ui/jquery.ui.position.js
@@ -337,36 +383,25 @@ $.ui.position = {
@scottgonzalez Owner

abs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -337,36 +383,25 @@ $.ui.position = {
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;
}
}
@scottgonzalez Owner

abs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -385,18 +420,12 @@ $.ui.position = {
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;
}
}
@scottgonzalez Owner

abs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@scottgonzalez scottgonzalez commented on the diff
ui/jquery.ui.position.js
@@ -216,7 +228,50 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
- elem.offset( $.extend( position, { using: options.using } ) );
+
+ if ( options.using ) {
+ // adds feedback as second argument to using callback, if present
@scottgonzalez Owner

left + targetWidth - elemWidth

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ui/jquery.ui.position.js
@@ -216,7 +228,50 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
- elem.offset( $.extend( position, { using: options.using } ) );
+
+ if ( options.using ) {
+ // adds feedback as second argument to using callback, if present
+ using = function( props ) {
+ var left = targetOffset.left - props.left,
@scottgonzalez Owner

top + targetHeight - elemHeight

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@scottgonzalez scottgonzalez commented on the diff
ui/jquery.ui.position.js
((24 lines not shown))
} else if ( $.isWindow( targetElem ) ) {
targetWidth = target.width();
targetHeight = target.height();
- basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
+ targetOffset = { top: target.scrollTop(), left: target.scrollLeft() };
} else if ( targetElem.preventDefault ) {
// force left top to allow flipping
options.at = "left top";
@scottgonzalez Owner

We should probably use targetElem instead of options.of here. Unrelated to the changes, but you're already doing a lot of cleanup.

@jzaefferer Owner

If I'm mapping this comment correctly, you're referring to the option.of.pageX/Y calls, which we can't replace, as they access event properties.

@scottgonzalez Owner

Yeah, that's mapped properly. targetElem is the event object; target is a jQuery object, targetElem is the first item of target. $( obj )[0] === obj; there's even proof in the conditional. I probably wrote the original code, I'm not sure why I would've switched between the conditional and the assignments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@scottgonzalez scottgonzalez commented on the diff
ui/jquery.ui.position.js
@@ -216,7 +228,50 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
- elem.offset( $.extend( position, { using: options.using } ) );
+
+ if ( options.using ) {
@scottgonzalez Owner

It looks like these calculations are supposed to use offsets, but props will contain position.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jzaefferer
Owner

How about a milestone release for position based on this branch? We still haven't gotten any real feedback on the actual feedback API.

@jzaefferer jzaefferer merged commit 252352e into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 10, 2012
  1. @jzaefferer

    Position: First draft for a new notification API, via using callback,…

    jzaefferer authored
    … telling you were the of-element is, not just when something flipped. New test page demonstrates usage
  2. @jzaefferer

    Position: Use a separate object and argument for the feedback informa…

    jzaefferer authored
    …tion, keeping position props as they were
  3. @jzaefferer
  4. @jzaefferer

    Position: Mini refactoring, avoids proxying using callback multiple t…

    jzaefferer authored
    …imes if more then one element is positioned
Commits on Apr 11, 2012
  1. @jzaefferer

    Position: Extend feedback test page to include two mouse-positioned e…

    jzaefferer authored
    …lements, highlights the 0px center/middle limitation
    
    Also rename the demo file to match the variables names, 'feedback', instead of 'notification'
  2. @jzaefferer

    Position: Improve feedback API by giving the center/middle position m…

    jzaefferer authored
    …ore weight. Also removed themeswitcher from test pages, now load faster
Commits on Apr 12, 2012
  1. @jzaefferer
  2. @jzaefferer
  3. @jzaefferer
  4. @jzaefferer
  5. @jzaefferer
  6. @jzaefferer
  7. @jzaefferer

    Position: add target property to feedback argument, to allow addition…

    jzaefferer authored
    …al calculation in user's using callback
  8. @jzaefferer

    Position: Inline center variable. Not necessary with gzip (or a good …

    jzaefferer authored
    …minifier) in place, wasn't consistent with other inline strings
  9. @jzaefferer

    Position: Refactor within-data to avoid calculating that more then ne…

    jzaefferer authored
    …eded. Not quite done, but good enough for now
Commits on Apr 13, 2012
  1. @jzaefferer
  2. @jzaefferer

    Position: Extract getWithinInfo method, use that for tests that call …

    jzaefferer authored
    …getScrollInfo directly, pass within info to that, gets rid of a few more DOM accesses
  3. @jzaefferer
  4. @jzaefferer

    Position: Expose target and element dimensions to allow further custo…

    jzaefferer authored
    …mization, like calculating the angle between the two elements
Commits on Apr 17, 2012
  1. @jzaefferer
  2. @jzaefferer

    Tooltip: Update custom-style demo, remove the ugly padding that was h…

    jzaefferer authored
    …iding layout issues, use new position feedback API instead of position callout in more then just one place
  3. @jzaefferer
Commits on Apr 18, 2012
  1. @jzaefferer
Commits on Apr 20, 2012
  1. @jzaefferer
  2. @jzaefferer

    Position: offset() always returns null for window, so provide default…

    jzaefferer authored
    … 0/0 coordinates to avoid checks further down
  3. @jzaefferer
  4. @scottgonzalez
  5. @scottgonzalez
  6. @scottgonzalez
  7. @scottgonzalez
  8. @scottgonzalez
Commits on Apr 23, 2012
  1. @scottgonzalez

    Merge branch 'master' into position-notification

    scottgonzalez authored
    Conflicts:
    	tests/unit/position/position_core_within.js
Commits on Apr 24, 2012
  1. @jzaefferer
  2. @jzaefferer

    Position: Fix scrollbar calculcation to correctly take overflow:scrol…

    jzaefferer authored
    …l into account, along with unit tests
This page is out of date. Refresh to see the latest.
View
101 demos/tooltip/custom-style.html
@@ -13,76 +13,67 @@
<script>
$(function() {
$( ".demo" ).tooltip({
- open: function(event, ui) {
- $("<div>").addClass("arrow").appendTo(ui.tooltip);
- },
position: {
- my: "center bottom",
+ my: "center bottom-20",
at: "center top",
- offset: "0 -20px"
+ using: function( position, feedback ) {
+ $( this ).css( position );
+ $( "<div>" )
+ .addClass( "arrow" )
+ .addClass( feedback.vertical )
+ .addClass( feedback.horizontal )
+ .appendTo( this );
+ }
}
});
});
</script>
<style>
- label {
- display: inline-block;
- width: 5em;
- }
- .demo {
- margin: 10em 0 0 10em;
- }
.ui-tooltip, .arrow:after {
- background: black;
- border: 2px solid white;
+ background: black;
+ border: 2px solid white;
}
.ui-tooltip {
- display: inline-block;
- position: relative;
- padding: 10px 20px;
- color: white;
- border-radius: 20px;
- text-align: center;
- font: bold 14px "Helvetica Neue", Sans-Serif;
- font-stretch: condensed;
- text-decoration: none;
- text-transform: uppercase;
- box-shadow: 0 0 7px black;
+ padding: 10px 20px;
+ color: white;
+ border-radius: 20px;
+ font: bold 14px "Helvetica Neue", Sans-Serif;
+ text-transform: uppercase;
+ box-shadow: 0 0 7px black;
}
.arrow {
- width: 70px;
- height: 16px;
- overflow: hidden;
- position: absolute;
- left: 50%;
- margin-left: -35px;
- bottom: -16px;
+ width: 70px;
+ height: 16px;
+ overflow: hidden;
+ position: absolute;
+ left: 50%;
+ margin-left: -35px;
+ bottom: -16px;
}
- .arrow:after {
- content: "";
- position: absolute;
- left: 20px;
- top: -20px;
- width: 25px;
- height: 25px;
- -webkit-box-shadow: 6px 5px 9px -9px black,
- 5px 6px 9px -9px black;
- -moz-box-shadow: 6px 5px 9px -9px black,
- 5px 6px 9px -9px black;
- box-shadow: 6px 5px 9px -9px black,
- 5px 6px 9px -9px black;
- -webkit-transform: rotate(45deg);
- -moz-transform: rotate(45deg);
- -ms-transform: rotate(45deg);
- -o-transform: rotate(45deg);
+ .arrow.top {
+ top: -16px;
+ bottom: auto;
}
- div[data-tooltip].active {
- opacity: 1;
- margin-top: 6px;
+ .arrow.left {
+ left: 20%;
+ }
+ .arrow:after {
+ content: "";
+ position: absolute;
+ left: 20px;
+ top: -20px;
+ width: 25px;
+ height: 25px;
+ box-shadow: 6px 5px 9px -9px black;
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ tranform: rotate(45deg);
}
- div[data-tooltip].out {
- opacity: 0;
- margin-top: -20px;
+ .arrow.top:after {
+ bottom: -20px;
+ top: auto;
}
</style>
</head>
View
2  tests/unit/position/position.html
@@ -43,7 +43,7 @@ <h2 id="qunit-userAgent"></h2>
<div id="parent" style="position: absolute; width: 6px; height: 6px; top: 4px; left: 4px; line-height: 6px;"></div>
<div id="within" style="position: absolute; width: 12px; height: 12px; top: 2px; left: 0px;"></div>
- <div style="position: absolute; top: 0px; left: 0px">
+ <div id="scrollx" style="position: absolute; top: 0px; left: 0px">
<div id="elx" style="position: absolute; width: 10px; height: 10px; line-height: 10px;"></div>
<div id="parentx" style="position: absolute; width: 20px; height: 20px; top: 40px; left: 40px;"></div>
</div>
View
91 tests/unit/position/position_core.js
@@ -255,11 +255,30 @@ test( "offsets", function() {
});
test( "using", function() {
- expect( 6 );
+ expect( 10 );
var count = 0,
elems = $( "#el1, #el2" ),
- expectedPosition = { top: 40, left: 40 },
+ of = $( "#parentx" ),
+ expectedPosition = { top: 60, left: 60 },
+ expectedFeedback = {
+ target: {
+ element: of,
+ width: 20,
+ height: 20,
+ left: 40,
+ top: 40
+ },
+ element: {
+ width: 6,
+ height: 6,
+ left: 60,
+ top: 60
+ },
+ horizontal: "left",
+ vertical: "top",
+ important: "vertical"
+ },
originalPosition = elems.position({
my: "right bottom",
at: "rigt bottom",
@@ -269,11 +288,14 @@ test( "using", function() {
elems.position({
my: "left top",
- at: "left top",
+ at: "center+10 bottom",
of: "#parentx",
- using: function( position ) {
+ using: function( position, feedback ) {
deepEqual( this, elems[ count ], "correct context for call #" + count );
deepEqual( position, expectedPosition, "correct position for call #" + count );
+ deepEqual( feedback.element.element[ 0 ], elems[ count ] );
+ delete feedback.element.element;
+ deepEqual( feedback, expectedFeedback );
count++;
}
});
@@ -569,6 +591,67 @@ test( "within", function() {
}, "flipfit - left top" );
});
+test( "with scrollbars", function() {
+ expect( 4 );
+
+ $( "#scrollx" ).css({
+ width: 100,
+ height: 100,
+ left: 0,
+ top: 0
+ });
+
+ collisionTest({
+ of: "#scrollx",
+ collision: "fit",
+ within: "#scrollx"
+ }, {
+ top: 90,
+ left: 90
+ }, "visible" );
+
+ $( "#scrollx" ).css({
+ overflow: "scroll"
+ });
+
+ var scrollbarInfo = $.position.getScrollInfo( $.position.getWithinInfo( $( "#scrollx" ) ) );
+
+ collisionTest({
+ of: "#scrollx",
+ collision: "fit",
+ within: "#scrollx"
+ }, {
+ top: 90 - scrollbarInfo.height,
+ left: 90 - scrollbarInfo.width
+ }, "scroll" );
+
+ $( "#scrollx" ).css({
+ overflow: "auto"
+ });
+
+ collisionTest({
+ of: "#scrollx",
+ collision: "fit",
+ within: "#scrollx"
+ }, {
+ top: 90,
+ left: 90
+ }, "auto, no scroll" );
+
+ $( "#scrollx" ).css({
+ overflow: "auto"
+ }).append( $("<div>").height(300).width(300) );
+
+ collisionTest({
+ of: "#scrollx",
+ collision: "fit",
+ within: "#scrollx"
+ }, {
+ top: 90 - scrollbarInfo.height,
+ left: 90 - scrollbarInfo.width
+ }, "auto, with scroll" );
+});
+
test( "fractions", function() {
expect( 1 );
View
20 tests/visual/position/position.html
@@ -10,23 +10,31 @@
<script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.menu.js"></script>
- <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
<script type="text/javascript">
$(function() {
var inputs = $("input");
$("ul").insertAfter(inputs);
$(window).resize(function() {
inputs.each(function() {
- $(this).position({
+ var input = $(this).position({
my: this.id.replace(/-/, " "),
at: this.id.replace(/-/, " "),
of: "#container",
collision: "none"
});
- $(this).next().menu().position({
- my: "left top+20",
+ var menu = $(this).next().menu()
+ menu.position({
+ my: "left+30 top+20",
at: "left bottom",
of: this,
+ using: function( position, feedback ) {
+ input.val(feedback.horizontal + " " + feedback.vertical)
+ $(this).offset( position );
+ $(this)
+ .removeClass("left right top bottom center middle")
+ .addClass(feedback.horizontal)
+ .addClass(feedback.vertical);
+ }
});
});
}).resize();
@@ -44,11 +52,11 @@
top: -22px;
left: 5px;
}
- .ui-flipped-left:before {
+ .right:before {
left: auto;
right: 5px;
}
- .ui-flipped-top:before {
+ .bottom:before {
content: "";
top: auto;
bottom: -19px;
View
146 tests/visual/position/position_feedback.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Position Visual Test: Default</title>
+ <link rel="stylesheet" href="../visual.css">
+ <link rel="stylesheet" href="../../../themes/base/jquery.ui.all.css">
+ <script src="../../../jquery-1.7.2.js"></script>
+ <script src="../../../ui/jquery.ui.core.js"></script>
+ <script src="../../../ui/jquery.ui.widget.js"></script>
+ <script src="../../../ui/jquery.ui.position.js"></script>
+ <script src="../../../ui/jquery.ui.menu.js"></script>
+ <script>
+ $(function() {
+ function using( position, feedback ) {
+ $( this )
+ .css( position )
+ .text( feedback.horizontal + " " + feedback.vertical + " " + feedback.important )
+ .removeClass( "left right top bottom center middle vertical horizontal" )
+ .addClass( feedback.horizontal )
+ .addClass( feedback.vertical )
+ .addClass( feedback.important );
+ }
+
+ var element = $( ".element" ),
+ target = $( "#target" ).position({
+ my: "center",
+ at: "center",
+ of: window
+ }),
+ targetOffset = target.offset(),
+ oppositeElement = element.clone().width( 50 ).appendTo( "body" ),
+ leftElement = element.clone().width( 50 ).height( 150 ).appendTo( "body" ),
+ rightElement = element.clone().height( 150 ).width( 150 ).appendTo( "body" );
+
+ $.each([
+ "center top-100",
+ "right+25 top-50",
+ "right+75 top",
+ "right+75 center",
+ "right+75 bottom",
+ "right+25 bottom+50",
+ "center bottom+100",
+ "left-25 bottom+50",
+ "left-75 bottom",
+ "left-75 center",
+ "left-75 top",
+ "left-25 top-50"
+ ], function( index, direction ) {
+ element.clone().insertAfter( target ).position({
+ my: "center",
+ at: direction,
+ of: target,
+ using: using
+ });
+ });
+
+ element.width( 150 );
+ $( document ).on( "mousemove", function( event ) {
+ var base = {
+ my: "left top",
+ at: "left top",
+ of: target,
+ using: using
+ };
+ element.position( $.extend({
+ offset: (event.pageX - targetOffset.left) + " " + (event.pageY - targetOffset.top)
+ }, base ));
+ oppositeElement.position( $.extend({
+ offset: (-1 * (event.pageX - targetOffset.left)) + " " + (-1 * (event.pageY - targetOffset.top))
+ }, base ));
+ leftElement.position( $.extend({
+ offset: (-0.9 * (event.pageX - targetOffset.left)) + " " + (0.9 * (event.pageY - targetOffset.top))
+ }, base ));
+ rightElement.position( $.extend({
+ offset: (0.9 * (event.pageX - targetOffset.left)) + " " + (-0.9 * (event.pageY - targetOffset.top))
+ }, base) );
+ });
+ });
+ </script>
+ <style>
+ #target, .element {
+ position: absolute;
+ border: 1px solid black;
+ border-radius: 5px;
+ width: 75px;
+ height: 25px;
+ padding: 5px;
+ }
+ #target {
+ height: 75px;
+ }
+ .element:before {
+ font-size: 12pt;
+ content: "";
+ position: absolute;
+ top: -19px;
+ left: 5px;
+ }
+ .right:before {
+ left: auto;
+ right: 5px;
+ }
+ .bottom:before {
+ content: "";
+ top: auto;
+ bottom: -19px;
+ }
+ .center:before {
+ left: 50%;
+ right: auto;
+ }
+ .middle:before {
+ top: 50%;
+ bottom: auto;
+ }
+ .horizontal:before {
+ height: 10px;
+ top: 50%;
+ margin-top: -8px;
+ bottom: auto;
+ left: -18px;
+ right: auto;
+ content: "";
+ }
+ .right.horizontal:before {
+ left: auto;
+ right: -18px;
+ content: "";
+ }
+ .bottom.horizontal:before {
+ top: auto;
+ bottom: 5px;
+ }
+ .top.horizontal:before {
+ top: 5px;
+ }
+ </style>
+</head>
+<body>
+
+<div id="target">all around me</div>
+<div class="element"></div>
+
+</body>
+</html>
View
109 tests/visual/position/position_feedback_rotate.html
@@ -0,0 +1,109 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Position Visual Test: Default</title>
+ <link rel="stylesheet" href="../visual.css">
+ <link rel="stylesheet" href="../../../themes/base/jquery.ui.all.css">
+ <script src="../../../jquery-1.7.2.js"></script>
+ <script src="../../../ui/jquery.ui.core.js"></script>
+ <script src="../../../ui/jquery.ui.widget.js"></script>
+ <script src="../../../ui/jquery.ui.position.js"></script>
+ <script src="../../../ui/jquery.ui.menu.js"></script>
+ <script>
+ $(function() {
+ function using( position, feedback ) {
+ var angleRad = Math.atan2(
+ feedback.target.top + feedback.target.height / 2 - feedback.element.top - feedback.element.height / 2,
+ feedback.target.left + feedback.target.width / 2 - feedback.element.left - feedback.element.width / 2
+ ) * 180 / Math.PI,
+ angle = Math.round( angleRad * 100) / 100;
+ $( this )
+ .css( position )
+ .text( feedback.horizontal + " " + feedback.vertical + " " + feedback.important + " " + angle + "°" )
+ .css({
+ "-webkit-transform": "rotate(" + angle + "deg)",
+ "-moz-transform": "rotate(" + angle + "deg)",
+ "-ms-transform": "rotate(" + angle + "deg)",
+ "-o-transform": "rotate(" + angle + "deg)",
+ "transform": "rotate(" + angle + "deg)"
+ });
+ }
+
+ var element = $( ".element" ),
+ target = $( "#target" ).position({
+ my: "center",
+ at: "center",
+ of: window
+ }),
+ targetOffset = target.offset();
+ oppositeElement = element.clone().width( 50 ).appendTo( "body" ),
+ leftElement = element.clone().width( 50 ).height( 150 ).appendTo( "body" ),
+ rightElement = element.clone().height( 150 ).width( 150 ).appendTo( "body" );
+
+ $.each([
+ "center top-100",
+ "right+25 top-50",
+ "right+75 top",
+ "right+75 center",
+ "right+75 bottom",
+ "right+25 bottom+50",
+ "center bottom+100",
+ "left-25 bottom+50",
+ "left-75 bottom",
+ "left-75 center",
+ "left-75 top",
+ "left-25 top-50"
+ ], function( index, direction ) {
+ element.clone().insertAfter( target ).position({
+ my: "center",
+ at: direction,
+ of: target,
+ using: using
+ });
+ });
+
+ element.width( 150 );
+ $( document ).on( "mousemove", function( event ) {
+ var base = {
+ my: "left top",
+ at: "left top",
+ of: target,
+ using: using
+ };
+ element.position( $.extend({
+ offset: (event.pageX - targetOffset.left) + " " + (event.pageY - targetOffset.top)
+ }, base ));
+ oppositeElement.position( $.extend({
+ offset: (-1 * (event.pageX - targetOffset.left)) + " " + (-1 * (event.pageY - targetOffset.top))
+ }, base ));
+ leftElement.position( $.extend({
+ offset: (-0.9 * (event.pageX - targetOffset.left)) + " " + (0.9 * (event.pageY - targetOffset.top))
+ }, base ));
+ rightElement.position( $.extend({
+ offset: (0.9 * (event.pageX - targetOffset.left)) + " " + (-0.9 * (event.pageY - targetOffset.top))
+ }, base ));
+ });
+ });
+ </script>
+ <style>
+ #target, .element {
+ position: absolute;
+ border: 1px solid black;
+ border-radius: 5px;
+ width: 75px;
+ height: 25px;
+ padding: 5px;
+ }
+ #target {
+ height: 75px;
+ }
+ </style>
+</head>
+<body>
+
+<div id="target">all around me</div>
+<div class="element"></div>
+
+</body>
+</html>
View
1  tests/visual/position/position_fit.html
@@ -10,7 +10,6 @@
<script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.menu.js"></script>
- <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
<script type="text/javascript">
$(function() {
var inputs = $("input");
View
1  tests/visual/position/position_flip.html
@@ -10,7 +10,6 @@
<script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.menu.js"></script>
- <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
<script type="text/javascript">
$(function() {
var inputs = $("input");
View
1  tests/visual/position/position_flipfit.html
@@ -10,7 +10,6 @@
<script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.menu.js"></script>
- <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
<script type="text/javascript">
$(function() {
var inputs = $("input");
View
1  tests/visual/position/position_margin.html
@@ -9,7 +9,6 @@
<script type="text/javascript" src="../../../ui/jquery.ui.core.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script>
- <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
<script type="text/javascript">
$(function() {
$( "#elem" ).position({
View
2  tests/visual/position/position_within.html
@@ -97,7 +97,7 @@
collision: $( "#collision_horizontal" ).val() + " " + $( "#collision_vertical" ).val()
});
}
- $( ".demo" ).append("<div style='width:5000px;height:5000px;' />").css("overflow","auto");
+ $( ".demo" ).css("overflow","scroll");
$( ".positionable" ).css( "opacity", 0.5 );
View
2  themes/base/jquery.ui.tooltip.css
@@ -4,8 +4,6 @@
* 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/Tooltip#theming
*/
.ui-tooltip {
padding:8px;
View
231 ui/jquery.ui.position.js
@@ -11,16 +11,32 @@
$.ui = $.ui || {};
-var rhorizontal = /left|center|right/,
+var cachedScrollbarWidth,
+ max = Math.max,
+ abs = Math.abs,
+ round = Math.round,
+ rhorizontal = /left|center|right/,
rvertical = /top|center|bottom/,
roffset = /[\+\-]\d+%?/,
rposition = /^\w+/,
rpercent = /%$/,
- center = "center",
_position = $.fn.position;
+function getOffsets( offsets, width, height ) {
+ return [
+ parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
+ parseInt( offsets[ 1 ], 10 ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
+ ];
+}
+function parseCss( element, property ) {
+ return parseInt( $.css( element, property ), 10 ) || 0;
+}
+
$.position = {
scrollbarWidth: function() {
+ if ( cachedScrollbarWidth !== undefined ) {
+ return cachedScrollbarWidth;
+ }
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];
@@ -37,18 +53,31 @@ $.position = {
div.remove();
- return w1 - w2;
+ return (cachedScrollbarWidth = 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;
-
+ getScrollInfo: function( within ) {
+ var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
+ overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
+ hasOverflowX = overflowX === "scroll" ||
+ ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
+ hasOverflowY = overflowY === "scroll" ||
+ ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
return {
- height: within.height() < within[0].scrollHeight ? scrollbarHeight : 0,
- width: within.width() < within[0].scrollWidth ? scrollbarWidth : 0
+ width: hasOverflowX ? $.position.scrollbarWidth() : 0,
+ height: hasOverflowY ? $.position.scrollbarWidth() : 0
+ };
+ },
+ getWithinInfo: function( element ) {
+ var withinElement = $( element || window ),
+ isWindow = $.isWindow( withinElement[0] );
+ return {
+ element: withinElement,
+ isWindow: isWindow,
+ offset: withinElement.offset() || { left: 0, top: 0 },
+ scrollLeft: withinElement.scrollLeft(),
+ scrollTop: withinElement.scrollTop(),
+ width: isWindow ? withinElement.width() : withinElement.outerWidth(),
+ height: isWindow ? withinElement.height() : withinElement.outerHeight()
};
}
};
@@ -61,34 +90,34 @@ $.fn.position = function( options ) {
// make a copy, we don't want to modify arguments
options = $.extend( {}, options );
- var target = $( options.of ),
- within = $( options.within || window ),
+ var atOffset, targetWidth, targetHeight, targetOffset, basePosition,
+ target = $( options.of ),
+ within = $.position.getWithinInfo( options.within ),
+ scrollInfo = $.position.getScrollInfo( within ),
targetElem = target[0],
collision = ( options.collision || "flip" ).split( " " ),
- offsets = {},
- atOffset,
- targetWidth,
- targetHeight,
- basePosition;
+ offsets = {};
if ( targetElem.nodeType === 9 ) {
targetWidth = target.width();
targetHeight = target.height();
- basePosition = { top: 0, left: 0 };
+ targetOffset = { top: 0, left: 0 };
} else if ( $.isWindow( targetElem ) ) {
targetWidth = target.width();
targetHeight = target.height();
- basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
+ targetOffset = { top: target.scrollTop(), left: target.scrollLeft() };
} else if ( targetElem.preventDefault ) {
// force left top to allow flipping
options.at = "left top";
@scottgonzalez Owner

We should probably use targetElem instead of options.of here. Unrelated to the changes, but you're already doing a lot of cleanup.

@jzaefferer Owner

If I'm mapping this comment correctly, you're referring to the option.of.pageX/Y calls, which we can't replace, as they access event properties.

@scottgonzalez Owner

Yeah, that's mapped properly. targetElem is the event object; target is a jQuery object, targetElem is the first item of target. $( obj )[0] === obj; there's even proof in the conditional. I probably wrote the original code, I'm not sure why I would've switched between the conditional and the assignments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
targetWidth = targetHeight = 0;
- basePosition = { top: options.of.pageY, left: options.of.pageX };
+ targetOffset = { top: targetElem.pageY, left: targetElem.pageX };
} else {
targetWidth = target.outerWidth();
targetHeight = target.outerHeight();
- basePosition = target.offset();
+ targetOffset = target.offset();
}
+ // clone to reuse original targetOffset later
+ basePosition = $.extend( {}, targetOffset );
// force my and at to have valid horizontal and vertical positions
// if a value is missing or invalid, it will be converted to center
@@ -99,13 +128,13 @@ $.fn.position = function( options ) {
if ( pos.length === 1) {
pos = rhorizontal.test( pos[ 0 ] ) ?
- pos.concat( [ center ] ) :
+ pos.concat( [ "center" ] ) :
rvertical.test( pos[ 0 ] ) ?
- [ center ].concat( pos ) :
- [ center, center ];
+ [ "center" ].concat( pos ) :
+ [ "center", "center" ];
}
- pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : center;
- pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : 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 ] );
@@ -129,54 +158,41 @@ $.fn.position = function( options ) {
if ( options.at[ 0 ] === "right" ) {
basePosition.left += targetWidth;
- } else if ( options.at[ 0 ] === center ) {
+ } else if ( options.at[ 0 ] === "center" ) {
basePosition.left += targetWidth / 2;
}
if ( options.at[ 1 ] === "bottom" ) {
basePosition.top += targetHeight;
- } else if ( options.at[ 1 ] === center ) {
+ } 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 )
- ];
+ atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
basePosition.left += atOffset[ 0 ];
basePosition.top += atOffset[ 1 ];
return this.each(function() {
- var elem = $( this ),
+ var collisionPosition, using,
+ 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,
+ marginLeft = parseCss( this, "marginLeft" ),
+ marginTop = parseCss( this, "marginTop" ),
+ collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
+ collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + 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;
+ myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
if ( options.my[ 0 ] === "right" ) {
position.left -= elemWidth;
- } else if ( options.my[ 0 ] === center ) {
+ } else if ( options.my[ 0 ] === "center" ) {
position.left -= elemWidth / 2;
}
if ( options.my[ 1 ] === "bottom" ) {
position.top -= elemHeight;
- } else if ( options.my[ 1 ] === center ) {
+ } else if ( options.my[ 1 ] === "center" ) {
position.top -= elemHeight / 2;
}
@@ -185,8 +201,8 @@ $.fn.position = function( options ) {
// 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 );
+ position.left = round( position.left );
+ position.top = round( position.top );
}
collisionPosition = {
@@ -216,7 +232,48 @@ $.fn.position = function( options ) {
if ( $.fn.bgiframe ) {
elem.bgiframe();
}
- elem.offset( $.extend( position, { using: options.using } ) );
+
+ if ( options.using ) {
@scottgonzalez Owner

It looks like these calculations are supposed to use offsets, but props will contain position.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ // adds feedback as second argument to using callback, if present
@scottgonzalez Owner

left + targetWidth - elemWidth

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ using = function( props ) {
+ var left = targetOffset.left - position.left,
+ right = left + targetWidth - elemWidth,
+ top = targetOffset.top - position.top,
+ bottom = top + targetHeight - elemHeight,
+ feedback = {
+ target: {
+ element: target,
+ left: targetOffset.left,
+ top: targetOffset.top,
+ width: targetWidth,
+ height: targetHeight
+ },
+ element: {
+ element: elem,
+ left: position.left,
+ top: position.top,
+ width: elemWidth,
+ height: elemHeight
+ },
+ horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
+ vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
@scottgonzalez Owner

If you're going to cache these, you should do it at the very top of the file so it only happens once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ };
+ if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
+ feedback.horizontal = "center";
+ }
+ if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
+ feedback.vertical = "middle";
+ }
+ if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
+ feedback.important = "horizontal";
+ } else {
+ feedback.important = "vertical";
+ }
+ options.using.call( this, props, feedback );
+ };
+ }
+
+ elem.offset( $.extend( position, { using: using } ) );
});
};
@@ -224,10 +281,8 @@ $.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(),
+ withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
+ outerWidth = within.width,
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
overLeft = withinOffset - collisionPosLeft,
overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
@@ -259,15 +314,13 @@ $.ui.position = {
position.left -= overRight;
// adjust based on position and margin
} else {
- position.left = Math.max( position.left - collisionPosLeft, position.left );
+ position.left = 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(),
+ withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
+ outerHeight = data.within.height,
collisionPosTop = position.top - data.collisionPosition.marginTop,
overTop = withinOffset - collisionPosTop,
overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
@@ -299,25 +352,20 @@ $.ui.position = {
position.top -= overBottom;
// adjust based on position and margin
} else {
- position.top = Math.max( position.top - collisionPosTop, position.top );
+ position.top = max( position.top - collisionPosTop, position.top );
}
}
},
flip: {
left: function( position, data ) {
- if ( data.at[ 0 ] === center ) {
+ 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,
+ withinOffset = within.offset.left + within.scrollLeft,
+ outerWidth = within.width,
+ offsetLeft = within.isWindow ? 0 : within.offset.left,
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
overLeft = collisionPosLeft - offsetLeft,
overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
@@ -336,37 +384,26 @@ $.ui.position = {
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" );
-
+ if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
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" );
-
+ if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
position.left += myOffset + atOffset + offset;
}
}
},
top: function( position, data ) {
- if ( data.at[ 1 ] === center ) {
+ 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,
+ withinOffset = within.offset.top + within.scrollTop,
+ outerHeight = within.height,
+ offsetTop = within.isWindow ? 0 : within.offset.top,
collisionPosTop = position.top - data.collisionPosition.marginTop,
overTop = collisionPosTop - offsetTop,
overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
@@ -384,19 +421,13 @@ $.ui.position = {
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" );
-
+ if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
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" );
-
+ if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
position.top += myOffset + atOffset + offset;
}
}
View
2  ui/jquery.ui.tooltip.js
@@ -5,8 +5,6 @@
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
- * http://docs.jquery.com/UI/Tooltip
- *
* Depends:
* jquery.ui.core.js
* jquery.ui.widget.js
Something went wrong with that request. Please try again.