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