jquery.jscroll.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*!
  2. * jScroll - jQuery Plugin for Infinite Scrolling / Auto-Paging - v2.2.4
  3. * http://jscroll.com/
  4. *
  5. * Copyright 2011-2013, Philip Klauzinski
  6. * http://klauzinski.com/
  7. * Dual licensed under the MIT and GPL Version 2 licenses.
  8. * http://jscroll.com/#license
  9. * http://www.opensource.org/licenses/mit-license.php
  10. * http://www.gnu.org/licenses/gpl-2.0.html
  11. *
  12. * @author Philip Klauzinski
  13. * @requires jQuery v1.4.3+
  14. */
  15. (function($) {
  16. // Define the jscroll namespace and default settings
  17. $.jscroll = {
  18. defaults: {
  19. debug: false,
  20. autoTrigger: true,
  21. autoTriggerUntil: false,
  22. loadingHtml: '<small>Loading...</small>',
  23. padding: 0,
  24. nextSelector: 'a:last',
  25. contentSelector: '',
  26. pagingSelector: '',
  27. callback: false
  28. }
  29. };
  30. // Constructor
  31. var jScroll = function($e, options) {
  32. // Private vars
  33. var _data = $e.data('jscroll'),
  34. _userOptions = (typeof options === 'function') ? { callback: options } : options,
  35. _options = $.extend({}, $.jscroll.defaults, _userOptions, _data || {}),
  36. _isWindow = ($e.css('overflow-y') === 'visible'),
  37. _$next = $e.find(_options.nextSelector).first(),
  38. _$window = $(window),
  39. _$body = $('body'),
  40. _$scroll = _isWindow ? _$window : $e,
  41. _nextHref = $.trim(_$next.attr('href') + ' ' + _options.contentSelector);
  42. // Initialization
  43. $e.data('jscroll', $.extend({}, _data, {initialized: true, waiting: false, nextHref: _nextHref}));
  44. _wrapInnerContent();
  45. _preloadImage();
  46. _setBindings();
  47. // Private methods
  48. // Check if a loading image is defined and preload
  49. function _preloadImage() {
  50. var src = $(_options.loadingHtml).filter('img').attr('src');
  51. if (src) {
  52. var image = new Image();
  53. image.src = src;
  54. }
  55. }
  56. // Wrapper inner content, if it isn't already
  57. function _wrapInnerContent() {
  58. if (!$e.find('.jscroll-inner').length) {
  59. $e.contents().wrapAll('<div class="jscroll-inner" />');
  60. }
  61. }
  62. // Find the next link's parent, or add one, and hide it
  63. function _nextWrap($next) {
  64. if (_options.pagingSelector) {
  65. var $parent = $next.closest(_options.pagingSelector).hide();
  66. } else {
  67. var $parent = $next.parent().not('.jscroll-inner,.jscroll-added').addClass('jscroll-next-parent').hide();
  68. if (!$parent.length) {
  69. $next.wrap('<div class="jscroll-next-parent" />').parent().hide();
  70. }
  71. }
  72. }
  73. // Remove the jscroll behavior and data from an element
  74. function _destroy() {
  75. return _$scroll.unbind('.jscroll')
  76. .removeData('jscroll')
  77. .find('.jscroll-inner').children().unwrap()
  78. .filter('.jscroll-added').children().unwrap();
  79. }
  80. // Observe the scroll event for when to trigger the next load
  81. function _observe() {
  82. _wrapInnerContent();
  83. var $inner = $e.find('div.jscroll-inner').first(),
  84. data = $e.data('jscroll'),
  85. borderTopWidth = parseInt($e.css('borderTopWidth')),
  86. borderTopWidthInt = isNaN(borderTopWidth) ? 0 : borderTopWidth,
  87. iContainerTop = parseInt($e.css('paddingTop')) + borderTopWidthInt,
  88. iTopHeight = _isWindow ? _$scroll.scrollTop() : $e.offset().top,
  89. innerTop = $inner.length ? $inner.offset().top : 0,
  90. iTotalHeight = Math.ceil(iTopHeight - innerTop + _$scroll.height() + iContainerTop);
  91. if (!data.waiting && iTotalHeight + _options.padding >= $inner.outerHeight()) {
  92. //data.nextHref = $.trim(data.nextHref + ' ' + _options.contentSelector);
  93. _debug('info', 'jScroll:', $inner.outerHeight() - iTotalHeight, 'from bottom. Loading next request...');
  94. return _load();
  95. }
  96. }
  97. // Check if the href for the next set of content has been set
  98. function _checkNextHref(data) {
  99. data = data || $e.data('jscroll');
  100. if (!data || !data.nextHref) {
  101. _debug('warn', 'jScroll: nextSelector not found - destroying');
  102. _destroy();
  103. return false;
  104. } else {
  105. _setBindings();
  106. return true;
  107. }
  108. }
  109. function _setBindings() {
  110. var $next = $e.find(_options.nextSelector).first();
  111. if (_options.autoTrigger && (_options.autoTriggerUntil === false || _options.autoTriggerUntil > 0)) {
  112. _nextWrap($next);
  113. if (_$body.height() <= _$window.height()) {
  114. _observe();
  115. }
  116. _$scroll.unbind('.jscroll').bind('scroll.jscroll', function() {
  117. return _observe();
  118. });
  119. if (_options.autoTriggerUntil > 0) {
  120. _options.autoTriggerUntil--;
  121. }
  122. } else {
  123. _$scroll.unbind('.jscroll');
  124. $next.bind('click.jscroll', function() {
  125. _nextWrap($next);
  126. _load();
  127. return false;
  128. });
  129. }
  130. }
  131. // Load the next set of content, if available
  132. function _load() {
  133. var $inner = $e.find('div.jscroll-inner').first(),
  134. data = $e.data('jscroll');
  135. data.waiting = true;
  136. $inner.append('<div class="jscroll-added" />')
  137. .children('.jscroll-added').last()
  138. .html('<div class="jscroll-loading">' + _options.loadingHtml + '</div>');
  139. return $e.animate({scrollTop: $inner.outerHeight()}, 0, function() {
  140. $inner.find('div.jscroll-added').last().load(data.nextHref, function(r, status, xhr) {
  141. if (status === 'error') {
  142. return _destroy();
  143. }
  144. var $next = $(this).find(_options.nextSelector).first();
  145. data.waiting = false;
  146. data.nextHref = $next.attr('href') ? $.trim($next.attr('href') + ' ' + _options.contentSelector) : false;
  147. $('.jscroll-next-parent', $e).remove(); // Remove the previous next link now that we have a new one
  148. _checkNextHref();
  149. if (_options.callback) {
  150. _options.callback.call(this);
  151. }
  152. _debug('dir', data);
  153. });
  154. });
  155. }
  156. // Safe console debug - http://klauzinski.com/javascript/safe-firebug-console-in-javascript
  157. function _debug(m) {
  158. if (_options.debug && typeof console === 'object' && (typeof m === 'object' || typeof console[m] === 'function')) {
  159. if (typeof m === 'object') {
  160. var args = [];
  161. for (var sMethod in m) {
  162. if (typeof console[sMethod] === 'function') {
  163. args = (m[sMethod].length) ? m[sMethod] : [m[sMethod]];
  164. console[sMethod].apply(console, args);
  165. } else {
  166. console.log.apply(console, args);
  167. }
  168. }
  169. } else {
  170. console[m].apply(console, Array.prototype.slice.call(arguments, 1));
  171. }
  172. }
  173. }
  174. // Expose API methods via the jQuery.jscroll namespace, e.g. $('sel').jscroll.method()
  175. $.extend($e.jscroll, {
  176. destroy: _destroy
  177. });
  178. return $e;
  179. };
  180. // Define the jscroll plugin method and loop
  181. $.fn.jscroll = function(m) {
  182. return this.each(function() {
  183. var $this = $(this),
  184. data = $this.data('jscroll');
  185. // Instantiate jScroll on this element if it hasn't been already
  186. if (data && data.initialized) return;
  187. var jscroll = new jScroll($this, m);
  188. });
  189. };
  190. })(jQuery);