WordPress.org

Make WordPress Core

Changeset 22437


Ignore:
Timestamp:
11/07/12 20:14:41 (3 years ago)
Author:
koopersmith
Message:

Media: Integrate the gallery workflow with the media menu.

wp.media.model.Query.more()

  • If a request has already been sent out for more attachments, return that request object instead of creating another.

wp.media.controller.Region

  • A region allows views to be swapped in and out of a section of the page without either view having to know about the other.
  • Application components can use the same callbacks and resources by leveraging Region.mode(), which triggers a set of callbacks to create or transform the current view, but only if necessary.

wp.media.view.Frame

  • Leverage Region controllers instead of forcing states to swap view objects, which causes states to fit more comfortably in the controller-camp.
  • Add previous(), a method to fetch the previous state id.
  • Separate out the default settings over several objects (so blank frames can be instantiated).

wp.media.view.MediaFrame

  • The base Frame used for media management: handles integration with the Modal and UploaderWindow views.

wp.media.view.MediaFrame.Post

  • Includes all default media states and callbacks necessary for inserting media into a post.

see #21390.

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/js/media-upload.js

    r22320 r22437  
    117117            }, this ); 
    118118 
    119             workflow.get('gallery').on( 'update', function( selection ) { 
     119            workflow.get('gallery-edit').on( 'update', function( selection ) { 
    120120                var view = wp.mce.view.get('gallery'), 
    121121                    shortcode; 
     
    126126                shortcode = view.gallery.shortcode( selection ); 
    127127                this.insert( shortcode.string() ); 
    128  
    129                 // Reset the workflow view to the library. 
    130                 workflow.render('library'); 
    131128            }, this ); 
    132129 
  • trunk/wp-includes/css/media-views.css

    r22362 r22437  
    133133    right: 0; 
    134134    bottom: 61px; 
    135     width: 247px; 
     135    width: 267px; 
    136136    padding: 0 16px; 
    137137    z-index: 75; 
     
    141141 
    142142.hide-sidebar .media-sidebar { 
    143     right: -280px; 
     143    right: -300px; 
    144144} 
    145145 
     
    219219} 
    220220 
     221.media-menu > a.button { 
     222    margin: 0 20px; 
     223} 
     224 
    221225/** 
    222226 * Frame 
     
    226230} 
    227231 
    228 .media-frame .media-content { 
     232.media-frame .region-content { 
    229233    position: absolute; 
    230234    top: 0; 
    231235    left: 200px; 
    232     right: 280px; 
     236    right: 300px; 
    233237    bottom: 61px; 
    234238    height: auto; 
     
    238242} 
    239243 
    240 .media-frame.hide-sidebar .media-content { 
    241     right: 0; 
    242 } 
    243  
    244 .media-frame.hide-toolbar .media-content { 
     244.media-frame.hide-sidebar .region-content { 
     245    right: 0; 
     246} 
     247 
     248.media-frame.hide-toolbar .region-content { 
    245249    bottom: 0; 
    246250} 
     
    579583} 
    580584 
    581 .media-content.uploader-inline { 
     585.region-content.uploader-inline { 
    582586    margin: 20px; 
    583587    padding: 20px; 
     
    800804 */ 
    801805 
    802 .attachment-display-settings, 
    803 .button div.attachment-display-settings { 
    804     padding: 0 1em 1em; 
    805 } 
    806  
    807 .attachment-display-settings h3 { 
    808     font-weight: 200; 
    809     margin: 1.4em 0 0.4em; 
    810 } 
    811  
    812806.attachment-display-settings h4 { 
    813807    margin: 1.4em 0 0.4em; 
  • trunk/wp-includes/js/mce-view.js

    r22348 r22437  
    695695 
    696696                this.frame = wp.media({ 
    697                     state:     'gallery', 
     697                    state:     'gallery-edit', 
    698698                    title:     mceview.l10n.editGallery, 
    699699                    editing:   true, 
     
    713713 
    714714                // Update the `shortcode` and `attachments`. 
    715                 this.frame.get('gallery').on( 'update', function( selection ) { 
     715                this.frame.get('gallery-edit').on( 'update', function( selection ) { 
    716716                    var view = mceview.get('gallery'); 
    717717 
  • trunk/wp-includes/js/media-models.js

    r22415 r22437  
    1515     */ 
    1616    media = wp.media = function( attributes ) { 
    17         if ( media.view.Frame ) 
    18             return new media.view.Frame( attributes ).render().attach().open(); 
     17        if ( media.view.MediaFrame.Post ) 
     18            return new media.view.MediaFrame.Post( attributes ).render().attach().open(); 
    1919    }; 
    2020 
     
    498498            var query = this; 
    499499 
     500            if ( this._more && 'pending' === this._more.state() ) 
     501                return this._more; 
     502 
    500503            if ( ! this.hasMore ) 
    501504                return $.Deferred().resolve().promise(); 
     
    504507            options.add = true; 
    505508 
    506             return this.fetch( options ).done( function( resp ) { 
     509            return this._more = this.fetch( options ).done( function( resp ) { 
    507510                if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) 
    508511                    query.hasMore = false; 
  • trunk/wp-includes/js/media-views.js

    r22397 r22437  
    5454 
    5555    /** 
     56     * wp.media.controller.Region 
     57     */ 
     58    media.controller.Region = function( options ) { 
     59        _.extend( this, _.pick( options || {}, 'id', 'controller' ) ); 
     60 
     61        this.on( 'empty', this.empty, this ); 
     62        this.mode('empty'); 
     63    }; 
     64 
     65    // Use Backbone's self-propagating `extend` inheritance method. 
     66    media.controller.Region.extend = Backbone.Model.extend; 
     67 
     68    _.extend( media.controller.Region.prototype, Backbone.Events, { 
     69        trigger: function( id ) { 
     70            this._mode = id; 
     71            return Backbone.Events.trigger.apply( this, arguments ); 
     72        }, 
     73 
     74        mode: function( mode ) { 
     75            if ( mode ) 
     76                return this.trigger( mode ); 
     77            return this._mode; 
     78        }, 
     79 
     80        view: function( view ) { 
     81            var previous = this._view, 
     82                mode = this._mode, 
     83                id = this.id; 
     84 
     85            // If no argument is provided, return the current view. 
     86            if ( ! view ) 
     87                return previous; 
     88 
     89            // If we're attempting to switch to the current view, bail. 
     90            if ( view === previous ) 
     91                return; 
     92 
     93            // Add classes to the new view. 
     94            if ( id ) 
     95                view.$el.addClass( 'region-' + id ); 
     96 
     97            if ( mode ) 
     98                view.$el.addClass( 'mode-' + mode ); 
     99 
     100            // Remove the hide class. 
     101            // this.$el.removeClass( 'hide-' + subview ); 
     102 
     103            if ( previous ) { 
     104                // Fire the view's `destroy` event if it exists. 
     105                if ( previous.destroy ) 
     106                    previous.destroy(); 
     107                // Undelegate events. 
     108                previous.undelegateEvents(); 
     109 
     110                // Replace the view in place. 
     111                previous.$el.replaceWith( view.$el ); 
     112            } 
     113 
     114            this._view = view; 
     115        }, 
     116 
     117        empty: function() { 
     118            this.view( new Backbone.View() ); 
     119        } 
     120    }); 
     121 
     122    /** 
    56123     * wp.media.controller.StateMachine 
    57124     */ 
     
    63130    media.controller.StateMachine.extend = Backbone.Model.extend; 
    64131 
    65     _.extend( media.controller.StateMachine.prototype, { 
     132    // Add events to the `StateMachine`. 
     133    _.extend( media.controller.StateMachine.prototype, Backbone.Events, { 
     134 
    66135        // Fetch a state model. 
    67136        // 
     
    89158            previous = this.state(); 
    90159 
    91             // Bail if we're trying to select the current state, or a state 
     160            // Bail if we're trying to select the current state, if we haven't 
     161            // created the `states` collection, or are trying to select a state 
    92162            // that does not exist. 
    93             if ( previous && id === previous.id || ! this.states.get( id ) ) 
     163            if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) 
    94164                return; 
    95165 
    96             if ( previous ) 
     166            if ( previous ) { 
    97167                previous.trigger('deactivate'); 
     168                this._previous = previous.id; 
     169            } 
    98170 
    99171            this._state = id; 
    100172            this.state().trigger('activate'); 
     173        }, 
     174 
     175        previous: function() { 
     176            return this._previous; 
    101177        } 
    102178    }); 
     
    123199            this.on( 'deactivate', this._deactivate, this ); 
    124200            this.on( 'deactivate', this.deactivate, this ); 
     201            this.on( 'reset', this.reset, this ); 
    125202        }, 
    126203 
     
    140217        }, 
    141218 
     219        reset: function() {}, 
     220 
    142221        menu: function() { 
    143             var menu = this.get('menu'); 
    144  
    145             if ( ! menu ) 
     222            var menu = this.frame.menu, 
     223                mode = this.get('menu'), 
     224                view; 
     225 
     226            if ( ! mode ) 
    146227                return; 
    147228 
    148             this.frame.menu( menu ); 
    149             menu.select( this.id ); 
    150         }, 
    151  
    152         toolbar: function() {}, 
    153         sidebar: function() {}, 
    154         content: function() {} 
     229            if ( menu.mode() !== mode ) 
     230                menu.mode( mode ); 
     231 
     232            view = menu.view(); 
     233            if ( view.select ) 
     234                view.select( this.id ); 
     235        } 
     236    }); 
     237 
     238    _.each(['toolbar','sidebar','content'], function( region ) { 
     239        media.controller.State.prototype[ region ] = function() { 
     240            var mode = this.get( region ); 
     241            if ( mode ) 
     242                this.frame[ region ].mode( mode ); 
     243        }; 
    155244    }); 
    156245 
     
    162251            multiple: false, 
    163252            describe: false, 
    164             title:    l10n.mediaLibraryTitle 
     253            toolbar:  'main-attachments', 
     254            sidebar:  'settings' 
    165255        }, 
    166256 
     
    194284            selection.on( 'selection:single', this.buildDetails, this ); 
    195285            selection.on( 'selection:unsingle', this.clearDetails, this ); 
    196             selection.on( 'add remove reset', this.updateToolbarVisibility, this ); 
     286            selection.on( 'add remove reset', this.refreshToolbar, this ); 
    197287 
    198288            this._updateEmpty(); 
     
    212302        }, 
    213303 
    214         toolbar: function() { 
     304        reset: function() { 
     305            this.get('selection').clear(); 
     306        }, 
     307 
     308        sidebar: function() { 
     309            media.controller.State.prototype.sidebar.apply( this, arguments ); 
     310            this.details(); 
     311        }, 
     312 
     313        content: function() { 
    215314            var frame = this.frame; 
    216  
    217             frame.toolbar( new media.view.Toolbar.PostLibrary({ 
    218                 controller: frame 
    219             }) ); 
    220         }, 
    221  
    222         sidebar: function() { 
    223             var frame = this.frame; 
    224  
    225             // Sidebar. 
    226             frame.sidebar( new media.view.Sidebar({ 
    227                 controller: frame 
    228             }) ); 
    229  
    230             this.details(); 
    231         }, 
    232  
    233         content: function() { 
    234             var frame = this.frame, 
    235                 library = this.get('library'), 
    236                 view; 
    237315 
    238316            // Content. 
    239317            if ( this.get('empty') ) { 
    240318                // Attempt to fetch any Attachments we don't already have. 
    241                 library.more(); 
     319                this.get('library').more(); 
    242320 
    243321                // In the meantime, render an inline uploader. 
    244                 view = new media.view.UploaderInline({ 
    245                     controller: frame 
    246                 }); 
     322                frame.content.mode('upload'); 
    247323            } else { 
    248324                // Browse our library of attachments. 
    249                 view = new media.view.AttachmentsBrowser({ 
    250                     controller: frame, 
    251                     collection: library, 
    252                     model:      this 
    253                 }); 
     325                frame.content.mode('browse'); 
    254326            } 
    255  
    256             frame.content( view.render() ); 
    257327        }, 
    258328 
     
    263333 
    264334        _updateEmpty: function() { 
    265             this.set( 'empty', ! this.get('library').length ); 
    266         }, 
    267  
    268         updateToolbarVisibility: function() { 
    269             this.frame.toolbar().visibility(); 
     335            var library = this.get('library'); 
     336            this.set( 'empty', ! library.length && ! library.props.get('search') ); 
     337        }, 
     338 
     339        refreshToolbar: function() { 
     340            this.frame.toolbar.view().refresh(); 
    270341        }, 
    271342 
     
    280351 
    281352        buildDetails: function( model ) { 
    282             this.frame.sidebar().add( 'details', new media.view.Attachment.Details({ 
    283                 controller: this.frame, 
     353            var frame = this.frame; 
     354            frame.sidebar.view().add( 'details', new media.view.Attachment.Details({ 
     355                controller: frame, 
    284356                model:      model, 
    285357                priority:   80 
     
    292364                return this; 
    293365 
    294             this.frame.sidebar().add( 'details', new Backbone.View({ 
     366            this.frame.sidebar.view().add( 'details', new Backbone.View({ 
    295367                priority: 80 
    296368            }).render() ); 
     
    347419    media.controller.Gallery = media.controller.Library.extend({ 
    348420        defaults: { 
    349             id:         'gallery', 
     421            id:         'gallery-edit', 
    350422            multiple:   false, 
    351423            describe:   true, 
    352             title:      l10n.createGalleryTitle, 
    353424            edge:       199, 
    354             editing:    false 
    355         }, 
    356  
    357         toolbar: function() { 
    358             var frame = this.frame; 
    359             frame.toolbar( new media.view.Toolbar.Gallery({ 
    360                 controller: frame 
    361             }) ); 
     425            editing:    false, 
     426            sortable:   true, 
     427            toolbar:    'gallery-edit', 
     428            sidebar:    'settings' 
     429        }, 
     430 
     431        initialize: function() { 
     432            // The single `Attachment` view to be used in the `Attachments` view. 
     433            if ( ! this.get('AttachmentView') ) 
     434                this.set( 'AttachmentView', media.view.Attachment.Gallery ); 
     435            media.controller.Library.prototype.initialize.apply( this, arguments ); 
    362436        }, 
    363437 
     
    365439            var frame = this.frame; 
    366440 
    367             // Sidebar. 
    368             frame.sidebar( new media.view.Sidebar({ 
    369                 controller: frame 
    370             }) ); 
    371  
     441            media.controller.State.prototype.sidebar.apply( this, arguments ); 
    372442            this.details(); 
    373             frame.sidebar().add({ 
     443 
     444            frame.sidebar.view().add({ 
    374445                settings: new media.view.Settings.Gallery({ 
    375446                    controller: frame, 
     
    378449                }).render() 
    379450            }); 
    380         }, 
    381  
    382         content: function() { 
    383             this.frame.content( new media.view.Attachments({ 
    384                 controller: this.frame, 
    385                 collection: this.get('library'), 
    386                 model:      this, 
    387                 sortable:   true, 
    388                 // The single `Attachment` view to be used in the `Attachments` view. 
    389                 AttachmentView: media.view.Attachment.Gallery 
    390             }).render() ); 
    391         } 
    392     }); 
    393  
    394     media.controller.GalleryAddImages = media.controller.Library.extend({ 
    395         defaults: { 
    396             id:       'gallery:add', 
    397             multiple: true, 
    398             title:    l10n.createGalleryTitle 
    399         }, 
    400  
    401         initialize: function() { 
    402             if ( ! this.get('library') ) 
    403                 this.set( 'library', media.query({ type: 'image' }) ); 
    404             return media.controller.Library.prototype.initialize.apply( this, arguments ); 
    405         }, 
    406  
    407         toolbar: function() { 
    408             var frame = this.frame; 
    409             frame.toolbar( new media.view.Toolbar.GalleryAddImages({ 
    410                 controller: frame 
    411             }) ); 
    412         }, 
    413  
    414         // Leave the sidebar. 
    415         sidebar: function() {} 
     451        } 
    416452    }); 
    417453 
     
    426462     */ 
    427463    media.view.Frame = Backbone.View.extend({ 
    428         tagName:   'div', 
    429         className: 'media-frame', 
    430         template:  media.template('media-frame'), 
    431464 
    432465        initialize: function() { 
    433             _.defaults( this.options, { 
    434                 state:     'upload', 
    435                 title:     '', 
    436                 selection: [], 
    437                 library:   {}, 
    438                 modal:     true, 
    439                 multiple:  false, 
    440                 uploader:  true, 
    441                 editing:   false 
    442             }); 
    443  
    444             this.createSelection(); 
    445             this.createSubviews(); 
    446             this.createStates(); 
    447         }, 
    448  
    449         render: function() { 
    450             var els = [  this.menu().el, this.content().el, this.sidebar().el, this.toolbar().el ]; 
    451  
    452             if ( this.modal ) 
    453                 this.modal.render(); 
    454  
    455             // Detach any views that will be rebound to maintain event bindings. 
    456             this.$el.children().filter( els ).detach(); 
    457             this.$el.empty().append( els ); 
    458  
    459             // Render the window uploader if it exists. 
    460             if ( this.uploader ) 
    461                 this.uploader.render().$el.appendTo( this.$el ); 
    462  
    463             return this; 
    464         }, 
    465  
    466         createSelection: function() { 
    467             var controller = this, 
    468                 selection = this.options.selection; 
    469  
    470             if ( ! (selection instanceof media.model.Selection) ) { 
    471                 selection = this.options.selection = new media.model.Selection( selection, { 
    472                     multiple: this.options.multiple 
     466            this._createRegions(); 
     467            this._createStates(); 
     468        }, 
     469 
     470        _createRegions: function() { 
     471            // Clone the regions array. 
     472            this.regions = this.regions ? this.regions.slice() : []; 
     473 
     474            // Initialize regions. 
     475            _.each( this.regions, function( region ) { 
     476                this[ region ] = new media.controller.Region({ 
     477                    controller: this, 
     478                    id:         region 
    473479                }); 
    474             } 
    475         }, 
    476  
    477         createStates: function() { 
    478             var options = this.options, 
    479                 menus = { 
    480                     landing: new media.view.Menu.Landing({ 
    481                         controller: this 
    482                     }) 
    483                 }; 
    484  
     480            }, this ); 
     481        }, 
     482 
     483        _createStates: function() { 
    485484            // Create the default `states` collection. 
    486485            this.states = new Backbone.Collection(); 
     
    490489                model.frame = this; 
    491490            }, this ); 
    492  
    493             // Add the default states. 
    494             this.states.add([ 
    495                 new media.controller.Library({ 
    496                     selection: options.selection, 
    497                     library:   media.query( options.library ), 
    498                     multiple:  this.options.multiple, 
    499                     menu:      menus.landing 
    500                 }), 
    501                 new media.controller.Upload({ 
    502                     multiple: this.options.multiple, 
    503                     menu:     menus.landing 
    504                 }), 
    505                 new media.controller.Gallery({ 
    506                     library: options.selection, 
    507                     editing: options.editing 
    508                 }), 
    509                 new media.controller.GalleryAddImages() 
    510             ]); 
    511  
    512             // Set the default state. 
    513             this.state( options.state ); 
    514         }, 
    515  
    516         createSubviews: function() { 
    517             // Initialize a stub view for each subview region. 
    518             _.each(['menu','content','sidebar','toolbar'], function( subview ) { 
    519                 this[ '_' + subview ] = new Backbone.View({ 
    520                     tagName:   'div', 
    521                     className: 'media-' + subview 
    522                 }); 
    523             }, this ); 
     491        }, 
     492 
     493        render: function() { 
     494            var els = _.map( this.regions, function( region ) { 
     495                    return this[ region ].view().el; 
     496                }, this ); 
     497 
     498            // Detach the current views to maintain event bindings. 
     499            $( els ).detach(); 
     500            this.$el.html( els ); 
     501 
     502            return this; 
     503        }, 
     504 
     505        reset: function() { 
     506            this.states.invoke( 'trigger', 'reset' ); 
     507        } 
     508    }); 
     509 
     510    // Make the `Frame` a `StateMachine`. 
     511    _.extend( media.view.Frame.prototype, media.controller.StateMachine.prototype ); 
     512 
     513    /** 
     514     * wp.media.view.MediaFrame 
     515     */ 
     516    media.view.MediaFrame = media.view.Frame.extend({ 
     517        className: 'media-frame', 
     518        regions:   ['menu','content','sidebar','toolbar'], 
     519 
     520        initialize: function() { 
     521            media.view.Frame.prototype.initialize.apply( this, arguments ); 
     522 
     523            _.defaults( this.options, { 
     524                title:    '', 
     525                modal:    true, 
     526                uploader: true 
     527            }); 
    524528 
    525529            // Initialize modal container view. 
     
    540544                }); 
    541545            } 
    542         } 
    543     }); 
    544  
    545     // Make the `Frame` a `StateMachine`. 
    546     _.extend( media.view.Frame.prototype, media.controller.StateMachine.prototype ); 
    547  
    548     // Create methods to fetch and replace individual subviews. 
    549     _.each(['menu','content','sidebar','toolbar'], function( subview ) { 
    550         media.view.Frame.prototype[ subview ] = function( view ) { 
    551             var previous = this[ '_' + subview ]; 
    552  
    553             if ( ! view ) 
    554                 return previous; 
    555  
    556             if ( view === previous ) 
    557                 return; 
    558  
    559             view.$el.addClass( 'media-' + subview ); 
    560  
    561             // Remove the hide class. 
    562             this.$el.removeClass( 'hide-' + subview ); 
    563  
    564             if ( previous.destroy ) 
    565                 previous.destroy(); 
    566             previous.undelegateEvents(); 
    567             previous.$el.replaceWith( view.$el ); 
    568             this[ '_' + subview ] = view; 
    569         }; 
     546        }, 
     547 
     548        render: function() { 
     549            if ( this.modal ) 
     550                this.modal.render(); 
     551 
     552            media.view.Frame.prototype.render.apply( this, arguments ); 
     553 
     554            // Render the window uploader if it exists. 
     555            if ( this.uploader ) 
     556                this.uploader.render().$el.appendTo( this.$el ); 
     557 
     558            return this; 
     559        } 
    570560    }); 
    571561 
    572562    // Map some of the modal's methods to the frame. 
    573563    _.each(['open','close','attach','detach'], function( method ) { 
    574         media.view.Frame.prototype[ method ] = function( view ) { 
     564        media.view.MediaFrame.prototype[ method ] = function( view ) { 
    575565            if ( this.modal ) 
    576566                this.modal[ method ].apply( this.modal, arguments ); 
    577567            return this; 
    578568        }; 
     569    }); 
     570 
     571 
     572    /** 
     573     * wp.media.view.MediaFrame.Post 
     574     */ 
     575    media.view.MediaFrame.Post = media.view.MediaFrame.extend({ 
     576        initialize: function() { 
     577            media.view.MediaFrame.prototype.initialize.apply( this, arguments ); 
     578 
     579            _.defaults( this.options, { 
     580                state:     'upload', 
     581                selection: [], 
     582                library:   {}, 
     583                multiple:  false, 
     584                editing:   false 
     585            }); 
     586 
     587            this.bindHandlers(); 
     588            this.createSelection(); 
     589            this.createStates(); 
     590        }, 
     591 
     592        bindHandlers: function() { 
     593            this.menu.on( 'main', this.mainMenu, this ); 
     594            this.menu.on( 'batch', this.batchMenu, this ); 
     595            this.menu.on( 'gallery', this.galleryMenu, this ); 
     596 
     597            this.content.on( 'browse', this.browseContent, this ); 
     598            this.content.on( 'upload', this.uploadContent, this ); 
     599            this.content.on( 'embed', this.embedContent, this ); 
     600 
     601            this.sidebar.on( 'settings', this.settingsSidebar, this ); 
     602            this.sidebar.on( 'attachment-settings', this.attachmentSettingsSidebar, this ); 
     603 
     604            this.toolbar.on( 'main-attachments', this.mainAttachmentsToolbar, this ); 
     605            this.toolbar.on( 'main-embed', this.mainEmbedToolbar, this ); 
     606            this.toolbar.on( 'batch-edit', this.batchEditToolbar, this ); 
     607            this.toolbar.on( 'batch-add', this.batchAddToolbar, this ); 
     608            this.toolbar.on( 'gallery-edit', this.galleryEditToolbar, this ); 
     609            this.toolbar.on( 'gallery-add', this.galleryAddToolbar, this ); 
     610        }, 
     611 
     612        createSelection: function() { 
     613            var controller = this, 
     614                selection = this.options.selection; 
     615 
     616            if ( ! (selection instanceof media.model.Selection) ) { 
     617                selection = this.options.selection = new media.model.Selection( selection, { 
     618                    multiple: this.options.multiple 
     619                }); 
     620            } 
     621        }, 
     622 
     623        createStates: function() { 
     624            var options = this.options; 
     625 
     626            // Add the default states. 
     627            this.states.add([ 
     628                new media.controller.Library({ 
     629                    selection: options.selection, 
     630                    library:   media.query( options.library ), 
     631                    multiple:  this.options.multiple, 
     632                    menu:      'main', 
     633                    sidebar:   'attachment-settings' 
     634                }), 
     635 
     636                new media.controller.Upload({ 
     637                    multiple: this.options.multiple, 
     638                    menu:      'main', 
     639                    sidebar:   'attachment-settings' 
     640                }), 
     641 
     642                new media.controller.Gallery({ 
     643                    editing: options.editing, 
     644                    menu:    'gallery' 
     645                }), 
     646 
     647                new media.controller.Library({ 
     648                    id:        'gallery-library', 
     649                    library:   media.query({ type: 'image' }), 
     650                    multiple:  true, 
     651                    menu:      'gallery', 
     652                    toolbar:   'gallery-add' 
     653                }), 
     654 
     655                new media.controller.Upload({ 
     656                    id:        'gallery-upload', 
     657                    multiple:  true, 
     658                    menu:      'gallery', 
     659                    toolbar:   'gallery-add' 
     660                }) 
     661            ]); 
     662 
     663            this.get('gallery-edit').on( 'change:library', this.updateGalleryLibraries, this ).set({ 
     664                library: options.selection 
     665            }); 
     666 
     667            // Set the default state. 
     668            this.state( options.state ); 
     669        }, 
     670 
     671        updateGalleryLibraries: function() { 
     672            var editLibrary = this.get('gallery-edit').get('library'); 
     673 
     674            _.each(['gallery-library','gallery-upload'], function( id ) { 
     675                var state = this.get( id ), 
     676                    original = state.get('_library'), 
     677                    skeleton; 
     678 
     679                // Remember the state's original library. 
     680                if ( ! original ) 
     681                    state.set( '_library', original = state.get('library') ); 
     682 
     683                // Create a skeleton library in its place. 
     684                skeleton = new Attachments( null, { 
     685                    props: _.pick( original.props.toJSON(), 'order', 'orderby' ) 
     686                }); 
     687 
     688                // Rejects attachments that do not exist in the original library 
     689                // or that do exist edit state's library. 
     690                skeleton.filters.difference = function( attachment ) { 
     691                    return ! original.getByCid( attachment.cid ) || !! editLibrary.getByCid( attachment.cid ); 
     692                }; 
     693 
     694                skeleton.evaluate = function( attachment ) { 
     695                    var valid = ! this.validator( attachment ), 
     696                        inSkeleton = !! this.getByCid( attachment.cid ); 
     697 
     698                    if ( ! valid && inSkeleton ) 
     699                        this.remove( attachment ); 
     700                    else if ( valid && ! inSkeleton ) 
     701                        this.add( attachment ).sort(); 
     702 
     703                    return this; 
     704                }; 
     705 
     706                skeleton.evaluateAll = function ( attachments ) { 
     707                    _.each( attachments.models, this.evaluate, this ); 
     708                    return this; 
     709                }; 
     710 
     711                skeleton.on( 'add remove', skeleton.evaluate, skeleton ); 
     712                skeleton.on( 'reset', skeleton.evaluateAll, skeleton ); 
     713                editLibrary.on( 'add remove', skeleton.evaluate, skeleton ); 
     714                editLibrary.on( 'reset', skeleton.evaluateAll, skeleton ); 
     715 
     716                // Mirror the original library. 
     717                skeleton.mirror( original ); 
     718 
     719                // Ensure we've evaluated everything in the edit library. 
     720                skeleton.evaluateAll( editLibrary ); 
     721 
     722                state.set( 'library', skeleton ); 
     723            }, this ); 
     724        }, 
     725 
     726        // Menus 
     727        mainMenu: function() { 
     728            this.menu.view( new media.view.Menu({ 
     729                controller: this, 
     730                views: { 
     731                    upload: { 
     732                        text: l10n.uploadFilesTitle, 
     733                        priority: 20 
     734                    }, 
     735                    library: { 
     736                        text: l10n.mediaLibraryTitle, 
     737                        priority: 40 
     738                    }, 
     739                    separateLibrary: new Backbone.View({ 
     740                        className: 'separator', 
     741                        priority: 60 
     742                    }), 
     743                    embed: { 
     744                        text: l10n.embedFromUrlTitle, 
     745                        priority: 80 
     746                    } 
     747                } 
     748            }) ); 
     749        }, 
     750 
     751        batchMenu: function() {}, 
     752 
     753        galleryMenu: function() { 
     754            var previous = this.previous(), 
     755                frame = this; 
     756 
     757            this.menu.view( new media.view.Menu({ 
     758                controller: this, 
     759                views: { 
     760                    cancel: { 
     761                        text:     l10n.cancelGalleryTitle, 
     762                        priority: 20, 
     763                        click:    function() { 
     764                            if ( previous ) 
     765                                frame.state( previous ); 
     766                            else 
     767                                frame.close(); 
     768                        } 
     769                    }, 
     770                    separateCancel: new Backbone.View({ 
     771                        className: 'separator', 
     772                        priority: 40 
     773                    }), 
     774                    'gallery-edit': { 
     775                        text: l10n.editGalleryTitle, 
     776                        priority: 60 
     777                    }, 
     778                    'gallery-upload': { 
     779                        text: l10n.uploadImagesTitle, 
     780                        priority: 80 
     781                    }, 
     782                    'gallery-library': { 
     783                        text: l10n.mediaLibraryTitle, 
     784                        priority: 100 
     785                    } 
     786                } 
     787            }) ); 
     788 
     789        }, 
     790 
     791        // Content 
     792        browseContent: function() { 
     793            var state = this.state(); 
     794 
     795            // Browse our library of attachments. 
     796            this.content.view( new media.view.AttachmentsBrowser({ 
     797                controller: this, 
     798                collection: state.get('library'), 
     799                model:      state, 
     800                sortable:   state.get('sortable'), 
     801 
     802                AttachmentView: state.get('AttachmentView') 
     803            }).render() ); 
     804        }, 
     805 
     806        uploadContent: function() { 
     807            // In the meantime, render an inline uploader. 
     808            this.content.view( new media.view.UploaderInline({ 
     809                controller: this 
     810            }).render() ); 
     811        }, 
     812 
     813        embedContent: function() {}, 
     814 
     815        // Sidebars 
     816        settingsSidebar: function() { 
     817            this.sidebar.view( new media.view.Sidebar({ 
     818                controller: this 
     819            }) ); 
     820        }, 
     821 
     822        attachmentSettingsSidebar: function() { 
     823            this.sidebar.view( new media.view.Sidebar({ 
     824                controller: this, 
     825                views: { 
     826                    settings: new media.view.Settings.AttachmentDisplay({ 
     827                        controller: this, 
     828                        priority:   20 
     829                    }).render() 
     830                } 
     831            }) ); 
     832        }, 
     833 
     834        // Toolbars 
     835        mainAttachmentsToolbar: function() { 
     836            this.toolbar.view( new media.view.Toolbar.Insert.Post({ 
     837                controller: this 
     838            }) ); 
     839        }, 
     840 
     841        mainEmbedToolbar: function() {}, 
     842        batchEditToolbar: function() {}, 
     843        batchAddToolbar: function() {}, 
     844 
     845        galleryEditToolbar: function() { 
     846            var editing = this.state().get('editing'); 
     847            this.toolbar.view( new media.view.Toolbar({ 
     848                controller: this, 
     849                items: { 
     850                    insert: { 
     851                        style:    'primary', 
     852                        text:     editing ? l10n.updateGallery : l10n.insertGallery, 
     853                        priority: 80, 
     854 
     855                        click: function() { 
     856                            var controller = this.controller, 
     857                                state = controller.state(); 
     858 
     859                            controller.close(); 
     860                            state.trigger( 'update', state.get('library') ); 
     861 
     862                            controller.reset(); 
     863                            // @todo: Make the state activated dynamic (instead of hardcoded). 
     864                            controller.state('upload'); 
     865                        } 
     866                    } 
     867                } 
     868            }) ); 
     869        }, 
     870 
     871        galleryAddToolbar: function() { 
     872            this.toolbar.view( new media.view.Toolbar({ 
     873                controller: this, 
     874                items: { 
     875                    insert: { 
     876                        style:    'primary', 
     877                        text:     l10n.addToGallery, 
     878                        priority: 80, 
     879 
     880                        click: function() { 
     881                            var controller = this.controller, 
     882                                state = controller.state(), 
     883                                edit = controller.get('gallery-edit'); 
     884 
     885                            edit.get('library').add( state.get('selection').models ); 
     886                            state.trigger('reset'); 
     887                            controller.state('gallery-edit'); 
     888                        } 
     889                    } 
     890                } 
     891            }) ); 
     892        } 
    579893    }); 
    580894 
     
    8101124        }, 
    8111125 
     1126        destroy: function() { 
     1127            _.each( this._views, function( view ) { 
     1128                if ( view.destroy ) 
     1129                    view.destroy(); 
     1130            }); 
     1131        }, 
     1132 
    8121133        render: function() { 
    8131134            var views = _.chain( this._views ).sortBy( function( view ) { 
     
    8231144            this.$secondary.html( _.pluck( views.secondary || [], 'el' ) ); 
    8241145 
     1146            this.refresh(); 
     1147 
    8251148            return this; 
    8261149        }, 
     
    8621185                this.render(); 
    8631186            return this; 
    864         } 
    865     }); 
    866  
    867     // wp.media.view.Toolbar.PostLibrary 
     1187        }, 
     1188 
     1189        refresh: function() {} 
     1190    }); 
     1191 
     1192    // wp.media.view.Toolbar.Insert 
    8681193    // --------------------------------- 
    869     media.view.Toolbar.PostLibrary = media.view.Toolbar.extend({ 
     1194    media.view.Toolbar.Insert = media.view.Toolbar.extend({ 
    8701195        initialize: function() { 
    8711196            var controller = this.options.controller, 
    8721197                selection = controller.state().get('selection'); 
    8731198 
    874             this.options.items = { 
     1199            this.options.items = _.defaults( this.options.items || {}, { 
    8751200                selection: new media.view.Selection({ 
    8761201                    controller: controller, 
     
    8791204                }).render(), 
    8801205 
    881                 'create-new-gallery': { 
     1206                insert: { 
    8821207                    style:    'primary', 
     1208                    priority: 80, 
     1209                    text:     l10n.insertIntoPost, 
     1210 
     1211                    click: function() { 
     1212                        controller.close(); 
     1213                        controller.state().trigger( 'insert', selection ); 
     1214                        selection.clear(); 
     1215                    } 
     1216                } 
     1217            }); 
     1218 
     1219            media.view.Toolbar.prototype.initialize.apply( this, arguments ); 
     1220        }, 
     1221 
     1222        refresh: function() { 
     1223            var selection = this.controller.state().get('selection'); 
     1224            this.get('insert').model.set( 'disabled', ! selection.length ); 
     1225        } 
     1226    }); 
     1227 
     1228    // wp.media.view.Toolbar.Insert.Post 
     1229    // --------------------------------- 
     1230    media.view.Toolbar.Insert.Post = media.view.Toolbar.Insert.extend({ 
     1231        initialize: function() { 
     1232            this.options.items = _.defaults( this.options.items || {}, { 
     1233                gallery: { 
    8831234                    text:     l10n.createNewGallery, 
    8841235                    priority: 40, 
    8851236 
    8861237                    click: function() { 
    887                         this.controller.state('gallery'); 
     1238                        var controller = this.controller, 
     1239                            selection = controller.state().get('selection'), 
     1240                            edit = controller.get('gallery-edit'); 
     1241 
     1242                        edit.set( 'library', new media.model.Selection( selection.models, { 
     1243                            props:    selection.props.toJSON(), 
     1244                            multiple: true 
     1245                        }) ); 
     1246 
     1247                        this.controller.state('gallery-edit'); 
    8881248                    } 
    8891249                }, 
    8901250 
    891                 'insert-into-post': new media.view.ButtonGroup({ 
    892                     priority: 30, 
    893                     classes:  'dropdown-flip-x dropdown-flip-y', 
    894                     buttons:  [ 
    895                         { 
    896                             text:  l10n.insertIntoPost, 
    897                             click: function() { 
    898                                 controller.close(); 
    899                                 controller.state().trigger( 'insert', selection ); 
    900                                 selection.clear(); 
    901                             } 
    902                         }, 
    903                         { 
    904                             classes:  ['down-arrow'], 
    905                             dropdown: new media.view.Settings.AttachmentDisplay().render().$el, 
    906  
    907                             click: function( event ) { 
    908                                 var $el = this.$el; 
    909  
    910                                 if ( ! $( event.target ).closest('.dropdown').length ) 
    911                                     $el.toggleClass('active'); 
    912  
    913                                 // Stop the event from propagating further so we can bind 
    914                                 // a one-time event to the body (and ensure that a click 
    915                                 // on the dropdown won't trigger said event). 
    916                                 event.stopPropagation(); 
    917  
    918                                 if ( $el.is(':visible') ) { 
    919                                     $(document.body).one( 'click', function() { 
    920                                         $el.removeClass('active'); 
    921                                     }); 
    922                                 } 
    923                             } 
    924                         } 
    925                     ] 
    926                 }).render(), 
    927  
    928                 'add-to-gallery': { 
    929                     text:     l10n.addToGallery, 
    930                     priority: 20 
    931                 } 
    932             }; 
    933  
    934             media.view.Toolbar.prototype.initialize.apply( this, arguments ); 
    935             this.visibility(); 
    936         }, 
    937  
    938         visibility: function() { 
    939             var selection = this.controller.state().get('selection'), 
    940                 count = selection.length, 
    941                 showGallery; 
    942  
    943             // Check if every attachment in the selection is an image. 
    944             showGallery = count > 1 && selection.all( function( attachment ) { 
    945                 return 'image' === attachment.get('type'); 
    946             }); 
    947  
    948             this.get('create-new-gallery').$el.toggle( showGallery ); 
    949             insert = this.get('insert-into-post'); 
    950             _.each( insert.buttons, function( button ) { 
    951                 button.model.set( 'style', showGallery ? '' : 'primary' ); 
    952             }); 
    953  
    954             _.first( insert.buttons ).model.set( 'disabled', ! count ); 
    955         } 
    956     }); 
    957  
    958     // wp.media.view.Toolbar.Gallery 
    959     // ----------------------------- 
    960     media.view.Toolbar.Gallery = media.view.Toolbar.extend({ 
    961         initialize: function() { 
    962             var controller = this.options.controller, 
    963                 state = controller.state(), 
    964                 editing = state.get('editing'), 
    965                 library = state.get('library'); 
    966  
    967             this.options.items = { 
    968                 update: { 
    969                     style:    'primary', 
    970                     text:     editing ? l10n.updateGallery : l10n.insertGallery, 
    971                     priority: 40, 
    972                     click:    function() { 
    973                         var state = controller.state(); 
    974                         controller.close(); 
    975                         state.trigger( 'update', state.get('library') ); 
    976                         controller.get('library').get('selection').clear(); 
    977                         controller.state('library'); 
    978                     } 
    979                 }, 
    980  
    981                 addImages: { 
    982                     text:     l10n.addImages, 
    983                     priority: -40, 
     1251                batch: { 
     1252                    text:     l10n.batchInsert, 
     1253                    priority: 60, 
    9841254 
    9851255                    click: function() { 
    986                         controller.get('gallery:add').set( 'selection', new media.model.Selection( library.models, { 
    987                             props:    controller.state().get('library').props.toJSON(), 
    988                             multiple: true 
    989                         }) ); 
    990                         controller.state('gallery:add'); 
    991                     } 
    992                 }, 
    993  
    994                 cancel: { 
    995                     text:     l10n.cancel, 
    996                     priority: -60, 
    997  
    998                     click: function() { 
    999                         if ( editing ) 
    1000                             controller.close(); 
    1001                         else 
    1002                             controller.state('library'); 
     1256                        this.controller.state('batch-edit'); 
    10031257                    } 
    10041258                } 
    1005             }; 
    1006  
    1007             media.view.Toolbar.prototype.initialize.apply( this, arguments ); 
    1008         } 
    1009     }); 
    1010  
    1011     // wp.media.view.Toolbar.GalleryAddImages 
    1012     // ----------------------------- 
    1013     media.view.Toolbar.GalleryAddImages = media.view.Toolbar.extend({ 
    1014         initialize: function() { 
    1015             var controller = this.options.controller; 
    1016  
    1017             this.options.items = { 
    1018                 update: { 
    1019                     style:    'primary', 
    1020                     text:     l10n.continueEditing, 
    1021                     priority: 40, 
    1022  
    1023                     click: function() { 
    1024                         controller.get('gallery').set( 'library', controller.state().get('selection') ); 
    1025                         controller.state('gallery'); 
    1026                     } 
    1027                 }, 
    1028  
    1029                 cancel: { 
    1030                     text:     l10n.cancel, 
    1031                     priority: -60, 
    1032  
    1033                     click: function() { 
    1034                         controller.state('gallery'); 
    1035                     } 
    1036                 } 
    1037             }; 
    1038  
    1039             media.view.Toolbar.prototype.initialize.apply( this, arguments ); 
     1259            }); 
     1260 
     1261            media.view.Toolbar.Insert.prototype.initialize.apply( this, arguments ); 
     1262        }, 
     1263 
     1264        refresh: function() { 
     1265            var selection = this.controller.state().get('selection'), 
     1266                count = selection.length; 
     1267 
     1268            // Call the parent's `refresh()` method. 
     1269            media.view.Toolbar.Insert.prototype.refresh.apply( this, arguments ); 
     1270 
     1271            // Check if every attachment in the selection is an image. 
     1272            this.get('gallery').$el.toggle( count > 1 && selection.all( function( attachment ) { 
     1273                return 'image' === attachment.get('type'); 
     1274            }) ); 
     1275 
     1276            // Batch insert shows for multiple selected attachments. 
     1277            // Temporarily disabled with `false &&`. 
     1278            this.get('batch').$el.toggle( false && count > 1 ); 
     1279 
     1280            // Insert only shows for single attachments. 
     1281            // Temporarily disabled. 
     1282            // this.get('insert').$el.toggle( count <= 1 ); 
    10401283        } 
    10411284    }); 
     
    11611404        }, 
    11621405 
     1406        destroy: function() { 
     1407            _.each( this._views, function( view ) { 
     1408                if ( view.destroy ) 
     1409                    view.destroy(); 
     1410            }); 
     1411        }, 
     1412 
    11631413        render: function() { 
    11641414            var els = _( this._views ).chain().sortBy( function( view ) { 
     
    12251475        toView: function( options, id ) { 
    12261476            options = options || {}; 
    1227             options.id = id; 
     1477            options.id = options.id || id; 
    12281478            return new media.view.MenuItem( options ).render(); 
    12291479        }, 
     
    12491499 
    12501500        events: { 
    1251             'click': 'toState' 
    1252         }, 
    1253  
    1254         toState: function() { 
    1255             this.controller.state( this.options.id ); 
     1501            'click': 'click' 
     1502        }, 
     1503 
     1504        click: function() { 
     1505            var options = this.options; 
     1506            if ( options.click ) 
     1507                options.click.call( this ); 
     1508            else if ( options.id ) 
     1509                this.controller.state( options.id ); 
    12561510        }, 
    12571511 
     
    12681522    }); 
    12691523 
    1270     media.view.Menu.Landing = media.view.Menu.extend({ 
    1271         views: { 
    1272             upload: { 
    1273                 text: l10n.uploadFilesTitle, 
    1274                 priority: 20 
    1275             }, 
    1276             library: { 
    1277                 text: l10n.mediaLibraryTitle, 
    1278                 priority: 40 
    1279             }, 
    1280             separateLibrary: new Backbone.View({ 
    1281                 className: 'separator', 
    1282                 priority: 60 
    1283             }), 
    1284             embed: { 
    1285                 text: l10n.embedFromUrlTitle, 
    1286                 priority: 80 
    1287             } 
    1288         } 
    1289     }); 
    1290  
    12911524    /** 
    12921525     * wp.media.view.Sidebar 
     
    13291562        destroy: function() { 
    13301563            this.model.off( null, null, this ); 
     1564            this.$el.off( 'click', 'a', this.preventDefault ); 
    13311565        }, 
    13321566 
     
    17031937                search: true, 
    17041938                upload: false, 
    1705                 total:  true 
     1939                total:  true, 
     1940 
     1941                AttachmentView: media.view.Attachment.Library 
    17061942            }); 
    17071943 
     
    17231959                model:      this.model, 
    17241960                sortable:   this.options.sortable, 
     1961 
    17251962                // The single `Attachment` view to be used in the `Attachments` view. 
    1726                 AttachmentView: media.view.Attachment.Library 
     1963                AttachmentView: this.options.AttachmentView 
    17271964            }); 
    17281965        }, 
     
    18692106     */ 
    18702107    media.view.Settings = Backbone.View.extend({ 
    1871         tagName:   'div', 
    1872         className: 'attachment-display-settings', 
    1873         template:  media.template('attachment-display-settings'), 
    1874  
    18752108        events: { 
    18762109            'click button':    'updateHandler', 
     
    18962129            this.model.validate = function( attrs ) { 
    18972130                return _.any( attrs, function( value, key ) { 
    1898                     return ! settings[ key ] || ! _.contains( settings[ key ].accepts, value ); 
     2131                    // If we don't have a `setting` for the `key`, assume the 
     2132                    // `value` is valid. Otherwise, check if the `value` exists 
     2133                    // in the `setting.accepts` array. 
     2134                    return settings[ key ] && ! _.contains( settings[ key ].accepts, value ); 
    18992135                }); 
    19002136            }; 
  • trunk/wp-includes/media.php

    r22415 r22437  
    14151415 
    14161416    <script type="text/html" id="tmpl-attachment-display-settings"> 
     1417        <h3><?php _e('Attachment Display Settings'); ?></h3> 
     1418 
    14171419        <h4><?php _e('Alignment'); ?></h4> 
    14181420        <div class="alignment button-group button-large" data-setting="align"> 
     
    14461448 
    14471449    <script type="text/html" id="tmpl-gallery-settings"> 
     1450        <h3><?php _e('Gallery Settings'); ?></h3> 
     1451 
    14481452        <h4><?php _e('Link To'); ?></h4> 
    14491453        <div class="link-to button-group" data-setting="link"> 
     
    14591463 
    14601464        <select class="columns" name="columns" data-setting="columns"> 
    1461             <?php for( $i = 1; $i <= 9; $i++ ) : ?> 
     1465            <?php for ( $i = 1; $i <= 9; $i++ ) : ?> 
    14621466                <option value="<?php echo esc_attr( $i ); ?>"> 
    14631467                    <?php echo esc_html( $i ); ?> 
  • trunk/wp-includes/script-loader.php

    r22378 r22437  
    331331 
    332332        // Upload 
    333         'uploadFilesTitle' => __( 'Upload Files' ), 
    334         'selectFiles'      => __( 'Select files' ), 
     333        'uploadFilesTitle'  => __( 'Upload Files' ), 
     334        'selectFiles'       => __( 'Select files' ), 
     335        'uploadImagesTitle' => __( 'Upload Images' ), 
    335336 
    336337        // Library 
     
    343344        'embedFromUrlTitle' => __( 'Embed From URL' ), 
    344345 
     346        // Batch 
     347        'batchInsert'      => __( 'Batch insert' ), 
     348        'cancelBatchTitle' => __( '&#8592; Cancel Batch' ), 
     349 
    345350        // Gallery 
    346351        'createGalleryTitle' => __( 'Create Gallery' ), 
     352        'editGalleryTitle'   => __( 'Edit Gallery' ), 
     353        'cancelGalleryTitle' => __( '&#8592; Cancel Gallery' ), 
    347354        'insertGallery'      => __( 'Insert gallery' ), 
    348355        'updateGallery'      => __( 'Update gallery' ), 
Note: See TracChangeset for help on using the changeset viewer.