1 {getOuterSize, getBounds, getSize, extend, updateClasses, defer} = @Tether.Utils
10 BOUNDS_FORMAT = ['left', 'top', 'right', 'bottom']
12 getBoundingRect = (tether, to) ->
13 if to is 'scrollParent'
14 to = tether.scrollParent
15 else if to is 'window'
16 to = [pageXOffset, pageYOffset, innerWidth + pageXOffset, innerHeight + pageYOffset]
19 to = to.documentElement
22 pos = size = getBounds to
23 style = getComputedStyle to
25 to = [pos.left, pos.top, size.width + pos.left, size.height + pos.top]
27 for side, i in BOUNDS_FORMAT
28 side = side[0].toUpperCase() + side.substr(1)
29 if side in ['Top', 'Left']
30 to[i] += parseFloat style["border#{ side }Width"]
32 to[i] -= parseFloat style["border#{ side }Width"]
37 position: ({top, left, targetAttachment}) ->
38 return true unless @options.constraints
40 removeClass = (prefix) =>
42 for side in BOUNDS_FORMAT
43 @removeClass "#{ prefix }-#{ side }"
45 {height, width} = @cache 'element-bounds', => getBounds @element
47 if width is 0 and height is 0 and @lastSize?
48 # Handle the item getting hidden as a result of our positioning without glitching
49 # the classes in and out
50 {width, height} = @lastSize
52 targetSize = @cache 'target-bounds', => @getTargetBounds()
53 targetHeight = targetSize.height
54 targetWidth = targetSize.width
59 allClasses = [@getClass('pinned'), @getClass('out-of-bounds')]
60 for constraint in @options.constraints
61 allClasses.push(constraint.outOfBoundsClass) if constraint.outOfBoundsClass
62 allClasses.push(constraint.pinnedClass) if constraint.pinnedClass
65 for side in ['left', 'top', 'right', 'bottom']
66 allClasses.push "#{ cls }-#{ side }"
70 tAttachment = extend {}, targetAttachment
71 eAttachment = extend {}, @attachment
73 for constraint in @options.constraints
74 {to, attachment, pin} = constraint
79 [changeAttachY, changeAttachX] = attachment.split(' ')
81 changeAttachX = changeAttachY = attachment
83 bounds = getBoundingRect @, to
85 if changeAttachY in ['target', 'both']
86 if (top < bounds[1] and tAttachment.top is 'top')
88 tAttachment.top = 'bottom'
90 if (top + height > bounds[3] and tAttachment.top is 'bottom')
92 tAttachment.top = 'top'
94 if changeAttachY is 'together'
95 if top < bounds[1] and tAttachment.top is 'top'
96 if eAttachment.top is 'bottom'
98 tAttachment.top = 'bottom'
101 eAttachment.top = 'top'
102 else if eAttachment.top is 'top'
104 tAttachment.top = 'bottom'
107 eAttachment.top = 'bottom'
109 if top + height > bounds[3] and tAttachment.top is 'bottom'
110 if eAttachment.top is 'top'
112 tAttachment.top = 'top'
115 eAttachment.top = 'bottom'
116 else if eAttachment.top is 'bottom'
118 tAttachment.top = 'top'
121 eAttachment.top = 'top'
123 if tAttachment.top is 'middle'
124 if top + height > bounds[3] and eAttachment.top is 'top'
126 eAttachment.top = 'bottom'
128 else if top < bounds[1] and eAttachment.top is 'bottom'
130 eAttachment.top = 'top'
132 if changeAttachX in ['target', 'both']
133 if (left < bounds[0] and tAttachment.left is 'left')
135 tAttachment.left = 'right'
137 if (left + width > bounds[2] and tAttachment.left is 'right')
139 tAttachment.left = 'left'
141 if changeAttachX is 'together'
142 if left < bounds[0] and tAttachment.left is 'left'
143 if eAttachment.left is 'right'
145 tAttachment.left = 'right'
148 eAttachment.left = 'left'
150 else if eAttachment.left is 'left'
152 tAttachment.left = 'right'
155 eAttachment.left = 'right'
157 else if left + width > bounds[2] and tAttachment.left is 'right'
158 if eAttachment.left is 'left'
160 tAttachment.left = 'left'
163 eAttachment.left = 'right'
165 else if eAttachment.left is 'right'
167 tAttachment.left = 'left'
170 eAttachment.left = 'left'
172 else if tAttachment.left is 'center'
173 if left + width > bounds[2] and eAttachment.left is 'left'
175 eAttachment.left = 'right'
177 else if left < bounds[0] and eAttachment.left is 'right'
179 eAttachment.left = 'left'
181 if changeAttachY in ['element', 'both']
182 if (top < bounds[1] and eAttachment.top is 'bottom')
184 eAttachment.top = 'top'
186 if (top + height > bounds[3] and eAttachment.top is 'top')
188 eAttachment.top = 'bottom'
190 if changeAttachX in ['element', 'both']
191 if (left < bounds[0] and eAttachment.left is 'right')
193 eAttachment.left = 'left'
195 if (left + width > bounds[2] and eAttachment.left is 'left')
197 eAttachment.left = 'right'
199 if typeof pin is 'string'
200 pin = (p.trim() for p in pin.split ',')
202 pin = ['top', 'left', 'right', 'bottom']
215 if top + height > bounds[3]
217 top = bounds[3] - height
229 if left + width > bounds[2]
231 left = bounds[2] - width
237 pinnedClass = @options.pinnedClass ? @getClass('pinned')
238 addClasses.push pinnedClass
240 addClasses.push "#{ pinnedClass }-#{ side }"
243 oobClass = @options.outOfBoundsClass ? @getClass('out-of-bounds')
244 addClasses.push oobClass
246 addClasses.push "#{ oobClass }-#{ side }"
248 if 'left' in pinned or 'right' in pinned
249 eAttachment.left = tAttachment.left = false
250 if 'top' in pinned or 'bottom' in pinned
251 eAttachment.top = tAttachment.top = false
253 if tAttachment.top isnt targetAttachment.top or tAttachment.left isnt targetAttachment.left or eAttachment.top isnt @attachment.top or eAttachment.left isnt @attachment.left
254 @updateAttachClasses eAttachment, tAttachment
257 updateClasses @target, addClasses, allClasses
258 updateClasses @element, addClasses, allClasses