diff options
Diffstat (limited to 'plugins/jetpack/modules/infinite-scroll/infinity.js')
-rw-r--r-- | plugins/jetpack/modules/infinite-scroll/infinity.js | 750 |
1 files changed, 0 insertions, 750 deletions
diff --git a/plugins/jetpack/modules/infinite-scroll/infinity.js b/plugins/jetpack/modules/infinite-scroll/infinity.js deleted file mode 100644 index e1a508e4..00000000 --- a/plugins/jetpack/modules/infinite-scroll/infinity.js +++ /dev/null @@ -1,750 +0,0 @@ -(function($){ // Open closure -// Local vars -var Scroller, ajaxurl, stats, type, text, totop; - -// IE requires special handling -var isIE = ( -1 != navigator.userAgent.search( 'MSIE' ) ); -if ( isIE ) { - var IEVersion = navigator.userAgent.match(/MSIE\s?(\d+)\.?\d*;/); - var IEVersion = parseInt( IEVersion[1] ); -} - -// HTTP ajaxurl when site is HTTPS causes Access-Control-Allow-Origin failure in Desktop and iOS Safari -if ( "https:" == document.location.protocol ) { - infiniteScroll.settings.ajaxurl = infiniteScroll.settings.ajaxurl.replace( "http://", "https://" ); -} - -/** - * Loads new posts when users scroll near the bottom of the page. - */ -Scroller = function( settings ) { - var self = this; - - // Initialize our variables - this.id = settings.id; - this.body = $( document.body ); - this.window = $( window ); - this.element = $( '#' + settings.id ); - this.wrapperClass = settings.wrapper_class; - this.ready = true; - this.disabled = false; - this.page = 1; - this.offset = settings.offset; - this.currentday = settings.currentday; - this.order = settings.order; - this.throttle = false; - this.handle = '<div id="infinite-handle"><span><button>' + text.replace( '\\', '' ) + '</button></span></div>'; - this.click_handle = settings.click_handle; - this.google_analytics = settings.google_analytics; - this.history = settings.history; - this.origURL = window.location.href; - this.pageCache = {}; - - // Footer settings - this.footer = $( '#infinite-footer' ); - this.footer.wrap = settings.footer; - - // Core's native MediaElement.js implementation needs special handling - this.wpMediaelement = null; - - // We have two type of infinite scroll - // cases 'scroll' and 'click' - - if ( type == 'scroll' ) { - // Bind refresh to the scroll event - // Throttle to check for such case every 300ms - - // On event the case becomes a fact - this.window.bind( 'scroll.infinity', function() { - this.throttle = true; - }); - - // Go back top method - self.gotop(); - - setInterval( function() { - if ( this.throttle ) { - // Once the case is the case, the action occurs and the fact is no more - this.throttle = false; - // Reveal or hide footer - self.thefooter(); - // Fire the refresh - self.refresh(); - self.determineURL(); // determine the url - } - }, 250 ); - - // Ensure that enough posts are loaded to fill the initial viewport, to compensate for short posts and large displays. - self.ensureFilledViewport(); - this.body.bind( 'post-load', { self: self }, self.checkViewportOnLoad ); - } else if ( type == 'click' ) { - if ( this.click_handle ) { - this.element.append( this.handle ); - } - - this.body.delegate( '#infinite-handle', 'click.infinity', function() { - // Handle the handle - if ( self.click_handle ) { - $( '#infinite-handle' ).remove(); - } - - // Fire the refresh - self.refresh(); - }); - } - - // Initialize any Core audio or video players loaded via IS - this.body.bind( 'post-load', { self: self }, self.initializeMejs ); -}; - -/** - * Check whether we should fetch any additional posts. - */ -Scroller.prototype.check = function() { - var container = this.element.offset(); - - // If the container can't be found, stop otherwise errors result - if ( 'object' !== typeof container ) { - return false; - } - - var bottom = this.window.scrollTop() + this.window.height(), - threshold = container.top + this.element.outerHeight(false) - (this.window.height() * 2); - - return bottom > threshold; -}; - -/** - * Renders the results from a successful response. - */ -Scroller.prototype.render = function( response ) { - this.body.addClass( 'infinity-success' ); - - // Check if we can wrap the html - this.element.append( response.html ); - this.body.trigger( 'post-load', response ); - this.ready = true; -}; - -/** - * Returns the object used to query for new posts. - */ -Scroller.prototype.query = function() { - return { - page : this.page + this.offset, // Load the next page. - currentday : this.currentday, - order : this.order, - scripts : window.infiniteScroll.settings.scripts, - styles : window.infiniteScroll.settings.styles, - query_args : window.infiniteScroll.settings.query_args, - query_before : window.infiniteScroll.settings.query_before, - last_post_date: window.infiniteScroll.settings.last_post_date - }; -}; - -/** - * Scroll back to top. - */ -Scroller.prototype.gotop = function() { - var blog = $( '#infinity-blog-title' ); - - blog.attr( 'title', totop ); - - // Scroll to top on blog title - blog.bind( 'click', function( e ) { - $( 'html, body' ).animate( { scrollTop: 0 }, 'fast' ); - e.preventDefault(); - }); -}; - - -/** - * The infinite footer. - */ -Scroller.prototype.thefooter = function() { - var self = this, - width; - - // Check if we have an id for the page wrapper - if ( $.type( this.footer.wrap ) === "string" ) { - width = $( 'body #' + this.footer.wrap ).outerWidth( false ); - - // Make the footer match the width of the page - if ( width > 479 ) - this.footer.find( '.container' ).css( 'width', width ); - } - - // Reveal footer - if ( this.window.scrollTop() >= 350 ) - self.footer.animate( { 'bottom': 0 }, 'fast' ); - else if ( this.window.scrollTop() < 350 ) - self.footer.animate( { 'bottom': '-50px' }, 'fast' ); -}; - - -/** - * Controls the flow of the refresh. Don't mess. - */ -Scroller.prototype.refresh = function() { - var self = this, - query, jqxhr, load, loader, color, customized; - - // If we're disabled, ready, or don't pass the check, bail. - if ( this.disabled || ! this.ready || ! this.check() ) - return; - - // Let's get going -- set ready to false to prevent - // multiple refreshes from occurring at once. - this.ready = false; - - // Create a loader element to show it's working. - if ( this.click_handle ) { - loader = '<span class="infinite-loader"></span>'; - this.element.append( loader ); - - loader = this.element.find( '.infinite-loader' ); - color = loader.css( 'color' ); - - try { - loader.spin( 'medium-left', color ); - } catch ( error ) { } - } - - // Generate our query vars. - query = $.extend({ - action: 'infinite_scroll' - }, this.query() ); - - // Inject Customizer state. - if ( 'undefined' !== typeof wp && wp.customize && wp.customize.settings.theme ) { - customized = {}; - query.wp_customize = 'on'; - query.theme = wp.customize.settings.theme.stylesheet; - wp.customize.each( function( setting ) { - if ( setting._dirty ) { - customized[ setting.id ] = setting(); - } - } ); - query.customized = JSON.stringify( customized ); - query.nonce = wp.customize.settings.nonce.preview; - } - - // Fire the ajax request. - jqxhr = $.post( infiniteScroll.settings.ajaxurl, query ); - - // Allow refreshes to occur again if an error is triggered. - jqxhr.fail( function() { - if ( self.click_handle ) { - loader.hide(); - } - - self.ready = true; - }); - - // Success handler - jqxhr.done( function( response ) { - // On success, let's hide the loader circle. - if ( self.click_handle ) { - loader.hide(); - } - - // Check for and parse our response. - if ( ! response || ! response.type ) { - return; - } - - // If we've succeeded... - if ( response.type == 'success' ) { - // If additional scripts are required by the incoming set of posts, parse them - if ( response.scripts ) { - $( response.scripts ).each( function() { - var elementToAppendTo = this.footer ? 'body' : 'head'; - - // Add script handle to list of those already parsed - window.infiniteScroll.settings.scripts.push( this.handle ); - - // Output extra data, if present - if ( this.extra_data ) { - var data = document.createElement('script'), - dataContent = document.createTextNode( "//<![CDATA[ \n" + this.extra_data + "\n//]]>" ); - - data.type = 'text/javascript'; - data.appendChild( dataContent ); - - document.getElementsByTagName( elementToAppendTo )[0].appendChild(data); - } - - // Build script tag and append to DOM in requested location - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = this.src; - script.id = this.handle; - - // If MediaElement.js is loaded in by this set of posts, don't initialize the players a second time as it breaks them all - if ( 'wp-mediaelement' === this.handle ) { - self.body.unbind( 'post-load', self.initializeMejs ); - } - - if ( 'wp-mediaelement' === this.handle && 'undefined' === typeof mejs ) { - self.wpMediaelement = {}; - self.wpMediaelement.tag = script; - self.wpMediaelement.element = elementToAppendTo; - setTimeout( self.maybeLoadMejs.bind( self ), 250 ); - } else { - document.getElementsByTagName( elementToAppendTo )[0].appendChild(script); - } - } ); - } - - // If additional stylesheets are required by the incoming set of posts, parse them - if ( response.styles ) { - $( response.styles ).each( function() { - // Add stylesheet handle to list of those already parsed - window.infiniteScroll.settings.styles.push( this.handle ); - - // Build link tag - var style = document.createElement('link'); - style.rel = 'stylesheet'; - style.href = this.src; - style.id = this.handle + '-css'; - - // Destroy link tag if a conditional statement is present and either the browser isn't IE, or the conditional doesn't evaluate true - if ( this.conditional && ( ! isIE || ! eval( this.conditional.replace( /%ver/g, IEVersion ) ) ) ) - var style = false; - - // Append link tag if necessary - if ( style ) - document.getElementsByTagName('head')[0].appendChild(style); - } ); - } - - // stash the response in the page cache - self.pageCache[self.page+self.offset] = response; - - // Increment the page number - self.page++; - - // Record pageview in WP Stats, if available. - if ( stats ) - new Image().src = document.location.protocol + '//pixel.wp.com/g.gif?' + stats + '&post=0&baba=' + Math.random(); - - // Add new posts to the postflair object - if ( 'object' == typeof response.postflair && 'object' == typeof WPCOM_sharing_counts ) - WPCOM_sharing_counts = $.extend( WPCOM_sharing_counts, response.postflair ); - - // Render the results - self.render.apply( self, arguments ); - - // If 'click' type and there are still posts to fetch, add back the handle - if ( type == 'click' ) { - if ( response.lastbatch ) { - if ( self.click_handle ) { - $( '#infinite-handle' ).remove(); - // Update body classes - self.body.addClass( 'infinity-end' ).removeClass( 'infinity-success' ); - } else { - self.body.trigger( 'infinite-scroll-posts-end' ); - } - } else { - if ( self.click_handle ) { - self.element.append( self.handle ); - } else { - self.body.trigger( 'infinite-scroll-posts-more' ); - } - } - } else if ( response.lastbatch ) { - self.disabled = true; - self.body.addClass( 'infinity-end' ).removeClass( 'infinity-success' ); - } - - // Update currentday to the latest value returned from the server - if ( response.currentday ) { - self.currentday = response.currentday; - } - - // Fire Google Analytics pageview - if ( self.google_analytics ) { - var ga_url = self.history.path.replace( /%d/, self.page ); - if ( 'object' === typeof _gaq ) { - _gaq.push( [ '_trackPageview', ga_url ] ); - } - if ( 'function' === typeof ga ) { - ga( 'send', 'pageview', ga_url ); - } - } - } - }); - - return jqxhr; -}; - -/** - * Core's native media player uses MediaElement.js - * The library's size is sufficient that it may not be loaded in time for Core's helper to invoke it, so we need to delay until `mejs` exists. - */ -Scroller.prototype.maybeLoadMejs = function() { - if ( null === this.wpMediaelement ) { - return; - } - - if ( 'undefined' === typeof mejs ) { - setTimeout( this.maybeLoadMejs, 250 ); - } else { - document.getElementsByTagName( this.wpMediaelement.element )[0].appendChild( this.wpMediaelement.tag ); - this.wpMediaelement = null; - - // Ensure any subsequent IS loads initialize the players - this.body.bind( 'post-load', { self: this }, this.initializeMejs ); - } -} - -/** - * Initialize the MediaElement.js player for any posts not previously initialized - */ -Scroller.prototype.initializeMejs = function( ev, response ) { - // Are there media players in the incoming set of posts? - if ( ! response.html || -1 === response.html.indexOf( 'wp-audio-shortcode' ) && -1 === response.html.indexOf( 'wp-video-shortcode' ) ) { - return; - } - - // Don't bother if mejs isn't loaded for some reason - if ( 'undefined' === typeof mejs ) { - return; - } - - // Adapted from wp-includes/js/mediaelement/wp-mediaelement.js - // Modified to not initialize already-initialized players, as Mejs doesn't handle that well - $(function () { - var settings = {}; - - if ( typeof _wpmejsSettings !== 'undefined' ) { - settings.pluginPath = _wpmejsSettings.pluginPath; - } - - settings.success = function (mejs) { - var autoplay = mejs.attributes.autoplay && 'false' !== mejs.attributes.autoplay; - if ( 'flash' === mejs.pluginType && autoplay ) { - mejs.addEventListener( 'canplay', function () { - mejs.play(); - }, false ); - } - }; - - $('.wp-audio-shortcode, .wp-video-shortcode').not( '.mejs-container' ).mediaelementplayer( settings ); - }); -} - -/** - * Trigger IS to load additional posts if the initial posts don't fill the window. - * On large displays, or when posts are very short, the viewport may not be filled with posts, so we overcome this by loading additional posts when IS initializes. - */ -Scroller.prototype.ensureFilledViewport = function() { - var self = this, - windowHeight = self.window.height(), - postsHeight = self.element.height(), - aveSetHeight = 0, - wrapperQty = 0; - - // Account for situations where postsHeight is 0 because child list elements are floated - if ( postsHeight === 0 ) { - $( self.element.selector + ' > li' ).each( function() { - postsHeight += $( this ).height(); - } ); - - if ( postsHeight === 0 ) { - self.body.unbind( 'post-load', self.checkViewportOnLoad ); - return; - } - } - - // Calculate average height of a set of posts to prevent more posts than needed from being loaded. - $( '.' + self.wrapperClass ).each( function() { - aveSetHeight += $( this ).height(); - wrapperQty++; - } ); - - if ( wrapperQty > 0 ) - aveSetHeight = aveSetHeight / wrapperQty; - else - aveSetHeight = 0; - - // Load more posts if space permits, otherwise stop checking for a full viewport - if ( postsHeight < windowHeight && ( postsHeight + aveSetHeight < windowHeight ) ) { - self.ready = true; - self.refresh(); - } - else { - self.body.unbind( 'post-load', self.checkViewportOnLoad ); - } -} - -/** - * Event handler for ensureFilledViewport(), tied to the post-load trigger. - * Necessary to ensure that the variable `this` contains the scroller when used in ensureFilledViewport(). Since this function is tied to an event, `this` becomes the DOM element the event is tied to. - */ -Scroller.prototype.checkViewportOnLoad = function( ev ) { - ev.data.self.ensureFilledViewport(); -} - -function fullscreenState() { - return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement - ? 1 - : 0; -} - -var previousFullScrenState = fullscreenState(); - -/** - * Identify archive page that corresponds to majority of posts shown in the current browser window. - */ -Scroller.prototype.determineURL = function () { - var self = this, - windowTop = $( window ).scrollTop(), - windowBottom = windowTop + $( window ).height(), - windowSize = windowBottom - windowTop, - setsInView = [], - setsHidden = [], - pageNum = false, - currentFullScreenState = fullscreenState(); - - // xor - check if the state has changed - if ( previousFullScrenState ^ currentFullScreenState ) { - // If we just switched to/from fullscreen, - // don't do the div clearing/caching or the - // URL setting. Doing so can break video playback - // if the video goes to fullscreen. - - previousFullScrenState = currentFullScreenState; - return; - } - previousFullScrenState = currentFullScreenState; - - // Find out which sets are in view - $( '.' + self.wrapperClass ).each( function() { - var id = $( this ).attr( 'id' ), - setTop = $( this ).offset().top, - setHeight = $( this ).outerHeight( false ), - setBottom = 0, - setPageNum = $( this ).data( 'page-num' ); - - // Account for containers that have no height because their children are floated elements. - if ( 0 === setHeight ) { - $( '> *', this ).each( function() { - setHeight += $( this ).outerHeight( false ); - } ); - } - - // Determine position of bottom of set by adding its height to the scroll position of its top. - setBottom = setTop + setHeight; - - // Populate setsInView object. While this logic could all be combined into a single conditional statement, this is easier to understand. - if ( setTop < windowTop && setBottom > windowBottom ) { // top of set is above window, bottom is below - setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum }); - } - else if( setTop > windowTop && setTop < windowBottom ) { // top of set is between top (gt) and bottom (lt) - setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum }); - } - else if( setBottom > windowTop && setBottom < windowBottom ) { // bottom of set is between top (gt) and bottom (lt) - setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum }); - } else { - setsHidden.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum }); - } - } ); - - $.each(setsHidden, function() { - var $set = $('#' + this.id); - if( $set.hasClass( 'is--replaced' ) ) { - return; - } - - self.pageCache[ this.pageNum].html = $set.html(); - - $set.css('min-height', ( this.bottom - this.top ) + 'px' ) - .addClass('is--replaced') - .empty(); - }); - - $.each(setsInView, function() { - var $set = $('#' + this.id); - - if( $set.hasClass('is--replaced') ) { - $set.css('min-height', '').removeClass('is--replaced'); - if( this.pageNum in self.pageCache ) { - $set.html( self.pageCache[this.pageNum].html ); - self.body.trigger( 'post-load', self.pageCache[this.pageNum] ); - } - } - - }); - - // Parse number of sets found in view in an attempt to update the URL to match the set that comprises the majority of the window. - if ( 0 == setsInView.length ) { - pageNum = -1; - } - else if ( 1 == setsInView.length ) { - var setData = setsInView.pop(); - - // If the first set of IS posts is in the same view as the posts loaded in the template by WordPress, determine how much of the view is comprised of IS-loaded posts - if ( ( ( windowBottom - setData.top ) / windowSize ) < 0.5 ) - pageNum = -1; - else - pageNum = setData.pageNum; - } - else { - var majorityPercentageInView = 0; - - // Identify the IS set that comprises the majority of the current window and set the URL to it. - $.each( setsInView, function( i, setData ) { - var topInView = 0, - bottomInView = 0, - percentOfView = 0; - - // Figure percentage of view the current set represents - if ( setData.top > windowTop && setData.top < windowBottom ) - topInView = ( windowBottom - setData.top ) / windowSize; - - if ( setData.bottom > windowTop && setData.bottom < windowBottom ) - bottomInView = ( setData.bottom - windowTop ) / windowSize; - - // Figure out largest percentage of view for current set - if ( topInView >= bottomInView ) - percentOfView = topInView; - else if ( bottomInView >= topInView ) - percentOfView = bottomInView; - - // Does current set's percentage of view supplant the largest previously-found set? - if ( percentOfView > majorityPercentageInView ) { - pageNum = setData.pageNum; - majorityPercentageInView = percentOfView; - } - } ); - } - - // If a page number could be determined, update the URL - // -1 indicates that the original requested URL should be used. - if ( 'number' == typeof pageNum ) { - self.updateURL( pageNum ); - } -} - -/** - * Update address bar to reflect archive page URL for a given page number. - * Checks if URL is different to prevent pollution of browser history. - */ -Scroller.prototype.updateURL = function( page ) { - // IE only supports pushState() in v10 and above, so don't bother if those conditions aren't met. - if ( ! window.history.pushState ) { - return; - } - var self = this, - pageSlug = -1 == page ? self.origURL : window.location.protocol + '//' + self.history.host + self.history.path.replace( /%d/, page ) + self.history.parameters; - - if ( window.location.href != pageSlug ) { - history.pushState( null, null, pageSlug ); - } -} - -/** - * Pause scrolling. - */ -Scroller.prototype.pause = function() { - this.disabled = true; -}; - -/** - * Resume scrolling. - */ -Scroller.prototype.resume = function() { - this.disabled = false; -}; - -/** - * Ready, set, go! - */ -$( document ).ready( function() { - // Check for our variables - if ( 'object' != typeof infiniteScroll ) - return; - - $( document.body ).addClass( infiniteScroll.settings.body_class ); - - // Set ajaxurl (for brevity) - ajaxurl = infiniteScroll.settings.ajaxurl; - - // Set stats, used for tracking stats - stats = infiniteScroll.settings.stats; - - // Define what type of infinity we have, grab text for click-handle - type = infiniteScroll.settings.type; - text = infiniteScroll.settings.text; - totop = infiniteScroll.settings.totop; - - // Initialize the scroller (with the ID of the element from the theme) - infiniteScroll.scroller = new Scroller( infiniteScroll.settings ); - - /** - * Monitor user scroll activity to update URL to correspond to archive page for current set of IS posts - */ - if( type == 'click' ) { - var timer = null; - $( window ).bind( 'scroll', function() { - // run the real scroll handler once every 250 ms. - if ( timer ) { return; } - timer = setTimeout( function() { - infiniteScroll.scroller.determineURL(); - timer = null; - } , 250 ); - }); - } - - // Integrate with Selective Refresh in the Customizer. - if ( 'undefined' !== typeof wp && wp.customize && wp.customize.selectiveRefresh ) { - - /** - * Handle rendering of selective refresh partials. - * - * Make sure that when a partial is rendered, the Jetpack post-load event - * will be triggered so that any dynamic elements will be re-constructed, - * such as ME.js elements, Photon replacements, social sharing, and more. - * Note that this is applying here not strictly to posts being loaded. - * If a widget contains a ME.js element and it is previewed via selective - * refresh, the post-load would get triggered allowing any dynamic elements - * therein to also be re-constructed. - * - * @param {wp.customize.selectiveRefresh.Placement} placement - */ - wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) { - var content; - if ( 'string' === typeof placement.addedContent ) { - content = placement.addedContent; - } else if ( placement.container ) { - content = $( placement.container ).html(); - } - - if ( content ) { - $( document.body ).trigger( 'post-load', { html: content } ); - } - } ); - - /* - * Add partials for posts added via infinite scroll. - * - * This is unnecessary when MutationObserver is supported by the browser - * since then this will be handled by Selective Refresh in core. - */ - if ( 'undefined' === typeof MutationObserver ) { - $( document.body ).on( 'post-load', function( e, response ) { - var rootElement = null; - if ( response.html && -1 !== response.html.indexOf( 'data-customize-partial' ) ) { - if ( infiniteScroll.settings.id ) { - rootElement = $( '#' + infiniteScroll.settings.id ); - } - wp.customize.selectiveRefresh.addPartials( rootElement ); - } - } ); - } - } -}); - - -})(jQuery); // Close closure |