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