Make WordPress Core

Changeset 29057


Ignore:
Timestamp:
07/10/2014 06:36:34 AM (9 years ago)
Author:
helen
Message:

Media grid attachment modal:

  • Keyboard navigation.
  • History and routes for single items and search results.

props adamsilverstein. see #24716.

Location:
trunk/src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/upload.php

    r28993 r29057  
    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' );
  • trunk/src/wp-includes/js/media-grid.js

    r29056 r29057  
    1 /* global _wpMediaViewsL10n, setUserSetting, deleteUserSetting, MediaElementPlayer */
     1/* global _wpMediaViewsL10n, setUserSetting, deleteUserSetting, MediaElementPlayer, mediaGridSettings*/
    22(function($, _, Backbone, wp) {
    33    var media = wp.media, l10n;
     
    9292         */
    9393        initialize: function() {
     94            var self = this;
    9495            _.defaults( this.options, {
    9596                title:     l10n.mediaLibraryTitle,
     
    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
     
    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
     
    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
     
    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     *
     
    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',
     
    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
     
    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 );
     
    353437                this.modal.close = function() {
    354438                    self.modal.remove();
     439                    $( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */
    355440                };
    356441
     
    392477            });
    393478            this.content.set( view );
     479            // Update browser url when navigating media details
     480            wp.media.mediarouter.navigate( wp.media.mediarouter.baseUrl( '?item=' + this.model.id ) );
    394481        },
    395482
     
    462549            this.modal.close();
    463550            this.trigger( 'edit:attachment:next', this.model );
     551        },
     552
     553        getCurrentIndex: function() {
     554            return this.library.indexOf( this.model );
     555        },
     556
     557        hasNext: function() {
     558            return ( this.getCurrentIndex() + 1 ) < this.library.length;
     559        },
     560
     561        hasPrevious: function() {
     562            return ( this.getCurrentIndex() - 1 ) > -1;
     563        },
     564        /**
     565         * Respond to the keyboard events: right arrow, left arrow, escape.
     566         */
     567        keyEvent: function( event ) {
     568            var $target = $( event.target );
     569            // Pressing the escape key routes back to main url
     570            if ( event.keyCode === 27 ) {
     571                this.resetRoute();
     572                return event;
     573            }
     574            //Don't go left/right if we are in a textarea or input field
     575            if ( $target.is( 'input' ) || $target.is( 'textarea' ) ) {
     576                return event;
     577            }
     578            // The right arrow key
     579            if ( event.keyCode === 39 ) {
     580                if ( ! this.hasNext ) { return; }
     581                _.debounce( this.nextMediaItem(), 250 );
     582            }
     583            // The left arrow key
     584            if ( event.keyCode === 37 ) {
     585                if ( ! this.hasPrevious ) { return; }
     586                _.debounce( this.previousMediaItem(), 250 );
     587            }
     588        },
     589
     590        resetRoute: function() {
     591            wp.media.mediarouter.navigate( wp.media.mediarouter.baseUrl( '' ) );
     592            return;
    464593        }
    465594    });
Note: See TracChangeset for help on using the changeset viewer.