/*! // Infinite Scroll jQuery plugin // copyright Paul Irish, licensed GPL & MIT // version 1.5.110408 // home and docs: http://www.infinite-scroll.com */ ; (function ($) { $.fn.infinitescroll = function (options, callback) { // console log wrapper. function debug() { if (opts.debug) { window.console && console.log.call(console, arguments) } } // grab each selector option and see if any fail. function areSelectorsValid(opts) { for (var key in opts) { if (key.indexOf && key.indexOf('Selector') > -1 && $(opts[key]).length === 0) { debug('Your ' + key + ' found no elements.'); return false; } return true; } } // find the number to increment in the path. function determinePath(path) { if (path.match(/^(.*?)\b2\b(.*?$)/)) { path = path.match(/^(.*?)\b2\b(.*?$)/).slice(1); // if there is any 2 in the url at all. } else if (path.match(/^(.*?)2(.*?$)/)) { // page= is used in django: // http://www.infinite-scroll.com/changelog/comment-page-1/#comment-127 if (path.match(/^(.*?page=)2(\/.*|$)/)) { path = path.match(/^(.*?page=)2(\/.*|$)/).slice(1); return path; } debug('Trying backup next selector parse technique. Treacherous waters here, matey.'); path = path.match(/^(.*?)2(.*?$)/).slice(1); } else { // page= is used in drupal too but second page is page=1 not page=2: // thx Jerod Fritz, vladikoff if (path.match(/^(.*?page=)1(\/.*|$)/)) { path = path.match(/^(.*?page=)1(\/.*|$)/).slice(1); return path; } if ($.isFunction(opts.pathParse)) { return [path]; } else { debug('Sorry, we couldn\'t parse your Next (Previous Posts) URL. Verify your the css selector points to the correct A tag. If you still get this error: yell, scream, and kindly ask for help at infinite-scroll.com.'); props.isInvalidPage = true; //prevent it from running on this page. } } return path; } // determine filtering nav for multiple instances function filterNav() { opts.isFiltered = true; return binder.trigger("error.infscr." + opts.infid, [302]); } // Calculate internal height (used for local scroll) function hiddenHeight(element) { var height = 0; $(element).children().each(function () { height = height + $(this).outerHeight(false); }); return height; } //Generate InstanceID based on random data (to give consistent but different ID's) function generateInstanceID(element) { var number = $(element).length + $(element).html().length + $(element).attr("class").length + $(element).attr("id").length; opts.infid = number; } function isNearBottom() { // distance remaining in the scroll // computed as: document height - distance already scroll - viewport height - buffer if (opts.container.nodeName == "HTML") { var pixelsFromWindowBottomToBottom = 0 + $(document).height() // have to do this bs because safari doesnt report a scrollTop on the html element - ($(opts.container).scrollTop() || $(opts.container.ownerDocument.body).scrollTop()) - $(window).height(); } else { var pixelsFromWindowBottomToBottom = 0 + hiddenHeight(opts.container) - $(opts.container).scrollTop() - $(opts.container).height(); } debug('math:', pixelsFromWindowBottomToBottom, opts.pixelsFromNavToBottom); // if distance remaining in the scroll (including buffer) is less than the orignal nav to bottom.... return (pixelsFromWindowBottomToBottom - opts.bufferPx < opts.pixelsFromNavToBottom); } function showDoneMsg() { props.loadingMsg .find('img') .hide() .parent() .find('div').html(opts.donetext).animate({ opacity: 1 }, 2000, function () { $(this).parent().fadeOut('normal'); }); // user provided callback when done opts.errorCallback(); } function infscrSetup() { if (opts.isDuringAjax || opts.isInvalidPage || opts.isDone || opts.isFiltered || opts.isPaused) return; if (!isNearBottom(opts, props)) return; $(document).trigger('retrieve.infscr.' + opts.infid); } // end of infscrSetup() function kickOffAjax() { // we dont want to fire the ajax multiple times opts.isDuringAjax = true; // show the loading message quickly // then hide the previous/next links after we're // sure the loading message was visible props.loadingMsg.appendTo(opts.loadMsgSelector).show(opts.loadingMsgRevealSpeed, function () { $(opts.navSelector).hide(); // increment the URL bit. e.g. /page/3/ opts.currPage++; debug('heading into ajax', path); // if we're dealing with a table we can't use DIVs box = $(opts.contentSelector).is('table') ? $('
') : $(''); frag = document.createDocumentFragment(); if ($.isFunction(opts.pathParse)) { // if we got a custom path parsing function, pass in our path guess and page iteration desturl = opts.pathParse(path.join('2'), opts.currPage); } else { desturl = path.join(opts.currPage); } box.load(desturl + ' ' + opts.itemSelector, null, loadCallback); }); } function loadCallback() { // if we've hit the last page.. if (opts.isDone) { showDoneMsg(); return false; } else { var children = box.children(); // if it didn't return anything if (children.length == 0 || children.hasClass('error404')) { // trigger a 404 error so we can quit. return infscrError([404]); } // use a documentFragment because it works when content is going into a table or UL while (box[0].firstChild) { frag.appendChild(box[0].firstChild); } $(opts.contentSelector)[0].appendChild(frag); // fadeout currently makes the 'd text ugly in IE6 props.loadingMsg.fadeOut('normal'); // smooth scroll to ease in the new content if (opts.animate) { var scrollTo = $(window).scrollTop() + $('#infscr-loading').height() + opts.extraScrollPx + 'px'; $('html,body').animate({ scrollTop: scrollTo }, 800, function () { opts.isDuringAjax = false; }); } // previously, we would pass in the new DOM element as context for the callback // however we're now using a documentfragment, which doesnt havent parents or children, // so the context is the contentContainer guy, and we pass in an array // of the elements collected as the first argument. callback.call($(opts.contentSelector)[0], children.get()); if (!opts.animate) opts.isDuringAjax = false; // once the call is done, we can allow it again. } } function initPause(pauseValue) { // if pauseValue is not 'pause' or 'resume', toggle it's value pauseValue = (pauseValue && (pauseValue == 'pause' || pauseValue == 'resume')) ? pauseValue : 'toggle'; switch (pauseValue) { case 'pause': opts.isPaused = true; break; case 'resume': opts.isPaused = false; break; case 'toggle': opts.isPaused = !opts.isPaused; break; } debug('Paused: ' + opts.isPaused); return false; } function infscrError(xhr) { if (!opts.isDone && xhr == 404) { // die if we're out of pages. debug('Page not found. Self-destructing...'); showDoneMsg(); opts.isDone = true; opts.currPage = 1; // if you need to go back to this instance binder.unbind('smartscroll.infscr.' + opts.infid); $(document).unbind('retrieve.infscr.' + opts.infid); } if (opts.isFiltered && xhr == 302) { // die if filtered. debug('Filtered. Going to next instance...'); opts.isDone = true; opts.currPage = 1; // if you need to go back to this instance opts.isPaused = false; binder.unbind('smartscroll.infscr.' + opts.infid, infscrSetup) .unbind('pause.infscr.' + opts.infid) .unbind('filter.infscr.' + opts.infid) .unbind('error.infscr.' + opts.infid); $(document).unbind('retrieve.infscr.' + opts.infid, kickOffAjax); } } /* * smartscroll: debounced scroll event for jQuery * * https://github.com/lukeshumard/smartscroll * Based on smartresize by @louis_remi: https://github.com/lrbabe/jquery.smartresize.js * * Copyright 2011 Louis-Remi & Luke Shumard * Licensed under the MIT license. * */ var event = $.event, scrollTimeout; event.special.smartscroll = { setup: function() { $(this).bind( "scroll", event.special.smartscroll.handler ); }, teardown: function() { $(this).unbind( "scroll", event.special.smartscroll.handler ); }, handler: function( event, execAsap ) { // Save the context var context = this, args = arguments; // set correct event type event.type = "smartscroll"; if (scrollTimeout) { clearTimeout(scrollTimeout); } scrollTimeout = setTimeout(function() { jQuery.event.handle.apply( context, args ); }, execAsap === "execAsap"? 0 : 100); } }; $.fn.smartscroll = function( fn ) { return fn ? this.bind( "smartscroll", fn ) : this.trigger( "smartscroll", ["execAsap"] ); }; // lets get started. $.browser.ie6 = $.browser.msie && $.browser.version < 7; var opts = $.extend({}, $.infinitescroll.defaults, options), props = $.infinitescroll, // shorthand box, frag, desturl, thisPause, errorStatus; callback = callback || function () { }; if (!areSelectorsValid(opts)) { return false; } opts.container = opts.container || document.documentElement; // contentSelector we'll use for our .load() opts.contentSelector = opts.contentSelector || this; // Generate unique instance ID if (opts.infid == 0) generateInstanceID(opts.contentSelector); // loadMsgSelector - if we want to place the load message in a specific selector, defaulted to the contentSelector opts.loadMsgSelector = opts.loadMsgSelector || opts.contentSelector; // get the relative URL - everything past the domain name. var relurl = /(.*?\/\/).*?(\/.*)/, path = $(opts.nextSelector).attr('href'); if (!path) { debug('Navigation selector not found'); return; } // set the path to be a relative URL from root. path = determinePath(path); // define loading msg props.loadingMsg = $('