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