1/** 2 * Version 2.0 3 * 4 * Written by Micael Sjölund, ESN (http://www.esn.me) 5 * LICENCE: Do whatever you want with the code. I don't take any responsibility for how you use it. 6 * 7 * Creates a growing container that automatically fills its content via ajax requests, when the user scrolls to the 8 * bottom of the container. More info: http://pushingtheweb.com/2010/09/endless-scroller-jquery-plugin/ 9 * 10 * Requires jStorage (), if the useCache option is set to true. WARNING: Somewhat experimental. See below for more info. 11 * 12 * 13 * Usage: 14 * .autobrowse(options) 15 * options Map of property-value options which controls plugin behaviour. 16 * .autobrowse(command) 17 * command String command that can be sent to the plugin. 18 * 19 * 20 * * COMMANDS 21 * * "flush" Clears the plugin cache 22 * 23 * 24 * * OPTIONS 25 * * url Callback to render url from offset argument. 26 * Example: function (offset) { return "http://mydomain.com/OFFSET/20".replace(/OFFSET/, offset) } 27 * * template Callback to render markup from json response. 28 * Example: function (response) { var markup=''; for (var i=0; i<response.length; i++) { markup+='<img src="'+response[i]+'" />' } return markup; } 29 * * itemsReturned Callback that is run on ajax json response to determine how many items was returned 30 * 31 * * OPTIONAL OPTIONS 32 * * loader Element, jQuery object or markup to indicate to the user that the site is waiting for more items. 33 * * offset Item offset for first ajax call to url, if you have already rendered items on the page 34 * * max Maximum number of items to be retrieved by the plugin (can also be used to tell the plugin how many 35 * items there are in total on the server, so that no unneccesary requests are made. 36 * * complete Callback that is run when the element has been updated with new content. This is run before the 37 * response is stored (if using useCache), making it is possible to manipulate the response here before 38 * it is stored. 39 * * sensitivity Number of pixels before end of element that the plugin will start fetching more items. 40 * * postData If you want to do a POST request instead of a GET request, supply this argument, either as a 41 * function or an object. If not set, a GET request will be made. 42 * * useCache If true, the plugin will use browser storage to keep the state between page loads. If the user 43 * clicks away from the page and then goes back, all items fetched will be rendered again, and the 44 * user will see the same view as when he left the page. Requires http://www.jstorage.info/. 45 * WARNING: This doesn't work with original jStorage. A modified version is 46 * available on http://github.com/msjolund/jquery-esn-autobrowse. jStorage also 47 * requires jquery-json: http://code.google.com/p/jquery-json/. Default: false 48 * * expiration How long to keep cache, in hours. Default: 24 49 * * stopFunction a function that will return true if it is necessary to stop autoscrolling 50 * * onError a function that will be executed on error (HTTP response 500, etc.) 51 * * timeout 52 */ 53(function( $ ){ 54jQuery.fn.autobrowse = function (options) 55{ 56 var defaults = { 57 url: function (offset) { return "/"; }, 58 template: function (response) { return ""; }, 59 offset: 0, 60 max: null, 61 loader: '<div class="loader"></div>', 62 itemsReturned: function (response) { return response.length }, 63 complete: function (response) {}, 64 finished: function (response) {}, 65 useCache: false, 66 expiration: 24, 67 sensitivity: 0, 68 postData: null, 69 stopFunction: function () {}, 70 onError: function () {}, 71 timeout: 500 72 }; 73 74 // flush cache command 75 if (typeof options == "string" && options == "flush") 76 { 77 jQuery.jStorage.flush(); 78 return this; 79 } 80 81 options = jQuery.extend(defaults, options); 82 83 // allow non-dynamic url 84 if (typeof options.url == "string") 85 { 86 var url = options.url; 87 options.url = function (offset) { return url; } 88 }else if(typeof options.url !== "function"){ 89 options.url = false;
90 } 91 92 var getDataLength = function (data) 93 { 94 var length = 0; 95 for (var i = 0; i < data.length; i++) 96 length += options.itemsReturned(data[i]); 97 return length; 98 }; 99 100 return this.each( function () 101 { 102 var localData, obj = jQuery(this); 103 var currentOffset = options.offset; 104 var loading = false; 105 var scrollTopUpdateTimer = null; 106 var stopping = false; 107 108 var _stopPlugin = function (handler) 109 { 110 jQuery(window).unbind("scroll", handler); 111 options.finished.call(obj); 112 }; 113 114 var scrollCallback = function () 115 { 116 var scrollTop = jQuery(window).scrollTop(); 117 var objBottom = obj.height() + obj.offset().top; 118 var winHeight = window.innerHeight ? window.innerHeight : $(window).height(); 119 var winBtmPos = scrollTop + winHeight; 120 if (scrollTopUpdateTimer){ 121 clearTimeout(scrollTopUpdateTimer); 122 } 123 if (options.useCache) { 124 scrollTopUpdateTimer = setTimeout(function () { jQuery.jStorage.set("autobrowseScrollTop", scrollTop); }, 200); 125 } 126 if (objBottom < winBtmPos + options.sensitivity && !loading) 127 { 128 loading = true; 129 if(typeof options.loader ==='string'){ 130 var loader = jQuery(options.loader); 131 loader.appendTo(obj); 132 }else{ 133 var loader = options.loader; 134 obj.append(loader.append()); 135 } 136 137 var ajaxCallback = function (response) { 138 if (options.itemsReturned(response) > 0) 139 { 140 // Create the markup and append it to the container 141 try { var markup = options.template(response); } 142 catch (e) { console.error(e) } // ignore for now 143 var newElements = jQuery(markup); 144 newElements.appendTo(obj); 145 146 // Call user onComplete callback 147 options.complete.call(obj, response, newElements); 148 149 // Store in local cache if option is set, and everything fetched fitted into the storage 150 if (options.useCache && getDataLength(localData) + options.offset == currentOffset) 151 { 152 localData.push(response); 153 if (!jQuery.jStorage.set("autobrowseStorage", localData)) 154 // Storage failed, remove last pushed response 155 localData.pop(); 156 } 157 158 // Update offsets 159 currentOffset += options.itemsReturned(response); 160 if (options.useCache) 161 { 162 jQuery.jStorage.set("autobrowseOffset", currentOffset); 163 } 164 } 165 166 loader.remove(); 167 // Check if these were the last items to fetch from the server, if so, stop listening 168 if (options.itemsReturned(response) == 0 || (options.max != null && currentOffset >= options.max) || options.stopFunction(response) === true) 169 { 170 _stopPlugin(scrollCallback); 171 stopping = true; 172 } 173 if(options.url === false){ 174 window.setTimeout(function(){ 175 loading = false; 176 if (!stopping) { 177 scrollCallback(); 178 } 179 },options.timeout); 180 }else{ 181 loading = false; 182 if (!stopping) { 183 scrollCallback(); 184 } 185 } 186 }; 187 188 if(options.url !== false){ 189 if (options.postData) 190 { 191 var data = null; 192 if (typeof options.postData == "function") 193 { 194 data = options.postData(); 195 } 196 else 197 { 198 data = options.postData; 199 } 200 201 jQuery.post(options.url(currentOffset), data, ajaxCallback, "json").error(options.onError); 202 } 203 else 204 { 205 jQuery.getJSON(options.url(currentOffset), ajaxCallback).error(options.onError); 206 } 207 }else{ 208 ajaxCallback(); 209 } 210 } 211 }; 212 213 var _startPlugin = function() 214 { 215 if (options.useCache) 216 var autobrowseScrollTop = jQuery.jStorage.get("autobrowseScrollTop"); 217 if (autobrowseScrollTop) 218 jQuery(window).scrollTop(autobrowseScrollTop); 219 jQuery(window).scroll(scrollCallback); 220 scrollCallback(); 221 }; 222 223 224 if (options.useCache) 225 { 226 if (jQuery.jStorage.get("autobrowseStorageKey") != options.url(0,0)) 227 { 228 // flush cache if wrong page 229 jQuery.jStorage.flush(); 230 } 231 else if (jQuery.jStorage.get("autobrowseExpiration") && jQuery.jStorage.get("autobrowseExpiration") < (new Date()).getTime()) 232 { 233 // flush cache if it's expired 234 jQuery.jStorage.flush(); 235 } 236 localData= jQuery.jStorage.get("autobrowseStorage"); 237 if (localData) 238 { 239 // for each stored ajax response 240 for (var i = 0; i < localData.length; i++) 241 { 242 var markup = options.template(localData[i]); 243 jQuery(markup).appendTo(obj); 244 currentOffset += options.itemsReturned(localData[i]); 245 options.complete.call(obj, localData[i]); 246 } 247 _startPlugin(); 248 } 249 else 250 { 251 localData = []; 252 jQuery.jStorage.get("autobrowseStorageKey") 253 jQuery.jStorage.set("autobrowseExpiration", (new Date()).getTime()+options.expiration*60*60*1000); 254 jQuery.jStorage.set("autobrowseOffset", currentOffset); 255 jQuery.jStorage.set("autobrowseStorageKey", options.url(0, 0)); 256 jQuery.jStorage.set("autobrowseStorage", localData); 257 jQuery.jStorage.set("autobrowseScrollTop", 0); 258 _startPlugin(); 259 } 260 } 261 262 else 263 { 264 _startPlugin(); 265 } 266 }); 267}; 268})( jQuery );
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.