4 * Utility class to align elements relatively to another.
6 * @author Alexander Ebert
7 * @copyright 2001-2015 WoltLab GmbH
8 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
9 * @module WoltLab/WCF/UI/Alignment
11 define(['Core', 'DOM/Util'], function(Core
, DOMUtil
) {
15 function UIAlignment() {};
16 UIAlignment
.prototype = {
18 * Sets the alignment for target element relatively to the reference element.
20 * @param {Element} el target element
21 * @param {Element} ref reference element
22 * @param {object<string, *>} options list of options to alter the behavior
24 set: function(el
, ref
, options
) {
25 options
= Core
.extend({
26 // offset to reference element
29 // align the pointer element, expects .pointer as a direct child of given element
32 // use static pointer positions, expects two items: class to move it to the bottom and the second to move it to the right
33 pointerClassNames
: [],
35 // alternate element used to calculate dimensions
36 refDimensionsElement
: null,
38 // preferred alignment, possible values: left/right and top/bottom
42 // allow flipping over axis, possible values: both, horizontal, vertical and none
46 if (!Array
.isArray(options
.pointerClassNames
) || options
.pointerClassNames
.length
!== 2) options
.pointerClassNames
= [];
47 if (options
.horizontal
!== 'right') options
.horizontal
= 'left';
48 if (options
.vertical
!== 'bottom') options
.horizontal
= 'top';
49 if (['both', 'horizontal', 'vertical', 'none'].indexOf(options
.allowFlip
) === -1) options
.allowFlip
= 'both';
51 // place element in the upper left corner to prevent calculation issues due to possible scrollbars
52 DOMUtil
.setStyles(el
, {
59 var elDimensions
= DOMUtil
.outerDimensions(el
);
60 var refDimensions
= DOMUtil
.outerDimensions((options
.refDimensionsElement
instanceof Element
? options
.refDimensionsElement
: ref
));
61 var refOffsets
= DOMUtil
.offset(ref
);
62 var windowHeight
= window
.innerHeight
;
63 var windowWidth
= window
.innerWidth
;
65 // in rtl languages we simply swap the value for 'horizontal'
66 if (WCF
.Language
.get('wcf.global.pageDirection') === 'rtl') {
67 options
.horizontal
= (options
.horizontal
=== 'left') ? 'right' : 'left';
70 var horizontal
= this._tryAlignmentHorizontal(options
.horizontal
, elDimensions
, refDimensions
, refOffsets
, windowWidth
);
71 if (!horizontal
.result
&& (options
.allowFlip
=== 'both' || options
.allowFlip
=== 'horizontal')) {
72 var horizontalFlipped
= this._tryAlignmentHorizontal((options
.horizontal
=== 'left' ? 'right' : 'left'), elDimensions
, refDimensions
, refOffsets
, windowWidth
);
73 // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
74 if (horizontalFlipped
.result
) {
75 horizontal
= horizontalFlipped
;
79 var left
= horizontal
.left
;
80 var right
= horizontal
.right
;
82 var vertical
= this._tryAlignmentVertical(options
.vertical
, elDimensions
, refDimensions
, refOffsets
, windowHeight
, options
.verticalOffset
);
83 if (!vertical
.result
&& (options
.allowFlip
=== 'both' || options
.allowFlip
=== 'vertical')) {
84 var verticalFlipped
= this._tryAlignmentVertical((options
.vertical
=== 'top' ? 'bottom' : 'top'), elDimensions
, refDimensions
, refOffsets
, windowHeight
, options
.verticalOffset
);
85 // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
86 if (verticalFlipped
.result
) {
87 vertical
= verticalFlipped
;
91 var bottom
= vertical
.bottom
;
92 var top
= vertical
.top
;
94 // set pointer position
95 if (options
.pointer
) {
97 // TODO: implement pointer support, e.g. for interactive dropdowns
98 console
.debug("TODO");
100 else if (options
.pointerClassNames
.length
=== 2) {
101 var pointerRight
= 0;
102 var pointerBottom
= 1;
104 el
.classList
[(top
=== 'auto' ? 'add' : 'remove')](options
.pointerClassNames
[pointerBottom
]);
105 el
.classList
[(left
=== 'auto' ? 'add' : 'remove')](options
.pointerClassNames
[pointerRight
]);
108 DOMUtil
.setStyles(el
, {
109 bottom
: bottom
+ (bottom
!== 'auto' ? 'px' : ''),
110 left
: left
+ (left
!== 'auto' ? 'px' : ''),
111 right
: right
+ (right
!== 'auto' ? 'px' : ''),
112 top
: top
+ (top
!== 'auto' ? 'px' : '')
117 * Calculates left/right position and verifys if the element would be still within the page's boundaries.
119 * @param {string} align align to this side of the reference element
120 * @param {object<string, integer>} elDimensions element dimensions
121 * @param {object<string, integer>} refDimensions reference element dimensions
122 * @param {object<string, integer>} refOffsets position of reference element relative to the document
123 * @param {integer} windowWidth window width
124 * @returns {object<string, *>} calculation results
126 _tryAlignmentHorizontal: function(align
, elDimensions
, refDimensions
, refOffsets
, windowWidth
) {
131 if (align
=== 'left') {
132 left
= refOffsets
.left
;
133 if (left
+ elDimensions
.width
> windowWidth
) {
138 right
= refOffsets
.left
+ refDimensions
.width
;
139 if (right
- elDimensions
.width
< 0) {
152 * Calculates top/bottom position and verifys if the element would be still within the page's boundaries.
154 * @param {string} align align to this side of the reference element
155 * @param {object<string, integer>} elDimensions element dimensions
156 * @param {object<string, integer>} refDimensions reference element dimensions
157 * @param {object<string, integer>} refOffsets position of reference element relative to the document
158 * @param {integer} windowHeight window height
159 * @param {integer} verticalOffset desired gap between element and reference element
160 * @returns {object<string, *>} calculation results
162 _tryAlignmentVertical: function(align
, elDimensions
, refDimensions
, refOffsets
, windowHeight
, verticalOffset
) {
167 if (align
=== 'top') {
168 bottom
= refOffsets
.top
+ verticalOffset
;
169 if (bottom
- elDimensions
.height
< 0) {
174 top
= refOffsets
.top
+ refDimensions
.height
+ verticalOffset
;
175 if (top
+ elDimensions
.height
> windowHeight
) {
188 return new UIAlignment();