1 | /*! |
---|
2 | * jQuery UI Position v1.10.0 |
---|
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+%?/, |
---|
22 | rposition = /^\w+/, |
---|
23 | rpercent = /%$/, |
---|
24 | _position = $.fn.position; |
---|
25 | |
---|
26 | function getOffsets( offsets, width, height ) { |
---|
27 | return [ |
---|
28 | parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), |
---|
29 | parseInt( offsets[ 1 ], 10 ) * ( 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: hasOverflowX ? $.position.scrollbarWidth() : 0, |
---|
99 | height: hasOverflowY ? $.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 ) ); |
---|