WordPress.org

Make WordPress Core

Changeset 22437


Ignore:
Timestamp:
11/07/2012 08:14:41 PM (6 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.