<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[50829] trunk/src: Media: Remove infinite scroll from media library and modal.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/50829">50829</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/50829","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>joedolson</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2021-05-07 23:17:33 +0000 (Fri, 07 May 2021)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Media: Remove infinite scroll from media library and modal.

Replace infinitely autoloading behavior on scroll with a user-controlled load more button. Fix a long standing accessibility issue in the media library. Infinite scroll poses a wide range of problems for accessibility, usability, and performance.

This change modifies the library to load 40 items in the initial view, with a load more button to load the next 40 items and a button to move focus from the load more region to the first of the most recently added items.

The text for communicating the jump target was broadly discussed, agreeing that the text incorporated here would most concisely and clearly convey the purpose of the button, and any further detail is learnable from use.
 
Props afercia, adamsilverstein, joedolson, audrasjb, francina 
Fixes <a href="https://core.trac.wordpress.org/ticket/50105">#50105</a>. See <a href="https://core.trac.wordpress.org/ticket/40330">#40330</a>. </pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcjsmediamodelsattachmentsjs">trunk/src/js/media/models/attachments.js</a></li>
<li><a href="#trunksrcjsmediamodelsqueryjs">trunk/src/js/media/models/query.js</a></li>
<li><a href="#trunksrcjsmediaviewsattachmentsbrowserjs">trunk/src/js/media/views/attachments/browser.js</a></li>
<li><a href="#trunksrcjsmediaviewsattachmentsjs">trunk/src/js/media/views/attachments.js</a></li>
<li><a href="#trunksrcwpadmincssmediacss">trunk/src/wp-admin/css/media.css</a></li>
<li><a href="#trunksrcwpadminincludesajaxactionsphp">trunk/src/wp-admin/includes/ajax-actions.php</a></li>
<li><a href="#trunksrcwpincludescssmediaviewscss">trunk/src/wp-includes/css/media-views.css</a></li>
<li><a href="#trunksrcwpincludesmediaphp">trunk/src/wp-includes/media.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcjsmediamodelsattachmentsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/js/media/models/attachments.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/js/media/models/attachments.js  2021-05-07 20:14:22 UTC (rev 50828)
+++ trunk/src/js/media/models/attachments.js    2021-05-07 23:17:33 UTC (rev 50829)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -348,20 +348,48 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return this.mirroring ? this.mirroring.hasMore() : false;
</span><span class="cx" style="display: block; padding: 0 10px">        },
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Holds the total number of attachments.
+        *
+        * @since 5.7.0
+        */
+       totalAttachments: 0,
+
+       /**
+        * Gets the total number of attachments.
+        *
+        * @since 5.7.0
+        *
+        * @return {number} The total number of attachments.
+        */
+       getTotalAttachments: function() {
+               return this.mirroring ? this.mirroring.totalAttachments : 0;
+       },
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * A custom Ajax-response parser.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * See trac ticket #24753
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {Object|Array} resp The raw response Object/Array.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Called automatically by Backbone whenever a collection's models are returned
+        * by the server, in fetch. The default implementation is a no-op, simply
+        * passing through the JSON response. We override this to add attributes to
+        * the collection items.
+        *
+        * Since WordPress 5.5, the response returns the attachments under `response.attachments`
+        * and `response.totalAttachments` holds the total number of attachments found.
+        *
+        * @param {Object|Array} response The raw response Object/Array.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @param {Object} xhr
</span><span class="cx" style="display: block; padding: 0 10px">         * @return {Array} The array of model attributes to be added to the collection
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        parse: function( resp, xhr ) {
-               if ( ! _.isArray( resp ) ) {
-                       resp = [resp];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ parse: function( response, xhr ) {
+               if ( ! _.isArray( response.attachments ) ) {
+                       response = [response.attachments];
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return _.map( resp, function( attrs ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         this.totalAttachments = parseInt( response.totalAttachments, 10 );
+
+               return _.map( response.attachments, function( attrs ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         var id, attachment, newAttributes;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( attrs instanceof Backbone.Model ) {
</span></span></pre></div>
<a id="trunksrcjsmediamodelsqueryjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/js/media/models/query.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/js/media/models/query.js        2021-05-07 20:14:22 UTC (rev 50828)
+++ trunk/src/js/media/models/query.js  2021-05-07 23:17:33 UTC (rev 50829)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -112,8 +112,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                options = options || {};
</span><span class="cx" style="display: block; padding: 0 10px">                options.remove = false;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return this._more = this.fetch( options ).done( function( resp ) {
-                       if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return this._more = this.fetch( options ).done( function( response ) {
+                       // Since WordPress 5.5, the response returns the attachments under `response.attachments`.
+                       var attachments = response.attachments;
+
+                       if ( _.isEmpty( attachments ) || -1 === this.args.posts_per_page || attachments.length < this.args.posts_per_page ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 query._hasMore = false;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                });
</span></span></pre></div>
<a id="trunksrcjsmediaviewsattachmentsbrowserjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/js/media/views/attachments/browser.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/js/media/views/attachments/browser.js   2021-05-07 20:14:22 UTC (rev 50828)
+++ trunk/src/js/media/views/attachments/browser.js     2021-05-07 23:17:33 UTC (rev 50829)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2,7 +2,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">        mediaTrash = wp.media.view.settings.mediaTrash,
</span><span class="cx" style="display: block; padding: 0 10px">        l10n = wp.media.view.l10n,
</span><span class="cx" style="display: block; padding: 0 10px">        $ = jQuery,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        AttachmentsBrowser;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ AttachmentsBrowser,
+       infiniteScrolling = wp.media.view.settings.infiniteScrolling,
+       __ = wp.i18n.__,
+       sprintf = wp.i18n.sprintf;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * wp.media.view.AttachmentsBrowser
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -68,13 +71,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        this.createUploader();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">                 // Add a heading before the attachments list.
</span><span class="cx" style="display: block; padding: 0 10px">                this.createAttachmentsHeading();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Create the list of attachments.
-               this.createAttachments();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Create the attachments wrapper view.
+               this.createAttachmentsWrapperView();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( ! infiniteScrolling ) {
+                       this.$el.addClass( 'has-load-more' );
+                       this.createLoadMoreView();
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( this.options.sidebar && 'errors' !== this.options.sidebar ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        this.createSidebar();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -92,6 +99,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                this.collection.on( 'add remove reset', this.updateContent, this );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( ! infiniteScrolling ) {
+                       this.collection.on( 'add remove reset', this.updateLoadMoreView, this );
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // The non-cached or cached attachments query has completed.
</span><span class="cx" style="display: block; padding: 0 10px">                this.collection.on( 'attachments:received', this.announceSearchResults, this );
</span><span class="cx" style="display: block; padding: 0 10px">        },
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -106,8 +117,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return {void}
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        announceSearchResults: _.debounce( function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                var count;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         var count,
+                       /* translators: Accessibility text. %d: Number of attachments found in a search. */
+                       mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Click load more for more results.' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( infiniteScrolling ) {
+                       /* translators: Accessibility text. %d: Number of attachments found in a search. */
+                       mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Scroll the page for more results.' );
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( this.collection.mirroring.args.s ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        count = this.collection.length;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -117,7 +135,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( this.collection.hasMore() ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                wp.a11y.speak( l10n.mediaFoundHasMoreResults.replace( '%d', count ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         wp.a11y.speak( mediaFoundHasMoreResultsMessage.replace( '%d', count ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 return;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -392,8 +410,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        noItemsView;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( this.controller.isModeActive( 'grid' ) ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        // Usually the media library.
</ins><span class="cx" style="display: block; padding: 0 10px">                         noItemsView = view.attachmentsNoResults;
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        // Usually the media modal.
</ins><span class="cx" style="display: block; padding: 0 10px">                         noItemsView = view.uploader;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -433,6 +453,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">        },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Creates the Attachments wrapper view.
+        *
+        * @since 5.7.0
+        *
+        * @return {void}
+        */
+       createAttachmentsWrapperView: function() {
+               this.attachmentsWrapper = new wp.media.View( {
+                       className: 'attachments-wrapper'
+               } );
+
+               // Create the list of attachments.
+               this.views.add( this.attachmentsWrapper );
+               this.createAttachments();
+       },
+
</ins><span class="cx" style="display: block; padding: 0 10px">         createAttachments: function() {
</span><span class="cx" style="display: block; padding: 0 10px">                this.attachments = new wp.media.view.Attachments({
</span><span class="cx" style="display: block; padding: 0 10px">                        controller:           this.controller,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -451,9 +488,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                this.controller.on( 'attachment:keydown:arrow',     _.bind( this.attachments.arrowEvent, this.attachments ) );
</span><span class="cx" style="display: block; padding: 0 10px">                this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                this.views.add( this.attachments );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         this.views.add( '.attachments-wrapper', this.attachments );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">                 if ( this.controller.isModeActive( 'grid' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        this.attachmentsNoResults = new View({
</span><span class="cx" style="display: block; padding: 0 10px">                                controller: this.controller,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -467,6 +503,157 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">        },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Creates the load more button and attachments counter view.
+        *
+        * @since 5.7.0
+        *
+        * @return {void}
+        */
+       createLoadMoreView: function() {
+               var view = this;
+
+               this.loadMoreWrapper = new View( {
+                       controller: this.controller,
+                       className: 'load-more-wrapper'
+               } );
+
+               this.loadMoreCount = new View( {
+                       controller: this.controller,
+                       tagName: 'p',
+                       className: 'load-more-count hidden'
+               } );
+
+               this.loadMoreButton = new wp.media.view.Button( {
+                       text: __( 'Load more' ),
+                       className: 'load-more hidden',
+                       style: 'primary',
+                       size: '',
+                       click: function() {
+                               view.loadMoreAttachments();
+                       }
+               } );
+
+               this.loadMoreSpinner = new wp.media.view.Spinner();
+
+               this.loadMoreJumpToFirst = new wp.media.view.Button( {
+                       text: __( 'Jump to first loaded item' ),
+                       className: 'load-more-jump hidden',
+                       size: '',
+                       click: function() {
+                               view.jumpToFirstAddedItem();
+                       }
+               } );
+
+               this.views.add( '.attachments-wrapper', this.loadMoreWrapper );
+               this.views.add( '.load-more-wrapper', this.loadMoreSpinner );
+               this.views.add( '.load-more-wrapper', this.loadMoreCount );
+               this.views.add( '.load-more-wrapper', this.loadMoreButton );
+               this.views.add( '.load-more-wrapper', this.loadMoreJumpToFirst );
+       },
+
+       /**
+        * Updates the Load More view. This function is debounced because the
+        * collection updates multiple times at the add, remove, and reset events.
+        * We need it to run only once, after all attachments are added or removed.
+        *
+        * @since 5.7.0
+        *
+        * @return {void}
+        */
+       updateLoadMoreView: _.debounce( function() {
+               // Ensure the load more view elements are initially hidden at each update.
+               this.loadMoreButton.$el.addClass( 'hidden' );
+               this.loadMoreCount.$el.addClass( 'hidden' );
+               this.loadMoreJumpToFirst.$el.addClass( 'hidden' ).prop( 'disabled', true );
+
+               if ( ! this.collection.getTotalAttachments() ) {
+                       return;
+               }
+
+               if ( this.collection.length ) {
+                       this.loadMoreCount.$el.text(
+                               /* translators: 1: Number of displayed attachments, 2: Number of total attachments. */
+                               sprintf(
+                                       __( 'Showing %1$s of %2$s media items' ),
+                                       this.collection.length,
+                                       this.collection.getTotalAttachments()
+                               )
+                       );
+
+                       this.loadMoreCount.$el.removeClass( 'hidden' );
+               }
+
+               /*
+                * Notice that while the collection updates multiple times hasMore() may
+                * return true when it's actually not true.
+                */
+               if ( this.collection.hasMore() ) {
+                       this.loadMoreButton.$el.removeClass( 'hidden' );
+               }
+
+               // Find the media item to move focus to. The jQuery `eq()` index is zero-based.
+               this.firstAddedMediaItem = this.$el.find( '.attachment' ).eq( this.firstAddedMediaItemIndex );
+
+               // If there's a media item to move focus to, make the "Jump to" button available.
+               if ( this.firstAddedMediaItem.length ) {
+                       this.firstAddedMediaItem.addClass( 'new-media' );
+                       this.loadMoreJumpToFirst.$el.removeClass( 'hidden' ).prop( 'disabled', false );
+               }
+
+               // If there are new items added, but no more to be added, move focus to Jump button.
+               if ( this.firstAddedMediaItem.length && ! this.collection.hasMore() ) {
+                       this.loadMoreJumpToFirst.$el.trigger( 'focus' );
+               }
+       }, 10 ),
+
+       /**
+        * Loads more attachments.
+        *
+        * @since 5.7.0
+        *
+        * @return {void}
+        */
+       loadMoreAttachments: function() {
+               var view = this;
+
+               if ( ! this.collection.hasMore() ) {
+                       return;
+               }
+
+               /*
+                * The collection index is zero-based while the length counts the actual
+                * amount of items. Thus the length is equivalent to the position of the
+                * first added item.
+                */
+               this.firstAddedMediaItemIndex = this.collection.length;
+
+               this.$el.addClass( 'more-loaded' );
+               this.collection.each( function( attachment ) {
+                       var attach_id = attachment.attributes.id;
+                       $( '[data-id="' + attach_id + '"]' ).addClass( 'found-media' );
+               });
+
+               view.loadMoreSpinner.show();
+
+               this.collection.more().done( function() {
+                       // Within done(), `this` is the returned collection.
+                       view.loadMoreSpinner.hide();
+               } );
+       },
+
+       /**
+        * Moves focus to the first new added item.     .
+        *
+        * @since 5.7.0
+        *
+        * @return {void}
+        */
+       jumpToFirstAddedItem: function() {
+               // Set focus on first added item.
+               this.firstAddedMediaItem.focus();
+       },
+
</ins><span class="cx" style="display: block; padding: 0 10px">         createAttachmentsHeading: function() {
</span><span class="cx" style="display: block; padding: 0 10px">                this.attachmentsHeading = new wp.media.view.Heading( {
</span><span class="cx" style="display: block; padding: 0 10px">                        text: l10n.attachmentsList,
</span></span></pre></div>
<a id="trunksrcjsmediaviewsattachmentsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/js/media/views/attachments.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/js/media/views/attachments.js   2021-05-07 20:14:22 UTC (rev 50828)
+++ trunk/src/js/media/views/attachments.js     2021-05-07 23:17:33 UTC (rev 50829)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,6 +1,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> var View = wp.media.View,
</span><span class="cx" style="display: block; padding: 0 10px">        $ = jQuery,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        Attachments;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ Attachments,
+       infiniteScrolling = wp.media.view.settings.infiniteScrolling;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{
</span><span class="cx" style="display: block; padding: 0 10px">        tagName:   'ul',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -35,6 +36,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                this.el.id = _.uniqueId('__attachments-view-');
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @param infiniteScrolling  Whether to enable infinite scrolling or use
+                *                           the default "load more" button.
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @param refreshSensitivity The time in milliseconds to throttle the scroll
</span><span class="cx" style="display: block; padding: 0 10px">                 *                           handler.
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param refreshThreshold   The amount of pixels that should be scrolled before
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -49,6 +52,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 *                           calculating the total number of columns.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                _.defaults( this.options, {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        infiniteScrolling:  infiniteScrolling || false,
</ins><span class="cx" style="display: block; padding: 0 10px">                         refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
</span><span class="cx" style="display: block; padding: 0 10px">                        refreshThreshold:   3,
</span><span class="cx" style="display: block; padding: 0 10px">                        AttachmentView:     wp.media.view.Attachment,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -84,11 +88,13 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                this.controller.on( 'library:selection:add', this.attachmentFocus, this );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Throttle the scroll handler and bind this.
-               this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( this.options.infiniteScrolling ) {
+                       // Throttle the scroll handler and bind this.
+                       this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                this.options.scrollElement = this.options.scrollElement || this.el;
-               $( this.options.scrollElement ).on( 'scroll', this.scroll );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 this.options.scrollElement = this.options.scrollElement || this.el;
+                       $( this.options.scrollElement ).on( 'scroll', this.scroll );
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                this.initSortable();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -387,7 +393,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        this.views.set( this.collection.map( this.createAttachmentView, this ) );
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span><span class="cx" style="display: block; padding: 0 10px">                        this.views.unset();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.collection.more().done( this.scroll );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( this.options.infiniteScrolling ) {
+                               this.collection.more().done( this.scroll );
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">        },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -400,7 +408,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return {void}
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        ready: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                this.scroll();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( this.options.infiniteScrolling ) {
+                       this.scroll();
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">         },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="trunksrcwpadmincssmediacss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/css/media.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/media.css  2021-05-07 20:14:22 UTC (rev 50828)
+++ trunk/src/wp-admin/css/media.css    2021-05-07 23:17:33 UTC (rev 50829)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -420,7 +420,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> .media-frame.mode-grid,
</span><span class="cx" style="display: block; padding: 0 10px"> .media-frame.mode-grid .media-frame-content,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.media-frame.mode-grid .attachments-browser .attachments,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.media-frame.mode-grid .attachments-browser:not(.has-load-more) .attachments,
+.media-frame.mode-grid .attachments-browser.has-load-more .attachments-wrapper,
</ins><span class="cx" style="display: block; padding: 0 10px"> .media-frame.mode-grid .uploader-inline-content {
</span><span class="cx" style="display: block; padding: 0 10px">        position: static;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -498,7 +499,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">        border: 4px dashed #c3c4c7;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.media-frame.mode-select .attachments-browser.fixed .attachments {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.media-frame.mode-select .attachments-browser.fixed:not(.has-load-more) .attachments,
+.media-frame.mode-select .attachments-browser.has-load-more.fixed .attachments-wrapper {
</ins><span class="cx" style="display: block; padding: 0 10px">         position: relative;
</span><span class="cx" style="display: block; padding: 0 10px">        top: 94px; /* prevent jumping up when the toolbar becomes fixed */
</span><span class="cx" style="display: block; padding: 0 10px">        padding-bottom: 94px; /* offset for above so the bottom doesn't get cut off */
</span></span></pre></div>
<a id="trunksrcwpadminincludesajaxactionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/ajax-actions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/ajax-actions.php      2021-05-07 20:14:22 UTC (rev 50828)
+++ trunk/src/wp-admin/includes/ajax-actions.php        2021-05-07 23:17:33 UTC (rev 50829)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2993,7 +2993,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
</span><span class="cx" style="display: block; padding: 0 10px">        $posts = array_filter( $posts );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp_send_json_success( $posts );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $result = array(
+               'attachments'      => $posts,
+               'totalAttachments' => $query->found_posts,
+       );
+
+       wp_send_json_success( $result );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span></span></pre></div>
<a id="trunksrcwpincludescssmediaviewscss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/css/media-views.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/css/media-views.css 2021-05-07 20:14:22 UTC (rev 50828)
+++ trunk/src/wp-includes/css/media-views.css   2021-05-07 23:17:33 UTC (rev 50829)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1188,7 +1188,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">        padding: 2px 8px 8px;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.attachments-browser .attachments,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.attachments-browser:not(.has-load-more) .attachments,
+.attachments-browser.has-load-more .attachments-wrapper,
</ins><span class="cx" style="display: block; padding: 0 10px"> .attachments-browser .uploader-inline {
</span><span class="cx" style="display: block; padding: 0 10px">        position: absolute;
</span><span class="cx" style="display: block; padding: 0 10px">        top: 72px;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1267,6 +1268,96 @@
</span><span class="cx" style="display: block; padding: 0 10px">        padding: 2em 0 0 2em;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.more-loaded .attachment:not(.found-media) {
+       background: #dcdcde;
+}
+
+.load-more-wrapper {
+       clear: both;
+       display: flex;
+       flex-wrap: wrap;
+       align-items: center;
+       justify-content: center;
+       padding: 1em 0;
+}
+
+.load-more-wrapper .load-more-count {
+       min-width: 100%;
+       margin: 0 0 1em;
+       text-align: center;
+}
+
+.load-more-wrapper .load-more {
+       margin: 0;
+}
+
+/* Needs high specificity. */
+.media-frame .load-more-wrapper .load-more + .spinner {
+       float: none;
+       margin: 0 -30px 0 10px;
+}
+
+/* Reset spinner margin when the button is hidden to avoid horizontal scrollbar. */
+.media-frame .load-more-wrapper .load-more.hidden + .spinner {
+       margin: 0;
+}
+
+/* Force a new row within the flex container. */
+.load-more-wrapper::after {
+       content: "";
+       min-width: 100%;
+       order: 1;
+}
+
+.load-more-wrapper .load-more-jump {
+       margin: 0 0 0 12px;
+}
+
+.attachment.new-media {
+       outline: 2px dotted #c3c4c7;
+}
+
+.load-more-wrapper {
+       clear: both;
+       display: flex;
+       flex-wrap: wrap;
+       align-items: center;
+       justify-content: center;
+       padding: 1em 0;
+}
+
+.load-more-wrapper .load-more-count {
+       min-width: 100%;
+       margin: 0 0 1em;
+       text-align: center;
+}
+
+.load-more-wrapper .load-more {
+       margin: 0;
+}
+
+/* Needs high specificity. */
+.media-frame .load-more-wrapper .load-more + .spinner {
+       float: none;
+       margin: 0 -30px 0 10px;
+}
+
+/* Reset spinner margin when the button is hidden to avoid horizontal scrollbar. */
+.media-frame .load-more-wrapper .load-more.hidden + .spinner {
+       margin: 0;
+}
+
+/* Force a new row within the flex container. */
+.load-more-wrapper::after {
+       content: "";
+       min-width: 100%;
+       order: 1;
+}
+
+.load-more-wrapper .load-more-jump {
+       margin: 0 0 0 12px;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Progress Bar
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2818,6 +2909,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">        .media-frame-content .media-toolbar .instructions {
</span><span class="cx" style="display: block; padding: 0 10px">                display: none;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /* Change margin direction on load more button in responsive views. */
+       .load-more-wrapper .load-more-jump {
+               margin: 12px 0 0 0;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> @media only screen and (min-width: 901px) and (max-height: 400px) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2826,6 +2923,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                top: 0;
</span><span class="cx" style="display: block; padding: 0 10px">                padding-top: 44px;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /* Change margin direction on load more button in responsive views. */
+       .load-more-wrapper .load-more-jump {
+               margin: 12px 0 0 0;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> @media only screen and (max-width: 480px) {
</span></span></pre></div>
<a id="trunksrcwpincludesmediaphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/media.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/media.php   2021-05-07 20:14:22 UTC (rev 50828)
+++ trunk/src/wp-includes/media.php     2021-05-07 23:17:33 UTC (rev 50829)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4306,29 +4306,39 @@
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Filters whether the Media Library grid has infinite scrolling. Default `false`.
+        *
+        * @since 5.7.0
+        *
+        * @param bool $value The filtered value, defaults to `false`.
+        */
+       $infinite_scrolling = apply_filters( 'media_library_infinite_scrolling', false );
+
</ins><span class="cx" style="display: block; padding: 0 10px">         $settings = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'tabs'             => $tabs,
-               'tabUrl'           => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),
-               'mimeTypes'        => wp_list_pluck( get_post_mime_types(), 0 ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'tabs'              => $tabs,
+               'tabUrl'            => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),
+               'mimeTypes'         => wp_list_pluck( get_post_mime_types(), 0 ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 /** This filter is documented in wp-admin/includes/media.php */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'captions'         => ! apply_filters( 'disable_captions', '' ),
-               'nonce'            => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'captions'          => ! apply_filters( 'disable_captions', '' ),
+               'nonce'             => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                         'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ),
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'post'             => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'post'              => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                         'id' => 0,
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'defaultProps'     => $props,
-               'attachmentCounts' => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'defaultProps'      => $props,
+               'attachmentCounts'  => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                         'audio' => ( $show_audio_playlist ) ? 1 : 0,
</span><span class="cx" style="display: block; padding: 0 10px">                        'video' => ( $show_video_playlist ) ? 1 : 0,
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'oEmbedProxyUrl'   => rest_url( 'oembed/1.0/proxy' ),
-               'embedExts'        => $exts,
-               'embedMimes'       => $ext_mimes,
-               'contentWidth'     => $content_width,
-               'months'           => $months,
-               'mediaTrash'       => MEDIA_TRASH ? 1 : 0,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'oEmbedProxyUrl'    => rest_url( 'oembed/1.0/proxy' ),
+               'embedExts'         => $exts,
+               'embedMimes'        => $ext_mimes,
+               'contentWidth'      => $content_width,
+               'months'            => $months,
+               'mediaTrash'        => MEDIA_TRASH ? 1 : 0,
+               'infiniteScrolling' => ( $infinite_scrolling ) ? 1 : 0,
</ins><span class="cx" style="display: block; padding: 0 10px">         );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $post = null;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4412,8 +4422,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                'searchLabel'                 => __( 'Search' ),
</span><span class="cx" style="display: block; padding: 0 10px">                'searchMediaLabel'            => __( 'Search media' ),          // Backward compatibility pre-5.3.
</span><span class="cx" style="display: block; padding: 0 10px">                'searchMediaPlaceholder'      => __( 'Search media items...' ), // Placeholder (no ellipsis), backward compatibility pre-5.3.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                /* translators: %d: Number of attachments found in a search. */
</ins><span class="cx" style="display: block; padding: 0 10px">                 'mediaFound'                  => __( 'Number of media items found: %d' ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'mediaFoundHasMoreResults'    => __( 'Number of media items displayed: %d. Scroll the page for more results.' ),
</del><span class="cx" style="display: block; padding: 0 10px">                 'noMedia'                     => __( 'No media items found.' ),
</span><span class="cx" style="display: block; padding: 0 10px">                'noMediaTryNewSearch'         => __( 'No media items found. Try a different search.' ),
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre>
</div>
</div>

</body>
</html>