]>
Commit | Line | Data |
---|---|---|
3c77f292 SH |
1 | /*** |
2 | umd: true | |
3 | ***/ | |
4 | ||
5 | (function () { | |
6 | // Prepare | |
7 | var $, ScrollTo | |
8 | $ = window.jQuery || require('jquery') | |
9 | ||
10 | // Fix scrolling animations on html/body on safari | |
11 | $.propHooks.scrollTop = $.propHooks.scrollLeft = { | |
12 | get: function (elem, prop) { | |
13 | var result = null | |
14 | if ( elem.tagName === 'HTML' || elem.tagName === 'BODY' ) { | |
15 | if ( prop === 'scrollLeft' ) { | |
16 | result = window.scrollX | |
17 | } | |
18 | else if ( prop === 'scrollTop' ) { | |
19 | result = window.scrollY | |
20 | } | |
21 | } | |
22 | if ( result == null ) { | |
23 | result = elem[prop] | |
24 | } | |
25 | return result | |
26 | } | |
27 | } | |
28 | $.Tween.propHooks.scrollTop = $.Tween.propHooks.scrollLeft = { | |
29 | get: function (tween) { | |
30 | return $.propHooks.scrollTop.get(tween.elem, tween.prop) | |
31 | }, | |
32 | set: function (tween) { | |
33 | // Our safari fix | |
34 | if ( tween.elem.tagName === 'HTML' || tween.elem.tagName === 'BODY' ) { | |
35 | // Defaults | |
36 | tween.options.bodyScrollLeft = (tween.options.bodyScrollLeft || window.scrollX) | |
37 | tween.options.bodyScrollTop = (tween.options.bodyScrollTop || window.scrollY) | |
38 | ||
39 | // Apply | |
40 | if ( tween.prop === 'scrollLeft' ) { | |
41 | tween.options.bodyScrollLeft = Math.round(tween.now) | |
42 | } | |
43 | else if ( tween.prop === 'scrollTop' ) { | |
44 | tween.options.bodyScrollTop = Math.round(tween.now) | |
45 | } | |
46 | ||
47 | // Apply | |
48 | window.scrollTo(tween.options.bodyScrollLeft, tween.options.bodyScrollTop) | |
49 | } | |
50 | // jQuery's IE8 Fix | |
51 | else if ( tween.elem.nodeType && tween.elem.parentNode ) { | |
52 | tween.elem[tween.prop] = tween.now | |
53 | } | |
54 | } | |
55 | } | |
56 | ||
57 | // jQuery ScrollTo | |
58 | ScrollTo = { | |
59 | // Configuration | |
60 | config: { | |
61 | duration: 400, | |
62 | easing: 'swing', | |
63 | callback: null, | |
64 | durationMode: 'each', | |
65 | offsetTop: 0, | |
66 | offsetLeft: 0 | |
67 | }, | |
68 | ||
69 | // Set Configuration | |
70 | configure: function (options) { | |
71 | // Apply Options to Config | |
72 | $.extend(ScrollTo.config, options || {}) | |
73 | ||
74 | // Chain | |
75 | return this | |
76 | }, | |
77 | ||
78 | // Perform the Scroll Animation for the Collections | |
79 | // We use $inline here, so we can determine the actual offset start for each overflow:scroll item | |
80 | // Each collection is for each overflow:scroll item | |
81 | scroll: function (collections, config) { | |
82 | // Prepare | |
83 | var collection, $container, $target, $inline, position, | |
84 | containerScrollTop, containerScrollLeft, | |
85 | containerScrollTopEnd, containerScrollLeftEnd, | |
86 | startOffsetTop, targetOffsetTop, targetOffsetTopAdjusted, | |
87 | startOffsetLeft, targetOffsetLeft, targetOffsetLeftAdjusted, | |
88 | scrollOptions, | |
89 | callback | |
90 | ||
91 | // Determine the Scroll | |
92 | collection = collections.pop() | |
93 | $container = collection.$container | |
94 | $target = collection.$target | |
95 | ||
96 | // Prepare the Inline Element of the Container | |
97 | $inline = $('<span/>').css({ | |
98 | 'position': 'absolute', | |
99 | 'top': '0px', | |
100 | 'left': '0px' | |
101 | }) | |
102 | position = $container.css('position') | |
103 | ||
104 | // Insert the Inline Element of the Container | |
105 | $container.css({position: 'relative'}) | |
106 | $inline.appendTo($container) | |
107 | ||
108 | // Determine the top offset | |
109 | startOffsetTop = $inline.offset().top | |
110 | targetOffsetTop = $target.offset().top | |
111 | targetOffsetTopAdjusted = targetOffsetTop - startOffsetTop - parseInt(config.offsetTop, 10) | |
112 | ||
113 | // Determine the left offset | |
114 | startOffsetLeft = $inline.offset().left | |
115 | targetOffsetLeft = $target.offset().left | |
116 | targetOffsetLeftAdjusted = targetOffsetLeft - startOffsetLeft - parseInt(config.offsetLeft, 10) | |
117 | ||
118 | // Determine current scroll positions | |
119 | containerScrollTop = $container.prop('scrollTop') | |
120 | containerScrollLeft = $container.prop('scrollLeft') | |
121 | ||
122 | // Reset the Inline Element of the Container | |
123 | $inline.remove() | |
124 | $container.css({position: position}) | |
125 | ||
126 | // Prepare the scroll options | |
127 | scrollOptions = {} | |
128 | ||
129 | // Prepare the callback | |
130 | callback = function () { | |
131 | // Check | |
132 | if ( collections.length === 0 ) { | |
133 | // Callback | |
134 | if ( typeof config.callback === 'function' ) { | |
135 | config.callback() | |
136 | } | |
137 | } | |
138 | else { | |
139 | // Recurse | |
140 | ScrollTo.scroll(collections, config) | |
141 | } | |
142 | // Return true | |
143 | return true | |
144 | } | |
145 | ||
146 | // Handle if we only want to scroll if we are outside the viewport | |
147 | if ( config.onlyIfOutside ) { | |
148 | // Determine current scroll positions | |
149 | containerScrollTopEnd = containerScrollTop + $container.height() | |
150 | containerScrollLeftEnd = containerScrollLeft + $container.width() | |
151 | ||
152 | // Check if we are in the range of the visible area of the container | |
153 | if ( containerScrollTop < targetOffsetTopAdjusted && targetOffsetTopAdjusted < containerScrollTopEnd ) { | |
154 | targetOffsetTopAdjusted = containerScrollTop | |
155 | } | |
156 | if ( containerScrollLeft < targetOffsetLeftAdjusted && targetOffsetLeftAdjusted < containerScrollLeftEnd ) { | |
157 | targetOffsetLeftAdjusted = containerScrollLeft | |
158 | } | |
159 | } | |
160 | ||
161 | // Determine the scroll options | |
162 | if ( targetOffsetTopAdjusted !== containerScrollTop ) { | |
163 | scrollOptions.scrollTop = targetOffsetTopAdjusted | |
164 | } | |
165 | if ( targetOffsetLeftAdjusted !== containerScrollLeft ) { | |
166 | scrollOptions.scrollLeft = targetOffsetLeftAdjusted | |
167 | } | |
168 | ||
169 | // Check to see if the scroll is necessary | |
170 | if ( $container.prop('scrollHeight') === $container.height() ) { | |
171 | delete scrollOptions.scrollTop | |
172 | } | |
173 | if ( $container.prop('scrollWidth') === $container.width() ) { | |
174 | delete scrollOptions.scrollLeft | |
175 | } | |
176 | ||
177 | // Perform the scroll | |
178 | if ( scrollOptions.scrollTop != null || scrollOptions.scrollLeft != null ) { | |
179 | $container.animate(scrollOptions, { | |
180 | duration: config.duration, | |
181 | easing: config.easing, | |
182 | complete: callback | |
183 | }) | |
184 | } | |
185 | else { | |
186 | callback() | |
187 | } | |
188 | ||
189 | // Return true | |
190 | return true | |
191 | }, | |
192 | ||
193 | // ScrollTo the Element using the Options | |
194 | fn: function (options) { | |
195 | // Prepare | |
196 | var collections, config, $container, container | |
197 | collections = [] | |
198 | ||
199 | // Prepare | |
200 | var $target = $(this) | |
201 | if ( $target.length === 0 ) { | |
202 | // Chain | |
203 | return this | |
204 | } | |
205 | ||
206 | // Handle Options | |
207 | config = $.extend({}, ScrollTo.config, options) | |
208 | ||
209 | // Fetch | |
210 | $container = $target.parent() | |
211 | container = $container.get(0) | |
212 | ||
213 | // Cycle through the containers | |
214 | while ( ($container.length === 1) && (container !== document.body) && (container !== document) ) { | |
215 | // Check Container for scroll differences | |
216 | var containerScrollTop, containerScrollLeft | |
217 | containerScrollTop = $container.css('overflow-y') !== 'visible' && container.scrollHeight !== container.clientHeight | |
218 | containerScrollLeft = $container.css('overflow-x') !== 'visible' && container.scrollWidth !== container.clientWidth | |
219 | if ( containerScrollTop || containerScrollLeft ) { | |
220 | // Push the Collection | |
221 | collections.push({ | |
222 | '$container': $container, | |
223 | '$target': $target | |
224 | }) | |
225 | // Update the Target | |
226 | $target = $container | |
227 | } | |
228 | // Update the Container | |
229 | $container = $container.parent() | |
230 | container = $container.get(0) | |
231 | } | |
232 | ||
233 | // Add the final collection | |
234 | collections.push({ | |
235 | '$container': $('html'), | |
236 | // document.body doesn't work in firefox, html works for all | |
237 | // internet explorer starts at the beggining | |
238 | '$target': $target | |
239 | }) | |
240 | ||
241 | // Adjust the Config | |
242 | if ( config.durationMode === 'all' ) { | |
243 | config.duration /= collections.length | |
244 | } | |
245 | ||
246 | // Handle | |
247 | ScrollTo.scroll(collections, config) | |
248 | ||
249 | // Chain | |
250 | return this | |
251 | } | |
252 | } | |
253 | ||
254 | // Apply our extensions to jQuery | |
255 | $.ScrollTo = $.ScrollTo || ScrollTo | |
256 | $.fn.ScrollTo = $.fn.ScrollTo || ScrollTo.fn | |
257 | ||
258 | // Export | |
259 | return ScrollTo | |
260 | }).call(this) |