-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathjquery.esn.autobrowse.js
245 lines (227 loc) · 9.96 KB
/
jquery.esn.autobrowse.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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/**
* Version 2.0
*
* Written by Micael Sjölund, ESN (http://www.esn.me)
*
* Creates a growing container that automatically fills its content via ajax requests, when the user scrolls to the
* bottom of the container. More info: http://pushingtheweb.com/2010/09/endless-scroller-jquery-plugin/
*
* Requires jStorage (), if the useCache option is set to true. WARNING: Somewhat experimental. See below for more info.
*
*
* Usage:
* .autobrowse(options)
* options Map of property-value options which controls plugin behaviour.
* .autobrowse(command)
* command String command that can be sent to the plugin.
*
*
* * COMMANDS
* * "flush" Clears the plugin cache
*
*
* * OPTIONS
* * url Callback to render url from offset argument.
* Example: function (offset) { return "http://mydomain.com/OFFSET/20".replace(/OFFSET/, offset) }
* * template Callback to render markup from json response.
* Example: function (response) { var markup=''; for (var i=0; i<response.length; i++) { markup+='<img src="'+response[i]+'" />' } return markup; }
* * itemsReturned Callback that is run on ajax json response to determine how many items was returned
*
* * OPTIONAL OPTIONS
* * loader Element, jQuery object or markup to indicate to the user that the site is waiting for more items.
* * offset Item offset for first ajax call to url, if you have already rendered items on the page
* * max Maximum number of items to be retrieved by the plugin (can also be used to tell the plugin how many
* items there are in total on the server, so that no unneccesary requests are made.
* * complete Callback that is run when the element has been updated with new content. This is run before the
* response is stored (if using useCache), making it is possible to manipulate the response here before
* it is stored.
* * sensitivity Number of pixels before end of element that the plugin will start fetching more items.
* * postData If you want to do a POST request instead of a GET request, supply this argument, either as a
* function or an object. If not set, a GET request will be made.
* * useCache If true, the plugin will use browser storage to keep the state between page loads. If the user
* clicks away from the page and then goes back, all items fetched will be rendered again, and the
* user will see the same view as when he left the page. Requires http://www.jstorage.info/.
* WARNING: This doesn't work with original jStorage. A modified version is
* available on http://github.com/msjolund/jquery-esn-autobrowse. jStorage also
* requires jquery-json: http://code.google.com/p/jquery-json/. Default: false
* * expiration How long to keep cache, in hours. Default: 24
* * stopFunction a function that will return true if it is necessary to stop autoscrolling
* * onError a function that will be executed on error (HTTP response 500, etc.)
*
*/
(function( $ ){
jQuery.fn.autobrowse = function (options)
{
var defaults = {
url: function (offset) { return "/"; },
template: function (response) { return ""; },
offset: 0,
max: null,
loader: '<div class="loader"></div>',
itemsReturned: function (response) { return response.length },
complete: function (response) {},
finished: function (response) {},
useCache: false,
expiration: 24,
sensitivity: 0,
postData: null,
stopFunction: function () {},
onError: function () {}
};
// flush cache command
if (typeof options == "string" && options == "flush")
{
jQuery.jStorage.flush();
return this;
}
options = jQuery.extend(defaults, options);
// allow non-dynamic url
if (typeof options.url == "string")
{
var url = options.url;
options.url = function (offset) { return url; }
}
var getDataLength = function (data)
{
var length = 0;
for (var i = 0; i < data.length; i++)
length += options.itemsReturned(data[i]);
return length;
};
return this.each( function ()
{
var localData, obj = jQuery(this);
var currentOffset = options.offset;
var loading = false;
var scrollTopUpdateTimer = null;
var stopping = false;
var _stopPlugin = function (handler)
{
jQuery(window).unbind("scroll", handler);
options.finished.call(obj);
};
var scrollCallback = function ()
{
var scrollTop = jQuery(window).scrollTop();
var objBottom = obj.height() + obj.offset().top;
var winHeight = window.innerHeight ? window.innerHeight : $(window).height();
var winBtmPos = scrollTop + winHeight;
if (scrollTopUpdateTimer)
clearTimeout(scrollTopUpdateTimer);
if (options.useCache) {
scrollTopUpdateTimer = setTimeout(function () { jQuery.jStorage.set("autobrowseScrollTop", scrollTop); }, 200);
}
if (objBottom < winBtmPos + options.sensitivity && !loading)
{
var loader = jQuery(options.loader);
loader.appendTo(obj);
loading = true;
var ajaxCallback = function (response) {
if (options.itemsReturned(response) > 0)
{
// Create the markup and append it to the container
try { var markup = options.template(response); }
catch (e) { console.error(e) } // ignore for now
var newElements = jQuery(markup);
newElements.appendTo(obj);
// Call user onComplete callback
options.complete.call(obj, response, newElements);
// Store in local cache if option is set, and everything fetched fitted into the storage
if (options.useCache && getDataLength(localData) + options.offset == currentOffset)
{
localData.push(response);
if (!jQuery.jStorage.set("autobrowseStorage", localData))
// Storage failed, remove last pushed response
localData.pop();
}
// Update offsets
currentOffset += options.itemsReturned(response);
if (options.useCache)
{
jQuery.jStorage.set("autobrowseOffset", currentOffset);
}
}
loader.remove();
// Check if these were the last items to fetch from the server, if so, stop listening
if (options.itemsReturned(response) == 0 || (options.max != null && currentOffset >= options.max) || options.stopFunction(response) === true)
{
_stopPlugin(scrollCallback)
stopping = true;
}
loading = false;
if (!stopping) {
scrollCallback();
}
};
if (options.postData)
{
var data = null;
if (typeof options.postData == "function")
{
data = options.postData();
}
else
{
data = options.postData;
}
jQuery.post(options.url(currentOffset), data, ajaxCallback, "json").error(options.onError);
}
else
{
jQuery.getJSON(options.url(currentOffset), ajaxCallback).error(options.onError);
}
}
};
var _startPlugin = function()
{
if (options.useCache)
var autobrowseScrollTop = jQuery.jStorage.get("autobrowseScrollTop");
if (autobrowseScrollTop)
jQuery(window).scrollTop(autobrowseScrollTop);
jQuery(window).scroll(scrollCallback);
scrollCallback();
};
if (options.useCache)
{
if (jQuery.jStorage.get("autobrowseStorageKey") != options.url(0,0))
{
// flush cache if wrong page
jQuery.jStorage.flush();
}
else if (jQuery.jStorage.get("autobrowseExpiration") && jQuery.jStorage.get("autobrowseExpiration") < (new Date()).getTime())
{
// flush cache if it's expired
jQuery.jStorage.flush();
}
localData= jQuery.jStorage.get("autobrowseStorage");
if (localData)
{
// for each stored ajax response
for (var i = 0; i < localData.length; i++)
{
var markup = options.template(localData[i]);
jQuery(markup).appendTo(obj);
currentOffset += options.itemsReturned(localData[i]);
options.complete.call(obj, localData[i]);
}
_startPlugin();
}
else
{
localData = [];
jQuery.jStorage.get("autobrowseStorageKey")
jQuery.jStorage.set("autobrowseExpiration", (new Date()).getTime()+options.expiration*60*60*1000);
jQuery.jStorage.set("autobrowseOffset", currentOffset);
jQuery.jStorage.set("autobrowseStorageKey", options.url(0, 0));
jQuery.jStorage.set("autobrowseStorage", localData);
jQuery.jStorage.set("autobrowseScrollTop", 0);
_startPlugin();
}
}
else
{
_startPlugin();
}
});
};
})( jQuery );