nanoscroller.js 25 KB

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