infinity.js 23 KB

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