]>
Commit | Line | Data |
---|---|---|
31f18b77 FG |
1 | /*! Copyright (c) 2011 Piotr Rochala (http://rocha.la) |
2 | * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) | |
3 | * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. | |
4 | * | |
5 | * Version: 1.3.8 | |
6 | * | |
7 | */ | |
8 | (function($) { | |
9 | ||
10 | $.fn.extend({ | |
11 | slimScroll: function(options) { | |
12 | ||
13 | var defaults = { | |
14 | ||
15 | // width in pixels of the visible scroll area | |
16 | width : 'auto', | |
17 | ||
18 | // height in pixels of the visible scroll area | |
19 | height : '250px', | |
20 | ||
21 | // width in pixels of the scrollbar and rail | |
22 | size : '7px', | |
23 | ||
24 | // scrollbar color, accepts any hex/color value | |
25 | color: '#000', | |
26 | ||
27 | // scrollbar position - left/right | |
28 | position : 'right', | |
29 | ||
30 | // distance in pixels between the side edge and the scrollbar | |
31 | distance : '1px', | |
32 | ||
33 | // default scroll position on load - top / bottom / $('selector') | |
34 | start : 'top', | |
35 | ||
36 | // sets scrollbar opacity | |
37 | opacity : .4, | |
38 | ||
39 | // enables always-on mode for the scrollbar | |
40 | alwaysVisible : false, | |
41 | ||
42 | // check if we should hide the scrollbar when user is hovering over | |
43 | disableFadeOut : false, | |
44 | ||
45 | // sets visibility of the rail | |
46 | railVisible : false, | |
47 | ||
48 | // sets rail color | |
49 | railColor : '#333', | |
50 | ||
51 | // sets rail opacity | |
52 | railOpacity : .2, | |
53 | ||
54 | // whether we should use jQuery UI Draggable to enable bar dragging | |
55 | railDraggable : true, | |
56 | ||
57 | // defautlt CSS class of the slimscroll rail | |
58 | railClass : 'slimScrollRail', | |
59 | ||
60 | // defautlt CSS class of the slimscroll bar | |
61 | barClass : 'slimScrollBar', | |
62 | ||
63 | // defautlt CSS class of the slimscroll wrapper | |
64 | wrapperClass : 'slimScrollDiv', | |
65 | ||
66 | // check if mousewheel should scroll the window if we reach top/bottom | |
67 | allowPageScroll : false, | |
68 | ||
69 | // scroll amount applied to each mouse wheel step | |
70 | wheelStep : 20, | |
71 | ||
72 | // scroll amount applied when user is using gestures | |
73 | touchScrollStep : 200, | |
74 | ||
75 | // sets border radius | |
76 | borderRadius: '7px', | |
77 | ||
78 | // sets border radius of the rail | |
79 | railBorderRadius : '7px' | |
80 | }; | |
81 | ||
82 | var o = $.extend(defaults, options); | |
83 | ||
84 | // do it for every element that matches selector | |
85 | this.each(function(){ | |
86 | ||
87 | var isOverPanel, isOverBar, isDragg, queueHide, touchDif, | |
88 | barHeight, percentScroll, lastScroll, | |
89 | divS = '<div></div>', | |
90 | minBarHeight = 30, | |
91 | releaseScroll = false; | |
92 | ||
93 | // used in event handlers and for better minification | |
94 | var me = $(this); | |
95 | ||
96 | // ensure we are not binding it again | |
97 | if (me.parent().hasClass(o.wrapperClass)) | |
98 | { | |
99 | // start from last bar position | |
100 | var offset = me.scrollTop(); | |
101 | ||
102 | // find bar and rail | |
103 | bar = me.siblings('.' + o.barClass); | |
104 | rail = me.siblings('.' + o.railClass); | |
105 | ||
106 | getBarHeight(); | |
107 | ||
108 | // check if we should scroll existing instance | |
109 | if ($.isPlainObject(options)) | |
110 | { | |
111 | // Pass height: auto to an existing slimscroll object to force a resize after contents have changed | |
112 | if ( 'height' in options && options.height == 'auto' ) { | |
113 | me.parent().css('height', 'auto'); | |
114 | me.css('height', 'auto'); | |
115 | var height = me.parent().parent().height(); | |
116 | me.parent().css('height', height); | |
117 | me.css('height', height); | |
118 | } else if ('height' in options) { | |
119 | var h = options.height; | |
120 | me.parent().css('height', h); | |
121 | me.css('height', h); | |
122 | } | |
123 | ||
124 | if ('scrollTo' in options) | |
125 | { | |
126 | // jump to a static point | |
127 | offset = parseInt(o.scrollTo); | |
128 | } | |
129 | else if ('scrollBy' in options) | |
130 | { | |
131 | // jump by value pixels | |
132 | offset += parseInt(o.scrollBy); | |
133 | } | |
134 | else if ('destroy' in options) | |
135 | { | |
136 | // remove slimscroll elements | |
137 | bar.remove(); | |
138 | rail.remove(); | |
139 | me.unwrap(); | |
140 | return; | |
141 | } | |
142 | ||
143 | // scroll content by the given offset | |
144 | scrollContent(offset, false, true); | |
145 | } | |
146 | ||
147 | return; | |
148 | } | |
149 | else if ($.isPlainObject(options)) | |
150 | { | |
151 | if ('destroy' in options) | |
152 | { | |
153 | return; | |
154 | } | |
155 | } | |
156 | ||
157 | // optionally set height to the parent's height | |
158 | o.height = (o.height == 'auto') ? me.parent().height() : o.height; | |
159 | ||
160 | // wrap content | |
161 | var wrapper = $(divS) | |
162 | .addClass(o.wrapperClass) | |
163 | .css({ | |
164 | position: 'relative', | |
165 | overflow: 'hidden', | |
166 | width: o.width, | |
167 | height: o.height | |
168 | }); | |
169 | ||
170 | // update style for the div | |
171 | me.css({ | |
172 | overflow: 'hidden', | |
173 | width: o.width, | |
174 | height: o.height | |
175 | }); | |
176 | ||
177 | // create scrollbar rail | |
178 | var rail = $(divS) | |
179 | .addClass(o.railClass) | |
180 | .css({ | |
181 | width: o.size, | |
182 | height: '100%', | |
183 | position: 'absolute', | |
184 | top: 0, | |
185 | display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none', | |
186 | 'border-radius': o.railBorderRadius, | |
187 | background: o.railColor, | |
188 | opacity: o.railOpacity, | |
189 | zIndex: 90 | |
190 | }); | |
191 | ||
192 | // create scrollbar | |
193 | var bar = $(divS) | |
194 | .addClass(o.barClass) | |
195 | .css({ | |
196 | background: o.color, | |
197 | width: o.size, | |
198 | position: 'absolute', | |
199 | top: 0, | |
200 | opacity: o.opacity, | |
201 | display: o.alwaysVisible ? 'block' : 'none', | |
202 | 'border-radius' : o.borderRadius, | |
203 | BorderRadius: o.borderRadius, | |
204 | MozBorderRadius: o.borderRadius, | |
205 | WebkitBorderRadius: o.borderRadius, | |
206 | zIndex: 99 | |
207 | }); | |
208 | ||
209 | // set position | |
210 | var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance }; | |
211 | rail.css(posCss); | |
212 | bar.css(posCss); | |
213 | ||
214 | // wrap it | |
215 | me.wrap(wrapper); | |
216 | ||
217 | // append to parent div | |
218 | me.parent().append(bar); | |
219 | me.parent().append(rail); | |
220 | ||
221 | // make it draggable and no longer dependent on the jqueryUI | |
222 | if (o.railDraggable){ | |
223 | bar.bind("mousedown", function(e) { | |
224 | var $doc = $(document); | |
225 | isDragg = true; | |
226 | t = parseFloat(bar.css('top')); | |
227 | pageY = e.pageY; | |
228 | ||
229 | $doc.bind("mousemove.slimscroll", function(e){ | |
230 | currTop = t + e.pageY - pageY; | |
231 | bar.css('top', currTop); | |
232 | scrollContent(0, bar.position().top, false);// scroll content | |
233 | }); | |
234 | ||
235 | $doc.bind("mouseup.slimscroll", function(e) { | |
236 | isDragg = false;hideBar(); | |
237 | $doc.unbind('.slimscroll'); | |
238 | }); | |
239 | return false; | |
240 | }).bind("selectstart.slimscroll", function(e){ | |
241 | e.stopPropagation(); | |
242 | e.preventDefault(); | |
243 | return false; | |
244 | }); | |
245 | } | |
246 | ||
247 | // on rail over | |
248 | rail.hover(function(){ | |
249 | showBar(); | |
250 | }, function(){ | |
251 | hideBar(); | |
252 | }); | |
253 | ||
254 | // on bar over | |
255 | bar.hover(function(){ | |
256 | isOverBar = true; | |
257 | }, function(){ | |
258 | isOverBar = false; | |
259 | }); | |
260 | ||
261 | // show on parent mouseover | |
262 | me.hover(function(){ | |
263 | isOverPanel = true; | |
264 | showBar(); | |
265 | hideBar(); | |
266 | }, function(){ | |
267 | isOverPanel = false; | |
268 | hideBar(); | |
269 | }); | |
270 | ||
271 | // support for mobile | |
272 | me.bind('touchstart', function(e,b){ | |
273 | if (e.originalEvent.touches.length) | |
274 | { | |
275 | // record where touch started | |
276 | touchDif = e.originalEvent.touches[0].pageY; | |
277 | } | |
278 | }); | |
279 | ||
280 | me.bind('touchmove', function(e){ | |
281 | // prevent scrolling the page if necessary | |
282 | if(!releaseScroll) | |
283 | { | |
284 | e.originalEvent.preventDefault(); | |
285 | } | |
286 | if (e.originalEvent.touches.length) | |
287 | { | |
288 | // see how far user swiped | |
289 | var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep; | |
290 | // scroll content | |
291 | scrollContent(diff, true); | |
292 | touchDif = e.originalEvent.touches[0].pageY; | |
293 | } | |
294 | }); | |
295 | ||
296 | // set up initial height | |
297 | getBarHeight(); | |
298 | ||
299 | // check start position | |
300 | if (o.start === 'bottom') | |
301 | { | |
302 | // scroll content to bottom | |
303 | bar.css({ top: me.outerHeight() - bar.outerHeight() }); | |
304 | scrollContent(0, true); | |
305 | } | |
306 | else if (o.start !== 'top') | |
307 | { | |
308 | // assume jQuery selector | |
309 | scrollContent($(o.start).position().top, null, true); | |
310 | ||
311 | // make sure bar stays hidden | |
312 | if (!o.alwaysVisible) { bar.hide(); } | |
313 | } | |
314 | ||
315 | // attach scroll events | |
316 | attachWheel(this); | |
317 | ||
318 | function _onWheel(e) | |
319 | { | |
320 | // use mouse wheel only when mouse is over | |
321 | if (!isOverPanel) { return; } | |
322 | ||
323 | var e = e || window.event; | |
324 | ||
325 | var delta = 0; | |
326 | if (e.wheelDelta) { delta = -e.wheelDelta/120; } | |
327 | if (e.detail) { delta = e.detail / 3; } | |
328 | ||
329 | var target = e.target || e.srcTarget || e.srcElement; | |
330 | if ($(target).closest('.' + o.wrapperClass).is(me.parent())) { | |
331 | // scroll content | |
332 | scrollContent(delta, true); | |
333 | } | |
334 | ||
335 | // stop window scroll | |
336 | if (e.preventDefault && !releaseScroll) { e.preventDefault(); } | |
337 | if (!releaseScroll) { e.returnValue = false; } | |
338 | } | |
339 | ||
340 | function scrollContent(y, isWheel, isJump) | |
341 | { | |
342 | releaseScroll = false; | |
343 | var delta = y; | |
344 | var maxTop = me.outerHeight() - bar.outerHeight(); | |
345 | ||
346 | if (isWheel) | |
347 | { | |
348 | // move bar with mouse wheel | |
349 | delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight(); | |
350 | ||
351 | // move bar, make sure it doesn't go out | |
352 | delta = Math.min(Math.max(delta, 0), maxTop); | |
353 | ||
354 | // if scrolling down, make sure a fractional change to the | |
355 | // scroll position isn't rounded away when the scrollbar's CSS is set | |
356 | // this flooring of delta would happened automatically when | |
357 | // bar.css is set below, but we floor here for clarity | |
358 | delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta); | |
359 | ||
360 | // scroll the scrollbar | |
361 | bar.css({ top: delta + 'px' }); | |
362 | } | |
363 | ||
364 | // calculate actual scroll amount | |
365 | percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight()); | |
366 | delta = percentScroll * (me[0].scrollHeight - me.outerHeight()); | |
367 | ||
368 | if (isJump) | |
369 | { | |
370 | delta = y; | |
371 | var offsetTop = delta / me[0].scrollHeight * me.outerHeight(); | |
372 | offsetTop = Math.min(Math.max(offsetTop, 0), maxTop); | |
373 | bar.css({ top: offsetTop + 'px' }); | |
374 | } | |
375 | ||
376 | // scroll content | |
377 | me.scrollTop(delta); | |
378 | ||
379 | // fire scrolling event | |
380 | me.trigger('slimscrolling', ~~delta); | |
381 | ||
382 | // ensure bar is visible | |
383 | showBar(); | |
384 | ||
385 | // trigger hide when scroll is stopped | |
386 | hideBar(); | |
387 | } | |
388 | ||
389 | function attachWheel(target) | |
390 | { | |
391 | if (window.addEventListener) | |
392 | { | |
393 | target.addEventListener('DOMMouseScroll', _onWheel, false ); | |
394 | target.addEventListener('mousewheel', _onWheel, false ); | |
395 | } | |
396 | else | |
397 | { | |
398 | document.attachEvent("onmousewheel", _onWheel) | |
399 | } | |
400 | } | |
401 | ||
402 | function getBarHeight() | |
403 | { | |
404 | // calculate scrollbar height and make sure it is not too small | |
405 | barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight); | |
406 | bar.css({ height: barHeight + 'px' }); | |
407 | ||
408 | // hide scrollbar if content is not long enough | |
409 | var display = barHeight == me.outerHeight() ? 'none' : 'block'; | |
410 | bar.css({ display: display }); | |
411 | } | |
412 | ||
413 | function showBar() | |
414 | { | |
415 | // recalculate bar height | |
416 | getBarHeight(); | |
417 | clearTimeout(queueHide); | |
418 | ||
419 | // when bar reached top or bottom | |
420 | if (percentScroll == ~~percentScroll) | |
421 | { | |
422 | //release wheel | |
423 | releaseScroll = o.allowPageScroll; | |
424 | ||
425 | // publish approporiate event | |
426 | if (lastScroll != percentScroll) | |
427 | { | |
428 | var msg = (~~percentScroll == 0) ? 'top' : 'bottom'; | |
429 | me.trigger('slimscroll', msg); | |
430 | } | |
431 | } | |
432 | else | |
433 | { | |
434 | releaseScroll = false; | |
435 | } | |
436 | lastScroll = percentScroll; | |
437 | ||
438 | // show only when required | |
439 | if(barHeight >= me.outerHeight()) { | |
440 | //allow window scroll | |
441 | releaseScroll = true; | |
442 | return; | |
443 | } | |
444 | bar.stop(true,true).fadeIn('fast'); | |
445 | if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); } | |
446 | } | |
447 | ||
448 | function hideBar() | |
449 | { | |
450 | // only hide when options allow it | |
451 | if (!o.alwaysVisible) | |
452 | { | |
453 | queueHide = setTimeout(function(){ | |
454 | if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg) | |
455 | { | |
456 | bar.fadeOut('slow'); | |
457 | rail.fadeOut('slow'); | |
458 | } | |
459 | }, 1000); | |
460 | } | |
461 | } | |
462 | ||
463 | }); | |
464 | ||
465 | // maintain chainability | |
466 | return this; | |
467 | } | |
468 | }); | |
469 | ||
470 | $.fn.extend({ | |
471 | slimscroll: $.fn.slimScroll | |
472 | }); | |
473 | ||
474 | })(jQuery); |