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