/*!
* 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);