PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 7.6.2
Jetpack – WP Security, Backup, Speed, & Growth v7.6.2
16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 All 502 releases
jetpack / modules / infinite-scroll / infinity.js

infinity.js in Jetpack – WP Security, Backup, Speed, & Growth 7.6.2, at modules/infinite-scroll/infinity.js

809 lines 24.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* globals infiniteScroll, _wpmejsSettings, ga, _gaq, WPCOM_sharing_counts */
2 ( function( $ ) {
3 // Open closure
4 // Local vars
5 var Scroller, ajaxurl, stats, type, text, totop;
6
7 // IE requires special handling
8 var isIE = -1 != navigator.userAgent.search( 'MSIE' );
9 if ( isIE ) {
10 var IEVersion = navigator.userAgent.match( /MSIE\s?(\d+)\.?\d*;/ );
11 IEVersion = parseInt( IEVersion[ 1 ] );
12 }
13
14 // HTTP ajaxurl when site is HTTPS causes Access-Control-Allow-Origin failure in Desktop and iOS Safari
15 if ( 'https:' == document.location.protocol ) {
16 infiniteScroll.settings.ajaxurl = infiniteScroll.settings.ajaxurl.replace(
17 'http://',
18 'https://'
19 );
20 }
21
22 /**
23 * Loads new posts when users scroll near the bottom of the page.
24 */
25 Scroller = function( settings ) {
26 var self = this;
27
28 // Initialize our variables
29 this.id = settings.id;
30 this.body = $( document.body );
31 this.window = $( window );
32 this.element = $( '#' + settings.id );
33 this.wrapperClass = settings.wrapper_class;
34 this.ready = true;
35 this.disabled = false;
36 this.page = 1;
37 this.offset = settings.offset;
38 this.currentday = settings.currentday;
39 this.order = settings.order;
40 this.throttle = false;
41 this.handle =
42 '<div id="infinite-handle"><span><button>' +
43 text.replace( '\\', '' ) +
44 '</button></span></div>';
45 this.click_handle = settings.click_handle;
46 this.google_analytics = settings.google_analytics;
47 this.history = settings.history;
48 this.origURL = window.location.href;
49 this.pageCache = {};
50
51 // Footer settings
52 this.footer = $( '#infinite-footer' );
53 this.footer.wrap = settings.footer;
54
55 // Core's native MediaElement.js implementation needs special handling
56 this.wpMediaelement = null;
57
58 // We have two type of infinite scroll
59 // cases 'scroll' and 'click'
60
61 if ( type == 'scroll' ) {
62 // Bind refresh to the scroll event
63 // Throttle to check for such case every 300ms
64
65 // On event the case becomes a fact
66 this.window.bind( 'scroll.infinity', function() {
67 this.throttle = true;
68 } );
69
70 // Go back top method
71 self.gotop();
72
73 setInterval( function() {
74 if ( this.throttle ) {
75 // Once the case is the case, the action occurs and the fact is no more
76 this.throttle = false;
77 // Reveal or hide footer
78 self.thefooter();
79 // Fire the refresh
80 self.refresh();
81 self.determineURL(); // determine the url
82 }
83 }, 250 );
84
85 // Ensure that enough posts are loaded to fill the initial viewport, to compensate for short posts and large displays.
86 self.ensureFilledViewport();
87 this.body.bind( 'post-load', { self: self }, self.checkViewportOnLoad );
88 } else if ( type == 'click' ) {
89 if ( this.click_handle ) {
90 this.element.append( this.handle );
91 }
92
93 this.body.delegate( '#infinite-handle', 'click.infinity', function() {
94 // Handle the handle
95 if ( self.click_handle ) {
96 $( '#infinite-handle' ).remove();
97 }
98
99 // Fire the refresh
100 self.refresh();
101 } );
102 }
103
104 // Initialize any Core audio or video players loaded via IS
105 this.body.bind( 'post-load', { self: self }, self.initializeMejs );
106 };
107
108 /**
109 * Check whether we should fetch any additional posts.
110 */
111 Scroller.prototype.check = function() {
112 var container = this.element.offset();
113
114 // If the container can't be found, stop otherwise errors result
115 if ( 'object' !== typeof container ) {
116 return false;
117 }
118
119 var bottom = this.window.scrollTop() + this.window.height(),
120 threshold = container.top + this.element.outerHeight( false ) - this.window.height() * 2;
121
122 return bottom > threshold;
123 };
124
125 /**
126 * Renders the results from a successful response.
127 */
128 Scroller.prototype.render = function( response ) {
129 this.body.addClass( 'infinity-success' );
130
131 // Check if we can wrap the html
132 this.element.append( response.html );
133 this.body.trigger( 'post-load', response );
134 this.ready = true;
135 };
136
137 /**
138 * Returns the object used to query for new posts.
139 */
140 Scroller.prototype.query = function() {
141 return {
142 page: this.page + this.offset, // Load the next page.
143 currentday: this.currentday,
144 order: this.order,
145 scripts: window.infiniteScroll.settings.scripts,
146 styles: window.infiniteScroll.settings.styles,
147 query_args: window.infiniteScroll.settings.query_args,
148 query_before: window.infiniteScroll.settings.query_before,
149 last_post_date: window.infiniteScroll.settings.last_post_date,
150 };
151 };
152
153 /**
154 * Scroll back to top.
155 */
156 Scroller.prototype.gotop = function() {
157 var blog = $( '#infinity-blog-title' );
158
159 blog.attr( 'title', totop );
160
161 // Scroll to top on blog title
162 blog.bind( 'click', function( e ) {
163 $( 'html, body' ).animate( { scrollTop: 0 }, 'fast' );
164 e.preventDefault();
165 } );
166 };
167
168 /**
169 * The infinite footer.
170 */
171 Scroller.prototype.thefooter = function() {
172 var self = this,
173 width;
174
175 // Check if we have an id for the page wrapper
176 if ( $.type( this.footer.wrap ) === 'string' ) {
177 width = $( 'body #' + this.footer.wrap ).outerWidth( false );
178
179 // Make the footer match the width of the page
180 if ( width > 479 ) {
181 this.footer.find( '.container' ).css( 'width', width );
182 }
183 }
184
185 // Reveal footer
186 if ( this.window.scrollTop() >= 350 ) {
187 self.footer.animate( { bottom: 0 }, 'fast' );
188 } else if ( this.window.scrollTop() < 350 ) {
189 self.footer.animate( { bottom: '-50px' }, 'fast' );
190 }
191 };
192
193 /**
194 * Controls the flow of the refresh. Don't mess.
195 */
196 Scroller.prototype.refresh = function() {
197 var self = this,
198 query,
199 jqxhr,
200 load,
201 loader,
202 color,
203 customized;
204
205 // If we're disabled, ready, or don't pass the check, bail.
206 if ( this.disabled || ! this.ready || ! this.check() ) {
207 return;
208 }
209
210 // Let's get going -- set ready to false to prevent
211 // multiple refreshes from occurring at once.
212 this.ready = false;
213
214 // Create a loader element to show it's working.
215 if ( this.click_handle ) {
216 loader = '<span class="infinite-loader"></span>';
217 this.element.append( loader );
218
219 loader = this.element.find( '.infinite-loader' );
220 color = loader.css( 'color' );
221
222 try {
223 loader.spin( 'medium-left', color );
224 } catch ( error ) {}
225 }
226
227 // Generate our query vars.
228 query = $.extend(
229 {
230 action: 'infinite_scroll',
231 },
232 this.query()
233 );
234
235 // Inject Customizer state.
236 if ( 'undefined' !== typeof wp && wp.customize && wp.customize.settings.theme ) {
237 customized = {};
238 query.wp_customize = 'on';
239 query.theme = wp.customize.settings.theme.stylesheet;
240 wp.customize.each( function( setting ) {
241 if ( setting._dirty ) {
242 customized[ setting.id ] = setting();
243 }
244 } );
245 query.customized = JSON.stringify( customized );
246 query.nonce = wp.customize.settings.nonce.preview;
247 }
248
249 // Fire the ajax request.
250 jqxhr = $.post( infiniteScroll.settings.ajaxurl, query );
251
252 // Allow refreshes to occur again if an error is triggered.
253 jqxhr.fail( function() {
254 if ( self.click_handle ) {
255 loader.hide();
256 }
257
258 self.ready = true;
259 } );
260
261 // Success handler
262 jqxhr.done( function( response ) {
263 // On success, let's hide the loader circle.
264 if ( self.click_handle ) {
265 loader.hide();
266 }
267
268 // Check for and parse our response.
269 if ( ! response || ! response.type ) {
270 return;
271 }
272
273 // If we've succeeded...
274 if ( response.type == 'success' ) {
275 // If additional scripts are required by the incoming set of posts, parse them
276 if ( response.scripts ) {
277 $( response.scripts ).each( function() {
278 var elementToAppendTo = this.footer ? 'body' : 'head';
279
280 // Add script handle to list of those already parsed
281 window.infiniteScroll.settings.scripts.push( this.handle );
282
283 // Output extra data, if present
284 if ( this.extra_data ) {
285 var data = document.createElement( 'script' ),
286 dataContent = document.createTextNode(
287 '//<![CDATA[ \n' + this.extra_data + '\n//]]>'
288 );
289
290 data.type = 'text/javascript';
291 data.appendChild( dataContent );
292
293 document.getElementsByTagName( elementToAppendTo )[ 0 ].appendChild( data );
294 }
295
296 // Build script tag and append to DOM in requested location
297 var script = document.createElement( 'script' );
298 script.type = 'text/javascript';
299 script.src = this.src;
300 script.id = this.handle;
301
302 // If MediaElement.js is loaded in by this set of posts, don't initialize the players a second time as it breaks them all
303 if ( 'wp-mediaelement' === this.handle ) {
304 self.body.unbind( 'post-load', self.initializeMejs );
305 }
306
307 if ( 'wp-mediaelement' === this.handle && 'undefined' === typeof mejs ) {
308 self.wpMediaelement = {};
309 self.wpMediaelement.tag = script;
310 self.wpMediaelement.element = elementToAppendTo;
311 setTimeout( self.maybeLoadMejs.bind( self ), 250 );
312 } else {
313 document.getElementsByTagName( elementToAppendTo )[ 0 ].appendChild( script );
314 }
315 } );
316 }
317
318 // If additional stylesheets are required by the incoming set of posts, parse them
319 if ( response.styles ) {
320 $( response.styles ).each( function() {
321 // Add stylesheet handle to list of those already parsed
322 window.infiniteScroll.settings.styles.push( this.handle );
323
324 // Build link tag
325 var style = document.createElement( 'link' );
326 style.rel = 'stylesheet';
327 style.href = this.src;
328 style.id = this.handle + '-css';
329
330 // Destroy link tag if a conditional statement is present and either the browser isn't IE, or the conditional doesn't evaluate true
331 if (
332 this.conditional &&
333 ( ! isIE || ! eval( this.conditional.replace( /%ver/g, IEVersion ) ) )
334 ) {
335 style = false;
336 }
337
338 // Append link tag if necessary
339 if ( style ) {
340 document.getElementsByTagName( 'head' )[ 0 ].appendChild( style );
341 }
342 } );
343 }
344
345 // stash the response in the page cache
346 self.pageCache[ self.page + self.offset ] = response;
347
348 // Increment the page number
349 self.page++;
350
351 // Record pageview in WP Stats, if available.
352 if ( stats ) {
353 new Image().src =
354 document.location.protocol +
355 '//pixel.wp.com/g.gif?' +
356 stats +
357 '&post=0&baba=' +
358 Math.random();
359 }
360
361 // Add new posts to the postflair object
362 if ( 'object' === typeof response.postflair && 'object' === typeof WPCOM_sharing_counts ) {
363 WPCOM_sharing_counts = $.extend( WPCOM_sharing_counts, response.postflair ); // eslint-disable-line no-global-assign
364 }
365
366 // Render the results
367 self.render.apply( self, arguments );
368
369 // If 'click' type and there are still posts to fetch, add back the handle
370 if ( type == 'click' ) {
371 if ( response.lastbatch ) {
372 if ( self.click_handle ) {
373 $( '#infinite-handle' ).remove();
374 // Update body classes
375 self.body.addClass( 'infinity-end' ).removeClass( 'infinity-success' );
376 } else {
377 self.body.trigger( 'infinite-scroll-posts-end' );
378 }
379 } else {
380 if ( self.click_handle ) {
381 self.element.append( self.handle );
382 } else {
383 self.body.trigger( 'infinite-scroll-posts-more' );
384 }
385 }
386 } else if ( response.lastbatch ) {
387 self.disabled = true;
388 self.body.addClass( 'infinity-end' ).removeClass( 'infinity-success' );
389 }
390
391 // Update currentday to the latest value returned from the server
392 if ( response.currentday ) {
393 self.currentday = response.currentday;
394 }
395
396 // Fire Google Analytics pageview
397 if ( self.google_analytics ) {
398 var ga_url = self.history.path.replace( /%d/, self.page );
399 if ( 'object' === typeof _gaq ) {
400 _gaq.push( [ '_trackPageview', ga_url ] );
401 }
402 if ( 'function' === typeof ga ) {
403 ga( 'send', 'pageview', ga_url );
404 }
405 }
406 }
407 } );
408
409 return jqxhr;
410 };
411
412 /**
413 * Core's native media player uses MediaElement.js
414 * 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.
415 */
416 Scroller.prototype.maybeLoadMejs = function() {
417 if ( null === this.wpMediaelement ) {
418 return;
419 }
420
421 if ( 'undefined' === typeof mejs ) {
422 setTimeout( this.maybeLoadMejs, 250 );
423 } else {
424 document
425 .getElementsByTagName( this.wpMediaelement.element )[ 0 ]
426 .appendChild( this.wpMediaelement.tag );
427 this.wpMediaelement = null;
428
429 // Ensure any subsequent IS loads initialize the players
430 this.body.bind( 'post-load', { self: this }, this.initializeMejs );
431 }
432 };
433
434 /**
435 * Initialize the MediaElement.js player for any posts not previously initialized
436 */
437 Scroller.prototype.initializeMejs = function( ev, response ) {
438 // Are there media players in the incoming set of posts?
439 if (
440 ! response.html ||
441 ( -1 === response.html.indexOf( 'wp-audio-shortcode' ) &&
442 -1 === response.html.indexOf( 'wp-video-shortcode' ) )
443 ) {
444 return;
445 }
446
447 // Don't bother if mejs isn't loaded for some reason
448 if ( 'undefined' === typeof mejs ) {
449 return;
450 }
451
452 // Adapted from wp-includes/js/mediaelement/wp-mediaelement.js
453 // Modified to not initialize already-initialized players, as Mejs doesn't handle that well
454 $( function() {
455 var settings = {};
456
457 if ( typeof _wpmejsSettings !== 'undefined' ) {
458 settings.pluginPath = _wpmejsSettings.pluginPath;
459 }
460
461 settings.success = function( mejs ) {
462 var autoplay = mejs.attributes.autoplay && 'false' !== mejs.attributes.autoplay;
463 if ( 'flash' === mejs.pluginType && autoplay ) {
464 mejs.addEventListener(
465 'canplay',
466 function() {
467 mejs.play();
468 },
469 false
470 );
471 }
472 };
473
474 $( '.wp-audio-shortcode, .wp-video-shortcode' )
475 .not( '.mejs-container' )
476 .mediaelementplayer( settings );
477 } );
478 };
479
480 /**
481 * Trigger IS to load additional posts if the initial posts don't fill the window.
482 * 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.
483 */
484 Scroller.prototype.ensureFilledViewport = function() {
485 var self = this,
486 windowHeight = self.window.height(),
487 postsHeight = self.element.height(),
488 aveSetHeight = 0,
489 wrapperQty = 0;
490
491 // Account for situations where postsHeight is 0 because child list elements are floated
492 if ( postsHeight === 0 ) {
493 $( self.element.selector + ' > li' ).each( function() {
494 postsHeight += $( this ).height();
495 } );
496
497 if ( postsHeight === 0 ) {
498 self.body.unbind( 'post-load', self.checkViewportOnLoad );
499 return;
500 }
501 }
502
503 // Calculate average height of a set of posts to prevent more posts than needed from being loaded.
504 $( '.' + self.wrapperClass ).each( function() {
505 aveSetHeight += $( this ).height();
506 wrapperQty++;
507 } );
508
509 if ( wrapperQty > 0 ) {
510 aveSetHeight = aveSetHeight / wrapperQty;
511 } else {
512 aveSetHeight = 0;
513 }
514
515 // Load more posts if space permits, otherwise stop checking for a full viewport
516 if ( postsHeight < windowHeight && postsHeight + aveSetHeight < windowHeight ) {
517 self.ready = true;
518 self.refresh();
519 } else {
520 self.body.unbind( 'post-load', self.checkViewportOnLoad );
521 }
522 };
523
524 /**
525 * Event handler for ensureFilledViewport(), tied to the post-load trigger.
526 * 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.
527 */
528 Scroller.prototype.checkViewportOnLoad = function( ev ) {
529 ev.data.self.ensureFilledViewport();
530 };
531
532 function fullscreenState() {
533 return document.fullscreenElement ||
534 document.mozFullScreenElement ||
535 document.webkitFullscreenElement ||
536 document.msFullscreenElement
537 ? 1
538 : 0;
539 }
540
541 var previousFullScrenState = fullscreenState();
542
543 /**
544 * Identify archive page that corresponds to majority of posts shown in the current browser window.
545 */
546 Scroller.prototype.determineURL = function() {
547 var self = this,
548 windowTop = $( window ).scrollTop(),
549 windowBottom = windowTop + $( window ).height(),
550 windowSize = windowBottom - windowTop,
551 setsInView = [],
552 setsHidden = [],
553 pageNum = false,
554 currentFullScreenState = fullscreenState();
555
556 // xor - check if the state has changed
557 if ( previousFullScrenState ^ currentFullScreenState ) {
558 // If we just switched to/from fullscreen,
559 // don't do the div clearing/caching or the
560 // URL setting. Doing so can break video playback
561 // if the video goes to fullscreen.
562
563 previousFullScrenState = currentFullScreenState;
564 return;
565 }
566 previousFullScrenState = currentFullScreenState;
567
568 // Find out which sets are in view
569 $( '.' + self.wrapperClass ).each( function() {
570 var id = $( this ).attr( 'id' ),
571 setTop = $( this ).offset().top,
572 setHeight = $( this ).outerHeight( false ),
573 setBottom = 0,
574 setPageNum = $( this ).data( 'page-num' );
575
576 // Account for containers that have no height because their children are floated elements.
577 if ( 0 === setHeight ) {
578 $( '> *', this ).each( function() {
579 setHeight += $( this ).outerHeight( false );
580 } );
581 }
582
583 // Determine position of bottom of set by adding its height to the scroll position of its top.
584 setBottom = setTop + setHeight;
585
586 // Populate setsInView object. While this logic could all be combined into a single conditional statement, this is easier to understand.
587 if ( setTop < windowTop && setBottom > windowBottom ) {
588 // top of set is above window, bottom is below
589 setsInView.push( { id: id, top: setTop, bottom: setBottom, pageNum: setPageNum } );
590 } else if ( setTop > windowTop && setTop < windowBottom ) {
591 // top of set is between top (gt) and bottom (lt)
592 setsInView.push( { id: id, top: setTop, bottom: setBottom, pageNum: setPageNum } );
593 } else if ( setBottom > windowTop && setBottom < windowBottom ) {
594 // bottom of set is between top (gt) and bottom (lt)
595 setsInView.push( { id: id, top: setTop, bottom: setBottom, pageNum: setPageNum } );
596 } else {
597 setsHidden.push( { id: id, top: setTop, bottom: setBottom, pageNum: setPageNum } );
598 }
599 } );
600
601 $.each( setsHidden, function() {
602 var $set = $( '#' + this.id );
603 if ( $set.hasClass( 'is--replaced' ) ) {
604 return;
605 }
606
607 self.pageCache[ this.pageNum ].html = $set.html();
608
609 $set
610 .css( 'min-height', this.bottom - this.top + 'px' )
611 .addClass( 'is--replaced' )
612 .empty();
613 } );
614
615 $.each( setsInView, function() {
616 var $set = $( '#' + this.id );
617
618 if ( $set.hasClass( 'is--replaced' ) ) {
619 $set.css( 'min-height', '' ).removeClass( 'is--replaced' );
620 if ( this.pageNum in self.pageCache ) {
621 $set.html( self.pageCache[ this.pageNum ].html );
622 self.body.trigger( 'post-load', self.pageCache[ this.pageNum ] );
623 }
624 }
625 } );
626
627 // 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.
628 if ( 0 == setsInView.length ) {
629 pageNum = -1;
630 } else if ( 1 == setsInView.length ) {
631 var setData = setsInView.pop();
632
633 // 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
634 if ( ( windowBottom - setData.top ) / windowSize < 0.5 ) {
635 pageNum = -1;
636 } else {
637 pageNum = setData.pageNum;
638 }
639 } else {
640 var majorityPercentageInView = 0;
641
642 // Identify the IS set that comprises the majority of the current window and set the URL to it.
643 $.each( setsInView, function( i, setData ) {
644 var topInView = 0,
645 bottomInView = 0,
646 percentOfView = 0;
647
648 // Figure percentage of view the current set represents
649 if ( setData.top > windowTop && setData.top < windowBottom ) {
650 topInView = ( windowBottom - setData.top ) / windowSize;
651 }
652
653 if ( setData.bottom > windowTop && setData.bottom < windowBottom ) {
654 bottomInView = ( setData.bottom - windowTop ) / windowSize;
655 }
656
657 // Figure out largest percentage of view for current set
658 if ( topInView >= bottomInView ) {
659 percentOfView = topInView;
660 } else if ( bottomInView >= topInView ) {
661 percentOfView = bottomInView;
662 }
663
664 // Does current set's percentage of view supplant the largest previously-found set?
665 if ( percentOfView > majorityPercentageInView ) {
666 pageNum = setData.pageNum;
667 majorityPercentageInView = percentOfView;
668 }
669 } );
670 }
671
672 // If a page number could be determined, update the URL
673 // -1 indicates that the original requested URL should be used.
674 if ( 'number' === typeof pageNum ) {
675 self.updateURL( pageNum );
676 }
677 };
678
679 /**
680 * Update address bar to reflect archive page URL for a given page number.
681 * Checks if URL is different to prevent pollution of browser history.
682 */
683 Scroller.prototype.updateURL = function( page ) {
684 // IE only supports pushState() in v10 and above, so don't bother if those conditions aren't met.
685 if ( ! window.history.pushState ) {
686 return;
687 }
688 var self = this,
689 pageSlug = self.origURL;
690
691 if ( -1 !== page ) {
692 pageSlug =
693 window.location.protocol +
694 '//' +
695 self.history.host +
696 self.history.path.replace( /%d/, page ) +
697 self.history.parameters;
698 }
699
700 if ( window.location.href != pageSlug ) {
701 history.pushState( null, null, pageSlug );
702 }
703 };
704
705 /**
706 * Pause scrolling.
707 */
708 Scroller.prototype.pause = function() {
709 this.disabled = true;
710 };
711
712 /**
713 * Resume scrolling.
714 */
715 Scroller.prototype.resume = function() {
716 this.disabled = false;
717 };
718
719 /**
720 * Ready, set, go!
721 */
722 $( document ).ready( function() {
723 // Check for our variables
724 if ( 'object' !== typeof infiniteScroll ) {
725 return;
726 }
727
728 $( document.body ).addClass( infiniteScroll.settings.body_class );
729
730 // Set ajaxurl (for brevity)
731 ajaxurl = infiniteScroll.settings.ajaxurl;
732
733 // Set stats, used for tracking stats
734 stats = infiniteScroll.settings.stats;
735
736 // Define what type of infinity we have, grab text for click-handle
737 type = infiniteScroll.settings.type;
738 text = infiniteScroll.settings.text;
739 totop = infiniteScroll.settings.totop;
740
741 // Initialize the scroller (with the ID of the element from the theme)
742 infiniteScroll.scroller = new Scroller( infiniteScroll.settings );
743
744 /**
745 * Monitor user scroll activity to update URL to correspond to archive page for current set of IS posts
746 */
747 if ( type == 'click' ) {
748 var timer = null;
749 $( window ).bind( 'scroll', function() {
750 // run the real scroll handler once every 250 ms.
751 if ( timer ) {
752 return;
753 }
754 timer = setTimeout( function() {
755 infiniteScroll.scroller.determineURL();
756 timer = null;
757 }, 250 );
758 } );
759 }
760
761 // Integrate with Selective Refresh in the Customizer.
762 if ( 'undefined' !== typeof wp && wp.customize && wp.customize.selectiveRefresh ) {
763 /**
764 * Handle rendering of selective refresh partials.
765 *
766 * Make sure that when a partial is rendered, the Jetpack post-load event
767 * will be triggered so that any dynamic elements will be re-constructed,
768 * such as ME.js elements, Photon replacements, social sharing, and more.
769 * Note that this is applying here not strictly to posts being loaded.
770 * If a widget contains a ME.js element and it is previewed via selective
771 * refresh, the post-load would get triggered allowing any dynamic elements
772 * therein to also be re-constructed.
773 *
774 * @param {wp.customize.selectiveRefresh.Placement} placement
775 */
776 wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
777 var content;
778 if ( 'string' === typeof placement.addedContent ) {
779 content = placement.addedContent;
780 } else if ( placement.container ) {
781 content = $( placement.container ).html();
782 }
783
784 if ( content ) {
785 $( document.body ).trigger( 'post-load', { html: content } );
786 }
787 } );
788
789 /*
790 * Add partials for posts added via infinite scroll.
791 *
792 * This is unnecessary when MutationObserver is supported by the browser
793 * since then this will be handled by Selective Refresh in core.
794 */
795 if ( 'undefined' === typeof MutationObserver ) {
796 $( document.body ).on( 'post-load', function( e, response ) {
797 var rootElement = null;
798 if ( response.html && -1 !== response.html.indexOf( 'data-customize-partial' ) ) {
799 if ( infiniteScroll.settings.id ) {
800 rootElement = $( '#' + infiniteScroll.settings.id );
801 }
802 wp.customize.selectiveRefresh.addPartials( rootElement );
803 }
804 } );
805 }
806 }
807 } );
808 } )( jQuery ); // Close closure
809