PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 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 All 508 releases
← All changes | modules/carousel/jetpack-carousel.js +31 -294 16.3-beta → 16.3-a.1 View file →
@@ -205,14 +205,8 @@
205 205 'ontouchstart' in window || ( window.DocumentTouch && document instanceof DocumentTouch )
206 206 );
207 207 }
208 208
209 - function prefersReducedMotion() {
210 - return (
211 - !! window.matchMedia && window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches
212 - );
213 - }
214 -
215 209 function scrollToElement( el, container, callback ) {
216 210 if ( ! el || ! container ) {
217 211 if ( callback ) {
218 212 return callback();
@@ -304,9 +298,8 @@
304 298 convertToPlainText: convertToPlainText,
305 299 stripHTML: stripHTML,
306 300 emitEvent: emitEvent,
307 301 isTouch: isTouch,
308 - prefersReducedMotion: prefersReducedMotion,
309 302 };
310 303 } )();
311 304
312 305 /////////////////////////////////////
@@ -313,14 +306,8 @@
313 306 // Carousel implementation
314 307 /////////////////////////////////////
315 308 function init() {
316 309 var commentInterval;
317 - // Rendered comments per attachment, kept for the lifetime of one overlay session.
318 - var commentsCache = {};
319 - // Attachment IDs with a comments request in flight, to prevent overlapping fetches.
320 - var commentsFetching = {};
321 - // Comments the server returns per page; mirrors the `number` arg in get_attachment_comments().
322 - var COMMENTS_PER_PAGE = 10;
323 310 var screenPadding;
324 311 var originalOverflow;
325 312 var originalHOverflow;
326 313 var scrollPos;
@@ -614,20 +601,8 @@
614 601 return;
615 602 }
616 603 if ( response.comment_status === 'approved' ) {
617 604 updatePostResults( jetpackCarouselStrings.comment_approved, true );
618 - /*
619 - Reflect the new comment in the badge total straight away. The refetch
620 - below only returns the first page, so on attachments with a full page
621 - of comments it cannot see the increment on its own. Bump the slide the
622 - comment was posted to -- captured as `attachmentId` -- not whichever
623 - slide happens to be current when this async response lands.
624 - */
625 - var postedSlide = getSlideByAttachmentId( attachmentId );
626 - if ( postedSlide ) {
627 - postedSlide.attrs.commentsCount =
628 - ( parseInt( postedSlide.attrs.commentsCount, 10 ) || 0 ) + 1;
629 - }
630 605 } else if ( response.comment_status === 'unapproved' ) {
631 606 updatePostResults( jetpackCarouselStrings.comment_unapproved, true );
632 607 } else {
633 608 // 'deleted', 'spam', false
@@ -633,13 +608,9 @@
633 608 // 'deleted', 'spam', false
634 609 updatePostResults( jetpackCarouselStrings.comment_post_error, false );
635 610 }
636 611 clearCommentTextAreaValue();
637 - // The new comment invalidates whatever we cached for this attachment.
638 - delete commentsCache[ attachmentId ];
639 - // Force past the in-flight guard so a still-pending first page can't
640 - // suppress this refresh.
641 - fetchComments( attachmentId, undefined, true );
612 + fetchComments( attachmentId );
642 613 submit.value = jetpackCarouselStrings.post_comment;
643 614 domUtil.hide( spinner );
644 615 form.classList.remove( 'jp-carousel-is-disabled' );
645 616 } else {
@@ -708,19 +679,8 @@
708 679 if ( commentsContainer ) {
709 680 commentsContainer.classList.toggle( 'jp-carousel-show' );
710 681 if ( commentsContainer.classList.contains( 'jp-carousel-show' ) ) {
711 682 extraInfoContainer.classList.add( 'jp-carousel-show' );
712 - // The panel is only now on screen, so this is when the comments are worth fetching.
713 - var current = carousel.currentSlide;
714 - if ( current ) {
715 - var currentCache = commentsCache[ current.attrs.attachmentId ];
716 - if ( ! currentCache ) {
717 - fetchComments( current.attrs.attachmentId );
718 - } else if ( currentCache.hasMore ) {
719 - // Cached page is already shown; re-arm paging for the rest.
720 - scheduleNextCommentsPage( current.attrs.attachmentId, currentCache.nextOffset );
721 - }
722 - }
723 683 } else {
724 684 extraInfoContainer.classList.remove( 'jp-carousel-show' );
725 685 }
726 686 }
@@ -818,17 +778,8 @@
818 778 swiper.slideTo( index + 1 );
819 779 }
820 780 }
821 781
822 - function getSlideByAttachmentId( attachmentId ) {
823 - for ( var i = 0; i < carousel.slides.length; i++ ) {
824 - if ( carousel.slides[ i ].attrs.attachmentId === attachmentId ) {
825 - return carousel.slides[ i ];
826 - }
827 - }
828 - return null;
829 - }
830 -
831 782 function selectSlideAtIndex( index ) {
832 783 if ( ! index || index < 0 || index > carousel.slides.length ) {
833 784 index = 0;
834 785 }
@@ -862,9 +813,9 @@
862 813 updateFullSizeLink( current );
863 814
864 815 if ( Number( jetpackCarouselStrings.display_comments ) === 1 ) {
865 816 testCommentsOpened( carousel.slides[ index ].attrs.commentsOpened );
866 - showCommentsForSlide( current );
817 + fetchComments( attachmentId );
867 818 domUtil.hide( carousel.info.querySelector( '#jp-carousel-comment-post-results' ) );
868 819 }
869 820
870 821 // Update pagination in footer.
@@ -911,10 +862,8 @@
911 862 carousel.isOpen = false;
912 863 swiper.destroy();
913 864 // Clear slide data for DOM garbage collection.
914 865 carousel.slides = [];
915 - commentsCache = {};
916 - commentsFetching = {};
917 866 carousel.currentSlide = undefined;
918 867 carousel.gallery.innerHTML = '';
919 868
920 869 domUtil.fadeOut( carousel.overlay, function () {
@@ -1006,21 +955,8 @@
1006 955 args.maxWidth = args.maxWidth * window.devicePixelRatio;
1007 956 args.maxHeight = args.maxHeight * window.devicePixelRatio;
1008 957 }
1009 958
1010 - /*
1011 - `maxWidth`/`maxHeight` now describe the device resolution. Anything beyond that is
1012 - headroom for zooming, so only the zoom request asks for it. The multiplier is folded
1013 - in here -- ahead of the size checks below -- so the zoom rendition keeps its extra
1014 - resolution even when the unzoomed one is already covered by `data-large-file`.
1015 - Without it, every slide would fetch ~4x the pixels the screen can show.
1016 - */
1017 - var headroom = args.zoomHeadroom || 1;
1018 - if ( headroom > 1 ) {
1019 - args.maxWidth = args.maxWidth * headroom;
1020 - args.maxHeight = args.maxHeight * headroom;
1021 - }
1022 -
1023 959 if ( largeWidth >= args.maxWidth || largeHeight >= args.maxHeight ) {
1024 960 return args.largeFile;
1025 961 }
1026 962
@@ -1036,10 +972,11 @@
1036 972
1037 973 // If we have a really large image load a smaller version
1038 974 // that is closer to the viewable size
1039 975 if ( args.origWidth > args.maxWidth || args.origHeight > args.maxHeight ) {
1040 - args.origMaxWidth = args.maxWidth;
1041 - args.origMaxHeight = args.maxHeight;
976 + // @2x the max sizes so we get a high enough resolution for zooming.
977 + args.origMaxWidth = args.maxWidth * 2;
978 + args.origMaxHeight = args.maxHeight * 2;
1042 979 // Add the fit arg to the list of Photon args.
1043 980 sanitizedUrl.searchParams.set( 'fit', args.origMaxWidth + ',' + args.origMaxHeight );
1044 981 }
1045 982
@@ -1277,111 +1214,22 @@
1277 1214 domUtil.fadeOut( commentForm );
1278 1215 }
1279 1216 }
1280 1217
1281 - function isCommentsPanelOpen() {
1282 - var wrapper = carousel.info.querySelector( '.jp-carousel-comments-wrapper' );
1283 - return !! wrapper && wrapper.classList.contains( 'jp-carousel-show' );
1284 - }
1285 -
1286 - function updateCommentsIndicator( count ) {
1287 - var indicator = carousel.info.querySelector(
1218 + function fetchComments( attachmentId, offset ) {
1219 + var shouldClear = offset === undefined;
1220 + var commentsIndicator = carousel.info.querySelector(
1288 1221 '.jp-carousel-icon-comments .jp-carousel-has-comments-indicator'
1289 1222 );
1290 1223
1291 - if ( ! indicator ) {
1292 - return;
1293 - }
1224 + commentsIndicator.classList.remove( 'jp-carousel-show' );
1294 1225
1295 - if ( count > 0 ) {
1296 - indicator.innerText = count;
1297 - indicator.classList.add( 'jp-carousel-show' );
1298 - } else {
1299 - indicator.classList.remove( 'jp-carousel-show' );
1300 - }
1301 - }
1302 -
1303 - /**
1304 - * Show what we already know about a slide's comments, and fetch the rest only if the
1305 - * comments panel is actually on screen. The comment list used to be re-fetched from
1306 - * admin-ajax on every single slide change, even though it is hidden by default.
1307 - * @param {object} slide - The slide being shown.
1308 - */
1309 - function showCommentsForSlide( slide ) {
1310 - var attachmentId = slide.attrs.attachmentId;
1311 - var comments = carousel.info.querySelector( '.jp-carousel-comments' );
1312 - var cached = commentsCache[ attachmentId ];
1313 -
1314 1226 clearInterval( commentInterval );
1315 - domUtil.hide( carousel.info.querySelector( '#jp-carousel-comments-loading' ) );
1316 1227
1317 - if ( cached ) {
1318 - comments.innerHTML = cached.html;
1319 - updateCommentsIndicator( cached.count );
1320 - domUtil.show( comments );
1321 - /*
1322 - Resume paging if the cached run ended on a full page, so returning to a slide
1323 - does not strand the comments past the first page. Only while the panel is on
1324 - screen, mirroring the fresh-fetch path below.
1325 - */
1326 - if ( cached.hasMore && isCommentsPanelOpen() ) {
1327 - scheduleNextCommentsPage( attachmentId, cached.nextOffset );
1328 - }
1329 - return;
1330 - }
1331 -
1332 - comments.innerHTML = '';
1333 - domUtil.hide( comments );
1334 - // The server tells us the count up front, so the badge costs no request.
1335 - updateCommentsIndicator( parseInt( slide.attrs.commentsCount, 10 ) || 0 );
1336 -
1337 - if ( isCommentsPanelOpen() ) {
1338 - fetchComments( attachmentId );
1339 - }
1340 - }
1341 -
1342 - /**
1343 - * Fetch the next page of comments once the reader scrolls near the bottom.
1344 - * @param {string} attachmentId - The attachment whose comments are shown.
1345 - * @param {number} nextOffset - Offset of the page to load next.
1346 - */
1347 - function scheduleNextCommentsPage( attachmentId, nextOffset ) {
1348 - clearInterval( commentInterval );
1349 - commentInterval = setInterval( function () {
1350 - if ( carousel.container.scrollTop + 150 > window.innerHeight ) {
1351 - clearInterval( commentInterval );
1352 - fetchComments( attachmentId, nextOffset );
1353 - }
1354 - }, 300 );
1355 - }
1356 -
1357 - function fetchComments( attachmentId, offset, force ) {
1358 - var shouldClear = offset === undefined;
1359 -
1360 - clearInterval( commentInterval );
1361 -
1362 1228 if ( ! attachmentId ) {
1363 1229 return;
1364 1230 }
1365 1231
1366 - /*
1367 - The comments UI is shared and only ever shows the current slide, so a request for
1368 - any other slide (e.g. a post-submit refetch after the reader has navigated on) must
1369 - not touch it.
1370 - */
1371 - if ( ! carousel.currentSlide || carousel.currentSlide.attrs.attachmentId !== attachmentId ) {
1372 - return;
1373 - }
1374 -
1375 - /*
1376 - One request per attachment at a time, so a reopen mid-flight can't double-load.
1377 - `force` (a post-submit refresh) supersedes instead: the newest request is recorded
1378 - below and any older one's response bails on the identity check.
1379 - */
1380 - if ( commentsFetching[ attachmentId ] && ! force ) {
1381 - return;
1382 - }
1383 -
1384 1232 if ( ! offset || offset < 1 ) {
1385 1233 offset = 0;
1386 1234 }
1387 1235
@@ -1394,16 +1242,8 @@
1394 1242 comments.innerHTML = '';
1395 1243 }
1396 1244
1397 1245 var xhr = new XMLHttpRequest();
1398 - // Record this as the live request for the attachment; a later `force` fetch replaces it.
1399 - commentsFetching[ attachmentId ] = xhr;
1400 -
1401 - // True while `xhr` is still the attachment's live request (not superseded).
1402 - var isLiveRequest = function () {
1403 - return commentsFetching[ attachmentId ] === xhr;
1404 - };
1405 -
1406 1246 var url =
1407 1247 jetpackCarouselStrings.ajaxurl +
1408 1248 '?action=get_attachment_comments' +
1409 1249 '&nonce=' +
@@ -1414,40 +1254,14 @@
1414 1254 offset;
1415 1255 xhr.open( 'GET', url );
1416 1256 xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' );
1417 1257
1418 - // Reveal the comments and clear the loading state; callers gate this themselves.
1419 - var revealComments = function () {
1258 + var onError = function () {
1420 1259 domUtil.fadeIn( comments );
1421 1260 domUtil.fadeOut( commentsLoading );
1422 1261 };
1423 1262
1424 - // Network-level failure (fires without onload); guard it like a late response.
1425 - var onError = function () {
1426 - // A newer (forced) request has superseded this one; leave the UI to it.
1427 - if ( ! isLiveRequest() ) {
1428 - return;
1429 - }
1430 - delete commentsFetching[ attachmentId ];
1431 -
1432 - // Only touch the shared UI if this attachment is still on screen.
1433 - if (
1434 - ! carousel.currentSlide ||
1435 - carousel.currentSlide.attrs.attachmentId !== attachmentId
1436 - ) {
1437 - return;
1438 - }
1439 - revealComments();
1440 - };
1441 -
1442 1263 xhr.onload = function () {
1443 - // A newer (forced) request has superseded this one; drop its result.
1444 - if ( ! isLiveRequest() ) {
1445 - return;
1446 - }
1447 - // The request is done, whatever the outcome below.
1448 - delete commentsFetching[ attachmentId ];
1449 -
1450 1264 // Ignore the results if they arrive late and we're now on a different slide.
1451 1265 if (
1452 1266 ! carousel.currentSlide ||
1453 1267 carousel.currentSlide.attrs.attachmentId !== attachmentId
@@ -1463,11 +1277,9 @@
1463 1277 // Do nothing.
1464 1278 }
1465 1279
1466 1280 if ( ! isSuccess || ! data || ! Array.isArray( data ) ) {
1467 - // Already past the identity/current-slide guards above.
1468 - revealComments();
1469 - return;
1281 + return onError();
1470 1282 }
1471 1283
1472 1284 if ( shouldClear ) {
1473 1285 comments.innerHTML = '';
@@ -1474,13 +1286,8 @@
1474 1286 }
1475 1287
1476 1288 for ( var i = 0; i < data.length; i++ ) {
1477 1289 var entry = data[ i ];
1478 - // Skip anything already on screen: a page can be requested twice if the
1479 - // reader re-opens a slide while its previous request is still in flight.
1480 - if ( comments.querySelector( '#jp-carousel-comment-' + entry.id ) ) {
1481 - continue;
1482 - }
1483 1290 var comment = document.createElement( 'div' );
1484 1291 comment.classList.add( 'jp-carousel-comment' );
1485 1292 comment.setAttribute( 'id', 'jp-carousel-comment-' + entry.id );
1486 1293 comment.innerHTML =
@@ -1496,46 +1303,25 @@
1496 1303 '</div>' +
1497 1304 entry.content +
1498 1305 '</div>';
1499 1306 comments.appendChild( comment );
1500 - }
1501 1307
1502 - /*
1503 - A full page back means there may be more; watch for a scroll to the bottom to
1504 - load the next one. A short page means we have reached the end.
1505 - */
1506 - var hasMore = data.length >= COMMENTS_PER_PAGE;
1507 - var nextOffset = offset + COMMENTS_PER_PAGE;
1508 - if ( hasMore ) {
1509 - scheduleNextCommentsPage( attachmentId, nextOffset );
1308 + // Set the interval to check for a new page of comments.
1309 + clearInterval( commentInterval );
1310 + commentInterval = setInterval( function () {
1311 + if ( carousel.container.scrollTop + 150 > window.innerHeight ) {
1312 + fetchComments( attachmentId, offset + 10 );
1313 + clearInterval( commentInterval );
1314 + }
1315 + }, 300 );
1510 1316 }
1511 1317
1512 - /*
1513 - The endpoint returns at most one page, so prefer the server's total where we
1514 - have it and only count what we rendered as a fallback.
1515 - */
1516 - var rendered = comments.querySelectorAll( '.jp-carousel-comment' ).length;
1517 - var count = Math.max(
1518 - parseInt( carousel.currentSlide.attrs.commentsCount, 10 ) || 0,
1519 - rendered
1520 - );
1521 -
1522 1318 if ( data.length > 0 ) {
1523 1319 domUtil.show( comments );
1524 - updateCommentsIndicator( count );
1320 + commentsIndicator.innerText = data.length;
1321 + commentsIndicator.classList.add( 'jp-carousel-show' );
1525 1322 }
1526 1323
1527 - /*
1528 - Keep what we rendered for as long as the overlay is open, so going back to a
1529 - slide costs nothing -- including where to resume paging from.
1530 - */
1531 - commentsCache[ attachmentId ] = {
1532 - html: comments.innerHTML,
1533 - count: count,
1534 - hasMore: hasMore,
1535 - nextOffset: nextOffset,
1536 - };
1537 -
1538 1324 domUtil.hide( commentsLoading );
1539 1325 };
1540 1326
1541 1327 xhr.onerror = onError;
@@ -1579,16 +1365,10 @@
1579 1365
1580 1366 fullImage.addEventListener(
1581 1367 'load',
1582 1368 function () {
1583 - /*
1584 - If the visitor zoomed while this was still downloading, a larger rendition
1585 - is already in place -- don't downgrade it back to the fit-to-screen src.
1586 - */
1587 - if ( ! image.hasAttribute( 'data-zoom-loaded' ) ) {
1588 - // Cached by this point, so swapping it in is effectively instant.
1589 - image.src = attrs.src;
1590 - }
1369 + // Cached by this point, so swapping it in is effectively instant.
1370 + image.src = attrs.src;
1591 1371 image.style.filter = '';
1592 1372 },
1593 1373 { once: true }
1594 1374 );
@@ -1603,30 +1383,8 @@
1603 1383
1604 1384 fullImage.src = attrs.src;
1605 1385 }
1606 1386
1607 - /**
1608 - * Swap in a higher-resolution rendition now that the visitor is zoomed in.
1609 - *
1610 - * The zoomed-out slide only ever loads what the screen can actually show. The browser
1611 - * keeps painting the image it already has until this one decodes, so the picture
1612 - * sharpens rather than blanking.
1613 - * @param {object} slide - The slide being zoomed.
1614 - */
1615 - function loadZoomImage( slide ) {
1616 - if ( ! slide || ! slide.attrs.zoomSrc || slide.attrs.zoomSrc === slide.attrs.src ) {
1617 - return;
1618 - }
1619 -
1620 - var image = slide.el.querySelector( 'img' );
1621 - if ( ! image || image.hasAttribute( 'data-zoom-loaded' ) ) {
1622 - return;
1623 - }
1624 -
1625 - image.setAttribute( 'data-zoom-loaded', 1 );
1626 - image.src = slide.attrs.zoomSrc;
1627 - }
1628 -
1629 1387 function preloadAdjacentImages( currentIndex ) {
1630 1388 var indicesToPreload = [];
1631 1389 var totalSlides = carousel.slides.length;
1632 1390
@@ -1753,9 +1511,8 @@
1753 1511 var attrs = {
1754 1512 originalElement: item,
1755 1513 attachmentId: attrID,
1756 1514 commentsOpened: item.getAttribute( 'data-comments-opened' ) || '0',
1757 - commentsCount: item.getAttribute( 'data-comments-count' ) || '0',
1758 1515 imageMeta: domUtil.getJSONAttribute( item, 'data-image-meta' ) || {},
1759 1516 title: item.getAttribute( 'data-image-title' ) || '',
1760 1517 desc: item.getAttribute( 'data-image-description' ) || '',
1761 1518 largeFile: item.getAttribute( 'data-large-file' ) || '',
@@ -1781,28 +1538,16 @@
1781 1538
1782 1539 if ( typeof wpcom !== 'undefined' && wpcom.carousel && wpcom.carousel.generateImgSrc ) {
1783 1540 attrs.src = wpcom.carousel.generateImgSrc( item, max );
1784 1541 } else {
1785 - var urlArgs = function ( zoomHeadroom ) {
1786 - // A fresh object each time: selectBestImageUrl() mutates what it is given.
1787 - return {
1788 - origFile: attrs.src,
1789 - origWidth: attrs.origWidth,
1790 - origHeight: attrs.origHeight,
1791 - maxWidth: max.width,
1792 - maxHeight: max.height,
1793 - largeFile: attrs.largeFile,
1794 - zoomHeadroom: zoomHeadroom,
1795 - };
1796 - };
1797 -
1798 - /*
1799 - The zoom rendition is kept aside until the visitor actually zooms. On sources
1800 - that cannot serve a larger one it comes back identical to `src`, and nothing
1801 - ever happens.
1802 - */
1803 - attrs.zoomSrc = selectBestImageUrl( urlArgs( 2 ) );
1804 - attrs.src = selectBestImageUrl( urlArgs( 1 ) );
1542 + attrs.src = selectBestImageUrl( {
1543 + origFile: attrs.src,
1544 + origWidth: attrs.origWidth,
1545 + origHeight: attrs.origHeight,
1546 + maxWidth: max.width,
1547 + maxHeight: max.height,
1548 + largeFile: attrs.largeFile,
1549 + } );
1805 1550 }
1806 1551
1807 1552 // Set the final src.
1808 1553 item.setAttribute( 'data-gallery-src', attrs.src );
@@ -1913,10 +1658,8 @@
1913 1658 }
1914 1659
1915 1660 domUtil.emitEvent( carousel.overlay, 'jp_carousel.beforeOpen' );
1916 1661 carousel.gallery.innerHTML = '';
1917 - commentsCache = {};
1918 - commentsFetching = {};
1919 1662
1920 1663 // Need to set the overlay manually to block or swiper does't initialise properly.
1921 1664 carousel.overlay.style.opacity = 1;
1922 1665 carousel.overlay.style.display = 'block';
@@ -1924,13 +1667,8 @@
1924 1667 initCarouselSlides( images, settings.startIndex );
1925 1668
1926 1669 swiper = new window.JetpackSwiper( '.jp-carousel-swiper-container', {
1927 1670 centeredSlides: true,
1928 - /*
1929 - Swiper's 300ms default is most of what makes slide-to-slide feel slow. Keep a
1930 - visible movement -- the slide is a real affordance on touch -- but a shorter one.
1931 - */
1932 - speed: domUtil.prefersReducedMotion() ? 0 : 250,
1933 1671 zoom: true,
1934 1672 loop: carousel.slides.length > 1,
1935 1673 // Turn off interactions and hide navigation arrows if there is only one slide.
1936 1674 enabled: carousel.slides.length > 1,
@@ -1963,9 +1701,8 @@
1963 1701 } );
1964 1702
1965 1703 swiper.on( 'zoomChange', function ( swiper, scale ) {
1966 1704 if ( scale > 1 ) {
1967 - loadZoomImage( carousel.currentSlide );
1968 1705 carousel.overlay.classList.add( 'jp-carousel-hide-controls' );
1969 1706 }
1970 1707
1971 1708 if ( scale === 1 ) {