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