1/*! nanoScrollerJS - v0.7.2 2* http://jamesflorentino.github.com/nanoScrollerJS/ 3* Copyright (c) 2013 James Florentino; Licensed MIT */ 4 5 6(function($, window, document) { 7 "use strict"; 8 9 var BROWSER_IS_IE7, BROWSER_SCROLLBAR_WIDTH, DOMSCROLL, DOWN, DRAG, KEYDOWN, KEYUP, MOUSEDOWN, MOUSEMOVE, MOUSEUP, MOUSEWHEEL, NanoScroll, PANEDOWN, RESIZE, SCROLL, SCROLLBAR, TOUCHMOVE, UP, WHEEL, defaults, getBrowserScrollbarWidth; 10 defaults = { 11 /** 12 a classname for the pane element. 13 @property paneClass 14 @type String 15 @default 'pane' 16 */ 17 18 paneClass: 'pane', 19 /** 20 a classname for the slider element. 21 @property sliderClass 22 @type String 23 @default 'slider' 24 */ 25 26 sliderClass: 'slider', 27 /** 28 a classname for the content element. 29 @property contentClass 30 @type String 31 @default 'content' 32 */ 33 34 contentClass: 'content', 35 /** 36 a setting to enable native scrolling in iOS devices. 37 @property iOSNativeScrolling 38 @type Boolean 39 @default false 40 */ 41 42 iOSNativeScrolling: false, 43 /** 44 a setting to prevent the rest of the page being 45 scrolled when user scrolls the `.content` element. 46 @property preventPageScrolling 47 @type Boolean 48 @default false 49 */ 50 51 preventPageScrolling: false, 52 /** 53 a setting to disable binding to the resize event. 54 @property disableResize 55 @type Boolean 56 @default false 57 */ 58 59 disableResize: false, 60 /** 61 a setting to make the scrollbar always visible. 62 @property alwaysVisible 63 @type Boolean 64 @default false 65 */ 66 67 alwaysVisible: false, 68 /** 69 a default timeout for the `flash()` method. 70 @property flashDelay 71 @type Number 72 @default 1500 73 */ 74 75 flashDelay: 1500, 76 /** 77 a minimum height for the `.slider` element. 78 @property sliderMinHeight 79 @type Number 80 @default 20 81 */ 82 83 sliderMinHeight: 20, 84 /** 85 a maximum height for the `.slider` element. 86 @property sliderMaxHeight 87 @type Number 88 @default null 89 */ 90 91 sliderMaxHeight: null, 92 /** 93 an alternate document context. 94 @property documentContext 95 @type Document 96 @default null 97 */ 98 99 documentContext: null, 100 /** 101 an alternate window context. 102 @property windowContext 103 @type Window 104 @default null 105 */ 106 107 windowContext: null 108 }; 109 /** 110 @property SCROLLBAR 111 @type String 112 @static 113 @final 114 @private 115 */ 116 117 SCROLLBAR = 'scrollbar'; 118 /** 119 @property SCROLL 120 @type String 121 @static 122 @final 123 @private 124 */ 125 126 SCROLL = 'scroll'; 127 /** 128 @property MOUSEDOWN 129 @type String 130 @final 131 @private 132 */ 133 134 MOUSEDOWN = 'mousedown'; 135 /** 136 @property MOUSEMOVE 137 @type String 138 @static 139 @final 140 @private 141 */ 142 143 MOUSEMOVE = 'mousemove'; 144 /** 145 @property MOUSEWHEEL 146 @type String 147 @final 148 @private 149 */ 150 151 MOUSEWHEEL = 'mousewheel'; 152 /** 153 @property MOUSEUP 154 @type String 155 @static 156 @final 157 @private 158 */ 159 160 MOUSEUP = 'mouseup'; 161 /** 162 @property RESIZE 163 @type String 164 @final 165 @private 166 */ 167 168 RESIZE = 'resize'; 169 /** 170 @property DRAG 171 @type String 172 @static 173 @final 174 @private 175 */ 176 177 DRAG = 'drag'; 178 /** 179 @property UP 180 @type String 181 @static 182 @final 183 @private 184 */ 185 186 UP = 'up'; 187 /** 188 @property PANEDOWN 189 @type String 190 @static 191 @final 192 @private 193 */ 194 195 PANEDOWN = 'panedown'; 196 /** 197 @property DOMSCROLL 198 @type String 199 @static 200 @final 201 @private 202 */ 203 204 DOMSCROLL = 'DOMMouseScroll'; 205 /** 206 @property DOWN 207 @type String 208 @static 209 @final 210 @private 211 */ 212
213 DOWN = 'down'; 214 /** 215 @property WHEEL 216 @type String 217 @static 218 @final 219 @private 220 */ 221 222 WHEEL = 'wheel'; 223 /** 224 @property KEYDOWN 225 @type String 226 @static 227 @final 228 @private 229 */ 230 231 KEYDOWN = 'keydown'; 232 /** 233 @property KEYUP 234 @type String 235 @static 236 @final 237 @private 238 */ 239 240 KEYUP = 'keyup'; 241 /** 242 @property TOUCHMOVE 243 @type String 244 @static 245 @final 246 @private 247 */ 248 249 TOUCHMOVE = 'touchmove'; 250 /** 251 @property BROWSER_IS_IE7 252 @type Boolean 253 @static 254 @final 255 @private 256 */ 257 258 BROWSER_IS_IE7 = window.navigator.appName === 'Microsoft Internet Explorer' && /msie 7./i.test(window.navigator.appVersion) && window.ActiveXObject; 259 /** 260 @property BROWSER_SCROLLBAR_WIDTH 261 @type Number 262 @static 263 @default null 264 @private 265 */ 266 267 BROWSER_SCROLLBAR_WIDTH = null; 268 /** 269 Returns browser's native scrollbar width 270 @method getBrowserScrollbarWidth 271 @return {Number} the scrollbar width in pixels 272 @static 273 @private 274 */ 275 276 getBrowserScrollbarWidth = function() { 277 var outer, outerStyle, scrollbarWidth; 278 outer = document.createElement('div'); 279 outerStyle = outer.style; 280 outerStyle.position = 'absolute'; 281 outerStyle.width = '100px'; 282 outerStyle.height = '100px'; 283 outerStyle.overflow = SCROLL; 284 outerStyle.top = '-9999px'; 285 document.body.appendChild(outer); 286 scrollbarWidth = outer.offsetWidth - outer.clientWidth; 287 document.body.removeChild(outer); 288 return scrollbarWidth; 289 }; 290 /** 291 @class NanoScroll 292 @param element {HTMLElement|Node} the main element 293 @param options {Object} nanoScroller's options 294 @constructor 295 */ 296 297 NanoScroll = (function() { 298 299 function NanoScroll(el, options) { 300 this.el = el; 301 this.options = options; 302 BROWSER_SCROLLBAR_WIDTH || (BROWSER_SCROLLBAR_WIDTH = getBrowserScrollbarWidth()); 303 this.$el = $(this.el); 304 this.doc = $(this.options.documentContext || document); 305 this.win = $(this.options.windowContext || window); 306 this.$content = this.$el.children("." + options.contentClass); 307 this.$content.attr('tabindex', 0); 308 this.content = this.$content[0]; 309 if (this.options.iOSNativeScrolling && (this.el.style.WebkitOverflowScrolling != null)) { 310 this.nativeScrolling(); 311 } else { 312 this.generate(); 313 } 314 this.createEvents(); 315 this.addEvents(); 316 this.reset(); 317 } 318 319 /** 320 Prevents the rest of the page being scrolled 321 when user scrolls the `.content` element. 322 @method preventScrolling 323 @param event {Event} 324 @param direction {String} Scroll direction (up or down) 325 @private 326 */ 327 328 329 NanoScroll.prototype.preventScrolling = function(e, direction) { 330 if (!this.isActive) { 331 return; 332 } 333 if (e.type === DOMSCROLL) { 334 if (direction === DOWN && e.originalEvent.detail > 0 || direction === UP && e.originalEvent.detail < 0) { 335 e.preventDefault(); 336 } 337 } else if (e.type === MOUSEWHEEL) { 338 if (!e.originalEvent || !e.originalEvent.wheelDelta) { 339 return; 340 } 341 if (direction === DOWN && e.originalEvent.wheelDelta < 0 || direction === UP && e.originalEvent.wheelDelta > 0) { 342 e.preventDefault(); 343 } 344 } 345 }; 346 347 /** 348 Enable iOS native scrolling 349 */ 350 351 352 NanoScroll.prototype.nativeScrolling = function() { 353 this.$content.css({ 354 WebkitOverflowScrolling: 'touch' 355 }); 356 this.iOSNativeScrolling = true; 357 this.isActive = true; 358 }; 359 360 /** 361 Updates those nanoScroller properties that 362 are related to current scrollbar position. 363 @method updateScrollValues 364 @private 365 */ 366 367 368 NanoScroll.prototype.updateScrollValues = function() { 369 var content; 370 content = this.content; 371 this.maxScrollTop = content.scrollHeight - content.clientHeight; 372 this.contentScrollTop = content.scrollTop; 373 if (!this.iOSNativeScrolling) { 374 this.maxSliderTop = this.paneHeight - this.sliderHeight; 375 this.sliderTop = this.contentScrollTop * this.maxSliderTop / this.maxScrollTop; 376 } 377 }; 378 379 /** 380 Creates event related methods 381 @method createEvents 382 @private 383 */ 384 385 386 NanoScroll.prototype.createEvents = function() { 387 var _this = this; 388 this.events = { 389 down: function(e) { 390 _this.isBeingDragged = true; 391 _this.offsetY = e.pageY - _this.slider.offset().top; 392 _this.pane.addClass('active'); 393 _this.doc.bind(MOUSEMOVE, _this.events[DRAG]).bind(MOUSEUP, _this.events[UP]); 394 return false;
395 }, 396 drag: function(e) { 397 _this.sliderY = e.pageY - _this.$el.offset().top - _this.offsetY; 398 _this.scroll(); 399 _this.updateScrollValues(); 400 if (_this.contentScrollTop >= _this.maxScrollTop) { 401 _this.$el.trigger('scrollend'); 402 } else if (_this.contentScrollTop === 0) { 403 _this.$el.trigger('scrolltop'); 404 } 405 return false; 406 }, 407 up: function(e) { 408 _this.isBeingDragged = false; 409 _this.pane.removeClass('active'); 410 _this.doc.unbind(MOUSEMOVE, _this.events[DRAG]).unbind(MOUSEUP, _this.events[UP]); 411 return false; 412 }, 413 resize: function(e) { 414 _this.reset(); 415 }, 416 panedown: function(e) { 417 _this.sliderY = (e.offsetY || e.originalEvent.layerY) - (_this.sliderHeight * 0.5); 418 _this.scroll(); 419 _this.events.down(e); 420 return false; 421 }, 422 scroll: function(e) { 423 if (_this.isBeingDragged) { 424 return; 425 } 426 _this.updateScrollValues(); 427 if (!_this.iOSNativeScrolling) { 428 _this.sliderY = _this.sliderTop; 429 _this.slider.css({ 430 top: _this.sliderTop 431 }); 432 } 433 if (e == null) { 434 return; 435 } 436 if (_this.contentScrollTop >= _this.maxScrollTop) { 437 if (_this.options.preventPageScrolling) { 438 _this.preventScrolling(e, DOWN); 439 } 440 _this.$el.trigger('scrollend'); 441 } else if (_this.contentScrollTop === 0) { 442 if (_this.options.preventPageScrolling) { 443 _this.preventScrolling(e, UP); 444 } 445 _this.$el.trigger('scrolltop'); 446 } 447 }, 448 wheel: function(e) { 449 if (e == null) { 450 return; 451 } 452 _this.sliderY += -e.wheelDeltaY || -e.delta; 453 _this.scroll(); 454 return false; 455 } 456 }; 457 }; 458 459 /** 460 Adds event listeners with jQuery. 461 @method addEvents 462 @private 463 */ 464 465 466 NanoScroll.prototype.addEvents = function() { 467 var events; 468 this.removeEvents(); 469 events = this.events; 470 if (!this.options.disableResize) { 471 this.win.bind(RESIZE, events[RESIZE]); 472 } 473 if (!this.iOSNativeScrolling) { 474 this.slider.bind(MOUSEDOWN, events[DOWN]); 475 this.pane.bind(MOUSEDOWN, events[PANEDOWN]).bind("" + MOUSEWHEEL + " " + DOMSCROLL, events[WHEEL]); 476 } 477 this.$content.bind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]); 478 }; 479 480 /** 481 Removes event listeners with jQuery. 482 @method removeEvents 483 @private 484 */ 485 486 487 NanoScroll.prototype.removeEvents = function() { 488 var events; 489 events = this.events; 490 this.win.unbind(RESIZE, events[RESIZE]); 491 if (!this.iOSNativeScrolling) { 492 this.slider.unbind(); 493 this.pane.unbind(); 494 } 495 this.$content.unbind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]); 496 }; 497 498 /** 499 Generates nanoScroller's scrollbar and elements for it. 500 @method generate 501 @chainable 502 @private 503 */ 504 505 506 NanoScroll.prototype.generate = function() { 507 var contentClass, cssRule, options, paneClass, sliderClass; 508 options = this.options; 509 paneClass = options.paneClass, sliderClass = options.sliderClass, contentClass = options.contentClass; 510 if (!this.$el.find("" + paneClass).length && !this.$el.find("" + sliderClass).length) { 511 this.$el.append("<div class=\"" + paneClass + "\"><div class=\"" + sliderClass + "\" /></div>"); 512 } 513 this.pane = this.$el.children("." + paneClass); 514 this.slider = this.pane.find("." + sliderClass); 515 if (BROWSER_SCROLLBAR_WIDTH) { 516 cssRule = this.$el.css('direction') === 'rtl' ? { 517 left: -BROWSER_SCROLLBAR_WIDTH 518 } : { 519 right: -BROWSER_SCROLLBAR_WIDTH 520 }; 521 this.$el.addClass('has-scrollbar'); 522 } 523 if (cssRule != null) { 524 this.$content.css(cssRule); 525 } 526 return this; 527 }; 528 529 /** 530 @method restore 531 @private 532 */ 533 534 535 NanoScroll.prototype.restore = function() { 536 this.stopped = false;
537 this.pane.show(); 538 this.addEvents(); 539 }; 540 541 /** 542 Resets nanoScroller's scrollbar. 543 @method reset 544 @chainable 545 @example 546 $(".nano").nanoScroller(); 547 */ 548 549 550 NanoScroll.prototype.reset = function() { 551 var content, contentHeight, contentStyle, contentStyleOverflowY, paneBottom, paneHeight, paneOuterHeight, paneTop, sliderHeight; 552 if (this.iOSNativeScrolling) { 553 this.contentHeight = this.content.scrollHeight; 554 return; 555 } 556 if (!this.$el.find("." + this.options.paneClass).length) { 557 this.generate().stop(); 558 } 559 if (this.stopped) { 560 this.restore(); 561 } 562 content = this.content; 563 contentStyle = content.style; 564 contentStyleOverflowY = contentStyle.overflowY; 565 if (BROWSER_IS_IE7) { 566 this.$content.css({ 567 height: this.$content.height() 568 }); 569 } 570 contentHeight = content.scrollHeight + BROWSER_SCROLLBAR_WIDTH; 571 paneHeight = this.pane.outerHeight(); 572 paneTop = parseInt(this.pane.css('top'), 10); 573 paneBottom = parseInt(this.pane.css('bottom'), 10); 574 paneOuterHeight = paneHeight + paneTop + paneBottom; 575 sliderHeight = Math.round(paneOuterHeight / contentHeight * paneOuterHeight); 576 if (sliderHeight < this.options.sliderMinHeight) { 577 sliderHeight = this.options.sliderMinHeight; 578 } else if ((this.options.sliderMaxHeight != null) && sliderHeight > this.options.sliderMaxHeight) { 579 sliderHeight = this.options.sliderMaxHeight; 580 } 581 if (contentStyleOverflowY === SCROLL && contentStyle.overflowX !== SCROLL) { 582 sliderHeight += BROWSER_SCROLLBAR_WIDTH; 583 } 584 this.maxSliderTop = paneOuterHeight - sliderHeight; 585 this.contentHeight = contentHeight; 586 this.paneHeight = paneHeight; 587 this.paneOuterHeight = paneOuterHeight; 588 this.sliderHeight = sliderHeight; 589 this.slider.height(sliderHeight); 590 this.events.scroll(); 591 this.pane.show(); 592 this.isActive = true; 593 if ((content.scrollHeight === content.clientHeight) || (this.pane.outerHeight(true) >= content.scrollHeight && contentStyleOverflowY !== SCROLL)) { 594 this.pane.hide(); 595 this.isActive = false; 596 } else if (this.el.clientHeight === content.scrollHeight && contentStyleOverflowY === SCROLL) { 597 this.slider.hide(); 598 } else { 599 this.slider.show(); 600 } 601 this.pane.css({ 602 opacity: (this.options.alwaysVisible ? 1 : ''), 603 visibility: (this.options.alwaysVisible ? 'visible' : '') 604 }); 605 return this; 606 }; 607 608 /** 609 @method scroll 610 @private 611 @example 612 $(".nano").nanoScroller({ scroll: 'top' }); 613 */ 614 615 616 NanoScroll.prototype.scroll = function() { 617 if (!this.isActive) { 618 return; 619 } 620 this.sliderY = Math.max(0, this.sliderY); 621 this.sliderY = Math.min(this.maxSliderTop, this.sliderY); 622 this.$content.scrollTop((this.paneHeight - this.contentHeight + BROWSER_SCROLLBAR_WIDTH) * this.sliderY / this.maxSliderTop * -1); 623 if (!this.iOSNativeScrolling) { 624 this.slider.css({ 625 top: this.sliderY 626 }); 627 } 628 return this; 629 }; 630 631 /** 632 Scroll at the bottom with an offset value 633 @method scrollBottom 634 @param offsetY {Number} 635 @chainable 636 @example 637 $(".nano").nanoScroller({ scrollBottom: value }); 638 */ 639 640 641 NanoScroll.prototype.scrollBottom = function(offsetY) { 642 if (!this.isActive) { 643 return; 644 } 645 this.reset(); 646 this.$content.scrollTop(this.contentHeight - this.$content.height() - offsetY).trigger(MOUSEWHEEL); 647 return this; 648 }; 649 650 /** 651 Scroll at the top with an offset value 652 @method scrollTop 653 @param offsetY {Number} 654 @chainable 655 @example 656 $(".nano").nanoScroller({ scrollTop: value }); 657 */ 658 659 660 NanoScroll.prototype.scrollTop = function(offsetY) { 661 if (!this.isActive) { 662 return; 663 } 664 this.reset(); 665 this.$content.scrollTop(+offsetY).trigger(MOUSEWHEEL); 666 return this; 667 }; 668 669 /** 670 Scroll to an element 671 @method scrollTo 672 @param node {Node} A node to scroll to. 673 @chainable 674 @example 675 $(".nano").nanoScroller({ scrollTo: $('#a_node') }); 676 */ 677 678 679 NanoScroll.prototype.scrollTo = function(node) { 680 if (!this.isActive) { 681 return; 682 } 683 this.reset(); 684 this.scrollTop($(node).get(0).offsetTop); 685 return this; 686 }; 687 688 /** 689 To stop the operation. 690 This option will tell the plugin to disable all event bindings and hide the gadget scrollbar from the UI. 691 @method stop 692 @chainable 693 @example 694 $(".nano").nanoScroller({ stop: true }); 695 */ 696 697 698 NanoScroll.prototype.stop = function() { 699 this.stopped = true; 700 this.removeEvents(); 701 this.pane.hide(); 702 return this; 703 }; 704 705 /** 706 To flash the scrollbar gadget for an amount of time defined in plugin settings (defaults to 1,5s). 707 Useful if you want to show the user (e.g. on pageload) that there is more content waiting for him. 708 @method flash 709 @chainable 710 @example 711 $(".nano").nanoScroller({ flash: true }); 712 */ 713 714 715 NanoScroll.prototype.flash = function() { 716 var _this = this; 717 if (!this.isActive) { 718 return; 719 } 720 this.reset(); 721 this.pane.addClass('flashed'); 722 setTimeout(function() { 723 _this.pane.removeClass('flashed'); 724 }, this.options.flashDelay); 725 return this; 726 }; 727 728 return NanoScroll; 729 730 })(); 731 $.fn.nanoScroller = function(settings) { 732 return this.each(function() { 733 var options, scrollbar; 734 if (!(scrollbar = this.nanoscroller)) { 735 options = $.extend({}, defaults, settings);
736 this.nanoscroller = scrollbar = new NanoScroll(this, options); 737 } 738 if (settings && typeof settings === "object") { 739 $.extend(scrollbar.options, settings); 740 if (settings.scrollBottom) { 741 return scrollbar.scrollBottom(settings.scrollBottom); 742 } 743 if (settings.scrollTop) { 744 return scrollbar.scrollTop(settings.scrollTop); 745 } 746 if (settings.scrollTo) { 747 return scrollbar.scrollTo(settings.scrollTo); 748 } 749 if (settings.scroll === 'bottom') { 750 return scrollbar.scrollBottom(0); 751 } 752 if (settings.scroll === 'top') { 753 return scrollbar.scrollTop(0); 754 } 755 if (settings.scroll && settings.scroll instanceof $) { 756 return scrollbar.scrollTo(settings.scroll); 757 } 758 if (settings.stop) { 759 return scrollbar.stop(); 760 } 761 if (settings.flash) { 762 return scrollbar.flash(); 763 } 764 } 765 return scrollbar.reset(); 766 }); 767 }; 768})(jQuery, window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.