WordPress.org

Make WordPress Core

Ticket #24716: 24716.38.diff

File 24716.38.diff, 7.5 KB (added by adamsilverstein, 4 years ago)

routing, keyboard

  • src/wp-admin/upload.php

     
    2424        wp_enqueue_media();
    2525        wp_enqueue_script( 'media-grid' );
    2626        wp_enqueue_script( 'media' );
     27        wp_localize_script( 'media-grid', 'mediaGridSettings', array( 'adminUrl' => parse_url( self_admin_url(), PHP_URL_PATH )  ) );
    2728
    2829        require_once( ABSPATH . 'wp-admin/admin-header.php' );
    2930        include( ABSPATH . 'wp-admin/admin-footer.php' );
  • src/wp-includes/js/media-grid.js

     
    1 /* global _wpMediaViewsL10n, setUserSetting, deleteUserSetting, MediaElementPlayer */
     1/* global _wpMediaViewsL10n, setUserSetting, deleteUserSetting, MediaElementPlayer, mediaGridSettings*/
    22(function($, _, Backbone, wp) {
    33        var media = wp.media, l10n;
    44
     
    9191                 * @global wp.Uploader
    9292                 */
    9393                initialize: function() {
     94                        var self = this;
    9495                        _.defaults( this.options, {
    9596                                title:     l10n.mediaLibraryTitle,
    9697                                modal:     false,
     
    140141                        this.createStates();
    141142                        this.bindHandlers();
    142143                        this.render();
     144
     145                        // Set up the Backbone router after a brief delay
     146                        _.delay( function(){
     147                                wp.media.mediarouter = new media.view.Frame.Router( self );
     148                                // Verify pushState support and activate
     149                                if ( window.history && window.history.pushState ) {
     150                                        Backbone.history.start({
     151                                                root: mediaGridSettings.adminUrl,
     152                                                pushState: true
     153                                        });
     154                                }
     155                        }, 250);
     156
     157                        // Update the URL when entering search string (at most once per second)
     158                        $( '#media-search-input' ).on( 'input', _.debounce( function() {
     159                                var $val = $( this ).val();
     160                                wp.media.mediarouter.navigate( wp.media.mediarouter.baseUrl( ( '' == $val ) ? '' : ( '?search=' + $val ) ) );
     161                        }, 1000 ) );
    143162                },
    144163
    145164                createSelection: function() {
     
    218237                 * Open the Edit Attachment modal.
    219238                 */
    220239                editAttachment: function( model ) {
    221                         var library = this.state().get('library');
     240                        var self    = this,
     241                                library = this.state().get('library');
    222242
    223243                        // Create a new EditAttachment frame, passing along the library and the attachment model.
    224                         this.editAttachmentFrame = new media.view.Frame.EditAttachment({
     244                        this.editAttachmentFrame = new media.view.Frame.EditAttachments({
    225245                                library:        library,
    226246                                model:          model
    227247                        });
     
    229249                        // Listen to events on the edit attachment frame for triggering pagination callback handlers.
    230250                        this.listenTo( this.editAttachmentFrame, 'edit:attachment:next', this.editNextAttachment );
    231251                        this.listenTo( this.editAttachmentFrame, 'edit:attachment:previous', this.editPreviousAttachment );
     252                        // Listen to keyboard events on the modal
     253                        $( 'body' ).on( 'keydown.media-modal', function( e ) {
     254                                self.editAttachmentFrame.keyEvent( e );
     255                        } );
    232256                },
    233257
    234258                /**
     
    300324        });
    301325
    302326        /**
     327         * A router for handling the browser history and application state
     328         */
     329        media.view.Frame.Router = Backbone.Router.extend({
     330
     331                mediaFrame: '',
     332
     333                initialize: function( mediaFrame ){
     334                        this.mediaFrame = mediaFrame;
     335                },
     336
     337                routes: {
     338                        'upload.php?item=:slug':    'showitem',
     339                        'upload.php?search=:query': 'search',
     340                        ':default':                 'defaultRoute'
     341                },
     342
     343                // Map routes against the page URL
     344                baseUrl: function( url ) {
     345                        return 'upload.php' + url;
     346                },
     347
     348                // Respond to the search route by filling the search field and trigggering the input event
     349                search: function( query ) {
     350                        // Ensure modal closed, see back button
     351                        this.closeModal();
     352                        $( '#media-search-input' ).val( query ).trigger( 'input' );
     353                },
     354
     355                // Show the modal with a specific item
     356                showitem: function( query ) {
     357                        var library = this.mediaFrame.state().get('library');
     358
     359                        // Remove existing modal if present
     360                        this.closeModal();
     361                        // Trigger the media frame to open the correct item
     362                        this.mediaFrame.trigger( 'edit:attachment', library.findWhere( { id: parseInt( query, 10 ) } ) );
     363                },
     364
     365                // Close the modal if set up
     366                closeModal: function() {
     367                        if ( 'undefined' !== typeof this.mediaFrame.editAttachmentFrame ) {
     368                                this.mediaFrame.editAttachmentFrame.modal.close();
     369                        }
     370                },
     371
     372                // Default route: make sure the modal and search are reset
     373                defaultRoute: function() {
     374                        this.closeModal();
     375                        $( '#media-search-input' ).val( '' ).trigger( 'input' );
     376                }
     377        });
     378
     379        /**
    303380         * A frame for editing the details of a specific media item.
    304381         *
    305382         * Opens in a modal by default.
     
    306383         *
    307384         * Requires an attachment model to be passed in the options hash under `model`.
    308385         */
    309         media.view.Frame.EditAttachment = media.view.Frame.extend({
     386        media.view.Frame.EditAttachments = media.view.Frame.extend({
    310387
    311388                className: 'edit-attachment-frame',
    312389                template: media.template( 'edit-attachment-frame' ),
     
    328405                                state: 'edit-attachment'
    329406                        });
    330407
     408                        this.library = this.options.library;
     409                        if ( this.options.model ) {
     410                                this.model = this.options.model;
     411                        } else {
     412                                this.model = this.library.at( 0 );
     413                        }
     414
    331415                        this.createStates();
    332416
    333417                        this.on( 'content:render:edit-metadata', this.editMetadataContent, this );
     
    334418                        this.on( 'content:render:edit-image', this.editImageContentUgh, this );
    335419
    336420                        // Only need a tab to Edit Image for images.
    337                         if ( this.model.get( 'type' ) === 'image' ) {
     421                        if ( 'undefined' !== typeof this.model && this.model.get( 'type' ) === 'image' ) {
    338422                                this.on( 'router:create', this.createRouter, this );
    339423                                this.on( 'router:render', this.browseRouter, this );
    340424                        }
     
    352436                                // Completely destroy the modal DOM element when closing it.
    353437                                this.modal.close = function() {
    354438                                        self.modal.remove();
     439                                        $( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */
     440                                        // Reset the browser URL
     441                                        self.resetRoute();
    355442                                };
    356443
    357444                                this.modal.content( this );
     
    391478                                model:      this.model
    392479                        });
    393480                        this.content.set( view );
     481                        // Update browser url when navigating media details
     482                        wp.media.mediarouter.navigate( wp.media.mediarouter.baseUrl( '?item=' + this.model.id ) );
    394483                },
    395484
    396485                /**
     
    461550                                return;
    462551                        this.modal.close();
    463552                        this.trigger( 'edit:attachment:next', this.model );
     553                },
     554
     555                getCurrentIndex: function() {
     556                        return this.library.indexOf( this.model );
     557                },
     558
     559                hasNext: function() {
     560                        return ( this.getCurrentIndex() + 1 ) < this.library.length;
     561                },
     562
     563                hasPrevious: function() {
     564                        return ( this.getCurrentIndex() - 1 ) > -1;
     565                },
     566                /**
     567                 * Respond to the keyboard events: right arrow, left arrow, escape.
     568                 */
     569                keyEvent: function( event ) {
     570                        var $target = $( event.target );
     571                        // Pressing the escape key routes back to main url
     572                        if ( event.keyCode === 27 ) {
     573                                this.resetRoute();
     574                                return event;
     575                        }
     576                        //Don't go left/right if we are in a textarea or input field
     577                        if ( $target.is( 'input' ) || $target.is( 'textarea' ) ) {
     578                                return event;
     579                        }
     580                        // The right arrow key
     581                        if ( event.keyCode === 39 ) {
     582                                if ( ! this.hasNext ) { return; }
     583                                _.debounce( this.nextMediaItem(), 250 );
     584                        }
     585                        // The left arrow key
     586                        if ( event.keyCode === 37 ) {
     587                                if ( ! this.hasPrevious ) { return; }
     588                                _.debounce( this.previousMediaItem(), 250 );
     589                        }
     590                },
     591
     592                resetRoute: function() {
     593                        wp.media.mediarouter.navigate( wp.media.mediarouter.baseUrl( '' ) );
     594                        return;
    464595                }
    465596        });
    466597