Make WordPress Core

Ticket #24716: 24716.31.diff

File 24716.31.diff, 8.9 KB (added by adamsilverstein, 10 years ago)

improved search routing

  • 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
     
    120120                 * @global wp.Uploader
    121121                 */
    122122                initialize: function() {
     123                        var self = this;
    123124                        _.defaults( this.options, {
    124125                                title:     l10n.mediaLibraryTitle,
    125126                                modal:     false,
     
    168169                        this.createStates();
    169170                        this.bindHandlers();
    170171                        this.render();
     172
     173                        // Set up the Backbone router after a brief delay
     174                        _.delay( function(){
     175                                wp.media.mediarouter = new media.view.Frame.Router( self );
     176                                // Verify pushState support and activate
     177                                if ( window.history && window.history.pushState ) {
     178                                        Backbone.history.start({
     179                                                root: mediaGridSettings.adminUrl,
     180                                                pushState: true
     181                                        });
     182                                }
     183                        }, 150);
     184
     185                        // Update the URL when entering search string (at most once per second)
     186                        $( '#media-search-input' ).on( 'input', _.debounce( function() {
     187                                var $val = $( this ).val();
     188                                wp.media.mediarouter.navigate( wp.media.mediarouter.baseUrl( ( '' == $val ) ? '' : ( '?search=' + $val ) ) );
     189                        }, 1000 ) );
    171190                },
    172191
    173192                createSelection: function() {
     
    213232
    214233                        // Handle a frame-level event for editing an attachment.
    215234                        this.on( 'edit:attachment', this.editAttachment, this );
    216                         this.on( 'edit:attachment:next', this.editNextAttachment, this );
    217                         this.on( 'edit:attachment:previous', this.editPreviousAttachment, this );
    218235                },
    219236
    220                 editPreviousAttachment: function( currentModel ) {
    221                         var library = this.state().get('library'),
    222                                 currentModelIndex = library.indexOf( currentModel );
    223                         this.trigger( 'edit:attachment', library.at( currentModelIndex - 1 ) );
    224                 },
    225 
    226                 editNextAttachment: function( currentModel ) {
    227                         var library = this.state().get('library'),
    228                                 currentModelIndex = library.indexOf( currentModel );
    229                         this.trigger( 'edit:attachment', library.at( currentModelIndex + 1 ) );
    230                 },
    231 
    232237                /**
    233238                 * Open the Edit Attachment modal.
    234239                 */
    235240                editAttachment: function( model ) {
    236                         var library = this.state().get('library'), hasPrevious, hasNext;
    237                         if ( library.indexOf( model ) > 0 ) {
    238                                 hasPrevious = true;
    239                         }
    240                         else {
    241                                 hasPrevious = false;
    242                         }
    243                         if ( library.indexOf( model ) < library.length - 1 ) {
    244                                 hasNext = true;
    245                         }
    246                         else {
    247                                 hasNext = false;
    248                         }
    249 
    250                         new media.view.Frame.EditAttachment({
    251                                 hasPrevious:    hasPrevious,
    252                                 hasNext:        hasNext,
    253                                 model:          model,
    254                                 gridController: this
     241                        var self = this;
     242                        // Create a new EditAttachment frame, passing along the library.
     243                        this.editAttachmentFrame = new media.view.Frame.EditAttachments({
     244                                library: this.state().get('library'),
     245                                model: model
    255246                        });
     247                        $( 'body' ).on( 'keydown.media-modal', function( e ) {
     248                                self.editAttachmentFrame.keyEvent( e );
     249                                } );
    256250                },
    257251
    258252                /**
     
    324318        });
    325319
    326320        /**
     321         * A router for handling the browser history and application state
     322         */
     323        media.view.Frame.Router = Backbone.Router.extend({
     324
     325                mediaFrame: '',
     326
     327                initialize: function( mediaFrame ){
     328                        this.mediaFrame = mediaFrame;
     329                },
     330
     331                routes: {
     332                        'upload.php?item=:slug':    'showitem',
     333                        'upload.php?search=:query': 'search',
     334                        ':default':                 'defaultRoute'
     335                },
     336
     337                // Map routes against the page URL
     338                baseUrl: function( url ) {
     339                        return 'upload.php' + url;
     340                },
     341
     342                // Respond to the search route by filling the search field and trigggering the input event
     343                search: function( query ) {
     344                        // Ensure modal closed, see back button
     345                        this.closeModal();
     346                        $( '#media-search-input' ).val( query ).trigger( 'input' );
     347                },
     348
     349                // Show the modal with a specific item
     350                showitem: function( query ) {
     351                        var library = this.mediaFrame.state().get('library');
     352
     353                        // Remove existing modal if present
     354                        this.closeModal();
     355                        // Trigger the media frame to open the correct item
     356                        this.mediaFrame.trigger( 'edit:attachment', library.findWhere( { id: parseInt( query, 10 ) } ) );
     357                },
     358
     359                // Close the modal if set up
     360                closeModal: function() {
     361                        if ( 'undefined' !== typeof this.mediaFrame.editAttachmentFrame ) {
     362                                this.mediaFrame.editAttachmentFrame.modal.close();
     363                        }
     364                },
     365
     366                // Default route: make sure the modal and search are reset
     367                defaultRoute: function() {
     368                        this.closeModal();
     369                        $( '#media-search-input' ).val( '' ).trigger( 'input' );
     370                }
     371        });
     372
     373        /**
    327374         * A frame for editing the details of a specific media item.
    328375         *
    329376         * Opens in a modal by default.
     
    330377         *
    331378         * Requires an attachment model to be passed in the options hash under `model`.
    332379         */
    333         media.view.Frame.EditAttachment = media.view.Frame.extend({
     380        media.view.Frame.EditAttachments = media.view.Frame.extend({
    334381
    335382                className: 'edit-attachment-frame',
    336383                template: media.template( 'edit-attachment-frame' ),
     
    352399                                state: 'edit-attachment'
    353400                        });
    354401
     402                        this.library = this.options.library;
     403                        if ( this.options.model ) {
     404                                this.model = this.options.model;
     405                        } else {
     406                                this.model = this.library.at( 0 );
     407                        }
     408
    355409                        this.createStates();
    356410
    357411                        this.on( 'content:render:edit-metadata', this.editMetadataContent, this );
     
    358412                        this.on( 'content:render:edit-image', this.editImageContentUgh, this );
    359413
    360414                        // Only need a tab to Edit Image for images.
    361                         if ( this.model.get( 'type' ) === 'image' ) {
     415                        if ( 'undefined' !== typeof this.model && this.model.get( 'type' ) === 'image' ) {
    362416                                this.on( 'router:create', this.createRouter, this );
    363417                                this.on( 'router:render', this.browseRouter, this );
    364418                        }
     
    373427                                // Completely destroy the modal DOM element when closing it.
    374428                                this.modal.close = function() {
    375429                                        self.modal.remove();
     430                                        $( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */
     431                                        // Reset the browser URL
     432                                        self.resetRoute();
    376433                                };
    377434
    378435                                this.modal.content( this );
     
    412469                                model:      this.model
    413470                        });
    414471                        this.content.set( view );
     472                        // Update browser url when navigating media details
     473                        wp.media.mediarouter.navigate( wp.media.mediarouter.baseUrl( '?item=' + this.model.id ) );
    415474                },
    416475
    417476                /**
     
    464523                        });
    465524                },
    466525
     526                getCurrentIndex: function() {
     527                        return this.library.indexOf( this.model );
     528                },
     529
     530                hasNext: function() {
     531                        return ( this.getCurrentIndex() + 1 ) < this.library.length;
     532                },
     533
     534                hasPrevious: function() {
     535                        return ( this.getCurrentIndex() - 1 ) > -1;
     536                },
     537
    467538                /**
    468539                 * Click handler to switch to the previous media item.
    469540                 */
    470541                previousMediaItem: function() {
    471                         if ( ! this.options.hasPrevious )
     542                        if ( ! this.hasPrevious() ) {
    472543                                return;
    473                         this.modal.close();
    474                         this.options.gridController.trigger( 'edit:attachment:previous', this.model );
     544                        }
     545                        this.model = this.library.at( this.getCurrentIndex() - 1 );
     546                        this.editMetadataContent();
    475547                },
    476548
    477549                /**
     
    478550                 * Click handler to switch to the next media item.
    479551                 */
    480552                nextMediaItem: function() {
    481                         if ( ! this.options.hasNext )
     553                        if ( ! this.hasNext() ) {
    482554                                return;
    483                         this.modal.close();
    484                         this.options.gridController.trigger( 'edit:attachment:next', this.model );
     555                        }
     556                        this.model = this.library.at( this.getCurrentIndex() + 1 );
     557                        this.editMetadataContent();
     558                },
     559                /**
     560                 * Respond to the keyboard events: right arrow, left arrow, escape.
     561                 */
     562                keyEvent: function( event ) {
     563                        var $target = $( event.target );
     564
     565                        // Pressing the escape key routes back to main url
     566                        if ( event.keyCode === 27 ) {
     567                                this.resetRoute();
     568                                return event;
     569                        }
     570
     571                        //Don't go left/right if we are in a textarea or input field
     572                        if ( $target.is( 'input' ) || $target.is( 'textarea' ) ) {
     573                                return event;
     574                        }
     575
     576                        // The right arrow key
     577                        if ( event.keyCode === 39 ) {
     578                                if ( ! this.hasNext ) { return; }
     579                                _.debounce( this.nextMediaItem(), 250 );
     580                        }
     581
     582                        // The left arrow key
     583                        if ( event.keyCode === 37 ) {
     584                                if ( ! this.hasPrevious ) { return; }
     585                                _.debounce( this.previousMediaItem(), 250 );
     586                        }
     587                },
     588
     589                resetRoute: function() {
     590                        wp.media.mediarouter.navigate( wp.media.mediarouter.baseUrl( '' ) );
     591                        return;
    485592                }
    486 
    487593        });
    488594
    489595        media.view.GridFieldOptions = media.View.extend({