/*! * infinite ajax scroll, a jquery plugin * version v0.1.5 * http://webcreate.nl/ * * copyright (c) 2011-2012 jeroen fiege * licensed under the mit license: * http://webcreate.nl/license */ (function($) { $.ias = function(options) { // setup var opts = $.extend({}, $.ias.defaults, options); var util = new $.ias.util(); // utilities module var paging = new $.ias.paging(); // paging module var hist = (opts.history ? new $.ias.history() : false); // history module var _self = this; // initialize init(); /** * initialize * * - tracks scrolling through pages * - remembers current page with the history module * - setup scroll event and hides pagination element * - loads and scrolls to previous page when we have something in our history * * @return self */ function init() { // track page number changes paging.onchangepage(function(pagenum, scrolloffset, pageurl) { if (hist) hist.setpage(pagenum, pageurl); // call onpagechange event opts.onpagechange.call(this, pagenum, pageurl, scrolloffset); }); // setup scroll and hide pagination reset(); // load and scroll to previous page if (hist && hist.havepage()) { stop_scroll(); pagenum = hist.getpage(); util.forcescrolltop(function() { if (pagenum > 1) { paginatetopage(pagenum); curtreshold = get_scroll_treshold(true); $("html,body").scrolltop(curtreshold); } else { reset(); } }); } return _self; } /** * reset scrolling and hide pagination links * * @return void */ function reset() { hide_pagination(); $(window).scroll(scroll_handler); } /** * scroll event handler * * @return void */ function scroll_handler() { scrtop = $(window).scrolltop(); wndheight = $(window).height(); curscroffset = scrtop + wndheight; if (curscroffset >= get_scroll_treshold()) { paginate(curscroffset); } } /** * cancel scrolling * * @return void */ function stop_scroll(callback) { $(window).unbind('scroll', scroll_handler); callback&&callback(); } /** * hide pagination * * @return void */ function hide_pagination() { $(opts.pagination).hide(); } /** * get scroll treshold based on the last item element * * @param boolean pure indicates if the tresholdmargin should be applied * @return integer treshold */ function get_scroll_treshold(pure) { el = $(opts.container).find(opts.item).last(); if (el.size() == 0) return 0; treshold = el.offset().top + el.height(); if (!pure) treshold += opts.tresholdmargin; return treshold; } /** * load the items from the next page. * * @param int curscroffset current scroll offset * @param function oncompletehandler callback function * @return void */ function paginate(curscroffset, oncompletehandler) { var $next if(opts.cur){ $next = $(opts.cur).next(opts.next) if(!$next.attr('href')){ $next = $next.find('a'); } }else{ $next = $(opts.next).next('a'); } if(isnan(number($next.text()))) return stop_scroll(); urlnextpage = $next.attr("href"); if (!urlnextpage) return stop_scroll(); paging.pushpages(curscroffset, urlnextpage); stop_scroll(); show_loader(); loaditems(urlnextpage, function(data, items) { // call the onloaditems callback result = opts.onloaditems.call(this, items); if (result !== false) { $(items).hide(); // at first, hide it so we can fade it in later // insert them after the last item with a nice fadein effect curlastitem = $(opts.container).find(opts.item).last(); curlastitem.after(items); $(items).fadein(); } // update pagination $(opts.pagination).replacewith($(opts.pagination, data)); remove_loader(); reset(); // call the onrendercomplete callback opts.onrendercomplete.call(this, items); if (oncompletehandler) oncompletehandler.call(this); var $nextn if(opts.cur){ $nextn = $(opts.cur).next(opts.next) if(!$nextn.attr('href')){ $nextn = $nextn.find('a'); } }else{ $nextn = $(opts.next).next('a'); } if(opts['end']&&(isnan(number($nextn.text()))||!$nextn.attr("href") )){ opts['end'](); } }); } /** * loads items from certain url, triggers * oncomplete handler when finished * * @param string the url to load * @param function the callback function * @return void */ function loaditems(url, oncompletehandler) { var items = []; $.get(url, null, function(data) { // walk through the items on the next page // and add them to the items array var t = /]*>([\s\s]*)<\/body>/.exec(data); if(t&&t.length===2){ t = t[1]; t = '
'+ t +'
'; }else{ t = '
'; } data = $(t); $(opts.container,data).find(opts.item).each(function() { items.push(this); }); if (oncompletehandler) oncompletehandler.call(this, data, items); }, "html"); } /** * paginate to a certain page number. * * - keeps paginating till the pagenum is reached * * @return void */ function paginatetopage(pagenum) { curtreshold = get_scroll_treshold(true); if (curtreshold > 0) { paginate(curtreshold, function() { stop_scroll(); if ((paging.getcurpagenum(curtreshold) + 1) < pagenum) { paginatetopage(pagenum); $("html,body").animate({"scrolltop": curtreshold}, 400, "swing"); } else { $("html,body").animate({"scrolltop": curtreshold}, 1000, "swing"); reset(); } }); } } /** * return the active loader of creates a new loader * * @return object loader jquery object */ function get_loader() { loader = $('.ias_loader'); if (loader.size() == 0) { loader = $("
"+opts.loader+"
"); $('body').append(loader); loader.hide(); } return loader; } /** * inserts the loader and does a fadein. * * @return void */ function show_loader(selector) { loader = get_loader(); window['loadertimer']&&window.cleartimeout(window['loadertimer']); loader.show(); } /** * removes the loader. * * return void */ function remove_loader() { loader = get_loader(); window['loadertimer'] = settimeout(function() { loader.hide(); },500); } }; /** * debug to console when available * * @param object $obj * @return */ function debug($obj) { if (window.console && window.console.log) window.console.log($obj); }; // plugin defaults $.ias.defaults = { container: '#container', item: '.item', pagination: '#pagination', next: '.next', loader: '', tresholdmargin: 0, history : true, onpagechange: function() {}, onloaditems: function() {}, onrendercomplete: function() {} }; // utility module $.ias.util = function() { // setup var wndisloaded = false; var forcescrolltopiscompleted = false; var self = this; // initialize init(); /** * initialize * * @return void */ function init() { $(window).load(function () { wndisloaded = true; }); } /** * force browsers to scroll to top. * * - when you hit back in you browser, it automatically scrolls * back to the last position. there is no way to stop this * in a nice way, so this function does it the hard way. * * @param function oncomplete callback function * @return void */ this.forcescrolltop = function(oncompletehandler) { $("html,body").scrolltop(0); if (!forcescrolltopiscompleted) { if (!wndisloaded) { settimeout(function() {self.forcescrolltop(oncompletehandler); }, 1); } else { oncompletehandler.call(); forcescrolltopiscompleted = true; } } }; }; // paging module $.ias.paging = function() { // setup var pagebreaks = [[0, document.location.tostring()]]; var changepagehandler = function() {}; var lastpagenum = 1; // initialize init(); /** * initialize * * @return void */ function init() { $(window).scroll(scroll_handler); } /** * scroll handler * * - triggers changepage event * * @return void */ function scroll_handler() { scrtop = $(window).scrolltop(); wndheight = $(window).height(); curscroffset = scrtop + wndheight; curpagenum = getcurpagenum(curscroffset); curpagebreak = getcurpagebreak(curscroffset); if (lastpagenum != curpagenum) { changepagehandler.call(this, curpagenum, curpagebreak[0]/*scroffset*/, curpagebreak[1]/*urlpage*/); // @todo fix for window height } lastpagenum = curpagenum; } /** * returns current page number based on scroll offset * * @param int scroll offset * @return int current page number */ function getcurpagenum(scrolloffset) { for(i=(pagebreaks.length-1);i>0;i--) { if (scrolloffset > pagebreaks[i][0]) { return i + 1; } } return 1; } /** * public function for getcurpagenum * * @return int current page number */ this.getcurpagenum = function(scrolloffset) { return getcurpagenum(scrolloffset); }; /** * returns current pagebreak information based on scroll offset * * @param int scroll offset * @return array pagebreak information */ function getcurpagebreak(scrolloffset) { for(i=(pagebreaks.length-1);i>=0;i--) { if (scrolloffset > pagebreaks[i][0]) { return pagebreaks[i]; } } return null; }; /** * sets onchangepage event handler * * @param function event handler * @return void */ this.onchangepage = function(fn) { changepagehandler = fn; }; /** * pushes the pages tracker * * @param int scroll offset for the new page * @return void */ this.pushpages = function(scrolloffset, urlnextpage) { pagebreaks.push([scrolloffset, urlnextpage]); }; }; // history module $.ias.history = function() { // setup var ispushed = false; var ishtml5 = false; // initialize init(); /** * initialize * * @return void */ function init() { ishtml5 = !!(window.history && history.pushstate && history.replacestate); ishtml5 = false; // html5 functions disabled due to problems in chrome }; /** * sets page to history * * @return void; */ this.setpage = function(pagenum, pageurl) { this.updatestate({page : pagenum}, "", pageurl); }; /** * checks if we have a page set in the history * * @return bool returns true when we have a previous page, false otherwise */ this.havepage = function() { return (this.getstate() != false); }; /** * gets the previous page from history * * @return int page number of previous page */ this.getpage = function() { if (this.havepage()) { stateobj = this.getstate(); return stateobj.page; } return 1; }; /** * returns current state * * @return object stateobj */ this.getstate = function() { if (ishtml5) { stateobj = history.state; if (stateobj && stateobj.ias) return stateobj.ias; } else { havestate = (window.location.hash.substring(0, 7) == "#/page/"); if (havestate) { pagenum = parseint(window.location.hash.replace("#/page/", "")); return { page : pagenum }; } } return false; }; /** * pushes state when not pushed already, otherwise * replaces the state. * * @param obj stateobj * @param string title * @param string url * @return void */ this.updatestate = function(stateobj, title, url) { if (ispushed) { this.replacestate(stateobj, title, url); } else { this.pushstate(stateobj, title, url); } }; /** * pushes state to history. * * @param obj stateobj * @param string title * @param string url * @return void */ this.pushstate = function(stateobj, title, url) { if (ishtml5) { history.pushstate({ ias : stateobj }, title, url); } else { hash = (stateobj.page > 0 ? "#/page/" + stateobj.page : ""); window.location.hash = hash; } ispushed = true; }; /** * replaces current history state. * * @param obj stateobj * @param string title * @param string url * @return void */ this.replacestate = function(stateobj, title, url) { if (ishtml5) { history.replacestate({ ias : stateobj }, title, url); } else { this.pushstate(stateobj, title, url); } }; }; })(jquery);