Make WordPress Core

Changeset 22362


Ignore:
Timestamp:
11/04/2012 10:59:12 PM (14 years ago)
Author:
koopersmith
Message:

Streamlining media, part I.

The main goal here is to rearrange the media components in a modularized structure to support more linear workflows. This is that structure using the pre-existing workflows, which will be improved over the course of the next few commits.

This leaves a few pieces a bit rough around the edges: namely gallery editing and selecting a featured image.

The fine print follows.


Styles

  • Tightened padding around the modal to optimize for a smaller default screen size.
  • Added a light dashed line surrounding the modal to provide a subtle cue for the persistent dropzone (which is evolving into a power user feature since we now have a dedicated upload state).
  • Add a size for hero buttons.
  • Remove transitions from frame subviews (e.g. menu, content, sidebar, toolbar).

Code

wp.media.controller.StateManager

  • Don't fire activate and deactivate if attempting to switch to the current state.

wp.media.controller.State

  • Add a base state class to bind default methods (as not all states will inherit from the Library state).
  • On activate, fire activate(), menu(), content(), sidebar(), and toolbar().
  • The menu view is often a shared object (as its most common use case is switching between states). Assign the view to the state's menu attribute.
  • menu() automatically fetches the state's menu attribute, attaches the menu view to the frame, and attempts to select a menu item that matches the state's id.

wp.media.controller.Library

  • Now inherits from wp.media.controller.State.

wp.media.controller.Upload

  • A new state to improve the upload experience.
  • Displays a large dropzone when empty (a UploaderInline view).
  • When attachments are uploaded, displays management interface (a library state restricted to attachments uploaded during the current session).

wp.media.view.Frame

  • In menu(), content(), sidebar(), and toolbar(), only change the view if it differs from the current view. Also, ensure hide-* classes are properly removed.

*

wp.media.view.PriorityList

  • A new container view used to sort and render child views by the priority property.
  • Used by wp.media.view.Sidebar and wp.media.view.Menu.
  • Next step: Use two instances to power wp.media.view.Toolbar.

wp.media.view.Menu and wp.media.view.MenuItem

  • A new PriorityList view that renders a list of views used to switch between states.
  • MenuItem instances have id attributes that are tied directly to states.
  • Separators can be added as plain Backbone.View instances with the separator class.
  • Supports any type of Backbone.View.

media.view.Menu.Landing

  • The landing menu for the 'insert media' workflow.
  • Includes an inactive link to an "Embed from URL" state.
  • Next steps: only use in select cases to allot for other workflows (such as featured images).

wp.media.view.AttachmentsBrowser

  • A container to render an Attachments view with accompanying UI controls (similar to what the Attachments view was when it contained the $list property).
  • Currently only renders a Search view as a control.
  • Next steps: Add optional view counts (e.g. "21 images"), upload buttons, and collection filter UI.

wp.media.view.Attachments

  • If the Attachments scroll buffer is not filled with Attachment views, continue loading more attachments.
  • Use this.model instead of this.controller.state() to allow Attachments views to have differing edge and gutter properties.
  • Add edge(), a method used to calculate the optimal dimensions for an attachment based on the current width of the Attachments container element.
  • edge() is currently only enabled on resize, as the relative positioning and CSS transforms used to center thumbnails are suboptimal when coupled with frequent resizing.
  • Next steps: For infinite scroll performance improvements, look into absolutely positioning attachment views and paging groups of attachment views.

wp.media.view.UploaderWindow

  • Now generates a $browser element as the browse button (instead of a full UploaderInline view). Using a portable browse button prevents us from having to create a new wp.Uploader instance every time we want access to a browse button.

wp.media.view.UploaderInline

  • No longer directly linked to the UploaderWindow view or its wp.Uploader instance.
  • Used as the default upload state view.

wp.media.view.Selection

  • An interactive representation of the selected Attachments.
  • Based on the improved workflows, this is likely overkill. For simplicity's sake, will probably remove this in favor of SelectionPreview.

see #21390.

Location:
trunk/wp-includes
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-includes/css/buttons.css

    r22314 r22362  
    8282}
    8383
     84.button.button-hero,
     85.button-group.button-hero .button {
     86        font-size: 14px;
     87        height: 46px;
     88        line-height: 44px;
     89        padding: 0 36px;
     90}
     91
    8492.button:active {
    8593        outline: none;
    … …  
    295303        top: 100%;
    296304        left: 0;
    297         margin-top: 5px;
     305        margin: 5px 0;
    298306        padding: 0.8em 1em;
    299307        border-radius: 3px;
    … …  
    315323        right: 0;
    316324}
     325
     326.dropdown-flip-y .dropdown {
     327        top: auto;
     328        bottom: 100%;
     329}
  • trunk/wp-includes/css/media-views.css

    r22343 r22362  
    44.media-modal {
    55        position: fixed;
    6         top: 80px;
    7         left: 60px;
    8         right: 60px;
    9         bottom: 60px;
     6        top: 60px;
     7        left: 40px;
     8        right: 40px;
     9        bottom: 40px;
    1010        z-index: 125000;
    1111}
    … …  
    2020        opacity: 0.8;
    2121        z-index: 120000;
     22}
     23
     24.media-modal-backdrop div {
     25        position: absolute;
     26        top: 10px;
     27        left: 10px;
     28        right: 10px;
     29        bottom: 10px;
     30        border: 1px dashed rgba( 255, 255, 255, 0.5 );
    2231}
    2332
    … …  
    3948        padding: 0;
    4049        margin: 0;
    41         font-size: 1.4em;
     50        font-size: 16px;
    4251}
    4352
    … …  
    7584        position: absolute;
    7685        top: 0;
    77         left: 220px;
     86        left: 0;
    7887        right: 0;
    7988        z-index: 100;
    80         height: 50px;
    81         padding: 0 10px;
    82         border-bottom: 1px solid #dfdfdf;
     89        height: 60px;
     90        padding: 0 16px;
     91        border: 0 solid #dfdfdf;
     92}
     93
     94.media-frame > .media-toolbar {
     95        top: auto;
     96        left: 200px;
     97        bottom: 0;
     98        border-width: 1px 0 0 0;
     99}
     100
     101.media-frame.hide-toolbar > .media-toolbar {
     102        bottom: -61px;
    83103}
    84104
    … …  
    95115        margin-left: 10px;
    96116        float: left;
    97         margin-top: 10px;
     117        margin-top: 15px;
    98118}
    99119
    … …  
    102122        margin-right: 10px;
    103123        float: left;
    104         margin-top: 10px;
     124        margin-top: 15px;
    105125}
    106126
    … …  
    111131        position: absolute;
    112132        top: 0;
    113         left: 0;
    114         bottom: 0;
    115         width: 219px;
    116         z-index: 50;
     133        right: 0;
     134        bottom: 61px;
     135        width: 247px;
     136        padding: 0 16px;
     137        z-index: 75;
    117138        background: #f5f5f5;
    118         border-right: 1px solid #dfdfdf;
     139        border-left: 1px solid #dfdfdf;
    119140}
    120141
    121142.hide-sidebar .media-sidebar {
    122         display: none;
     143        right: -280px;
     144}
     145
     146.hide-toolbar .media-sidebar {
     147        bottom: 0;
    123148}
    124149
    … …  
    148173
    149174/**
     175 * Menu
     176 */
     177.media-menu {
     178        position: absolute;
     179        top: 0;
     180        left: 0;
     181        bottom: 0;
     182        width: 199px;
     183        margin: 0;
     184        padding: 16px 0;
     185        z-index: 200;
     186        box-shadow: inset -6px 0 6px -6px rgba( 0, 0, 0, 0.4 );
     187}
     188
     189.media-menu li {
     190        position: relative;
     191        padding: 4px 20px;
     192        margin: 0;
     193        line-height: 18px;
     194        font-size: 14px;
     195        color: #21759B;
     196        text-shadow: 0 1px 0 #fff;
     197}
     198
     199.media-menu-item {
     200        cursor: pointer;
     201}
     202
     203.media-menu li:hover {
     204        background: rgba( 0, 0, 0, 0.04 );
     205}
     206
     207.media-menu .active,
     208.media-menu .active:hover {
     209        color: #333;
     210        font-weight: bold;
     211}
     212
     213.media-menu .separator {
     214        height: 0;
     215        margin: 12px 20px;
     216        padding: 0;
     217        border-top: 1px solid #dfdfdf;
     218        border-bottom: 1px solid #fff;
     219}
     220
     221/**
    150222 * Frame
    151223 */
    152 
    153 .media-frame .media-content,
    154 .media-frame .media-toolbar,
    155 .media-frame .media-sidebar {
    156         -webkit-transition-property: left, right, top, bottom, margin;
    157         -moz-transition-property:    left, right, top, bottom, margin;
    158         -ms-transition-property:     left, right, top, bottom, margin;
    159         -o-transition-property:      left, right, top, bottom, margin;
    160         transition-property:         left, right, top, bottom, margin;
    161 
    162         -webkit-transition-duration: 0.2s;
    163         -moz-transition-duration:    0.2s;
    164         -ms-transition-duration:     0.2s;
    165         -o-transition-duration:      0.2s;
    166         transition-duration:         0.2s;
     224.media-frame {
     225        overflow: hidden;
    167226}
    168227
    169228.media-frame .media-content {
    170229        position: absolute;
    171         top: 51px;
    172         left: 220px;
    173         right: 0;
    174         bottom: 0;
     230        top: 0;
     231        left: 200px;
     232        right: 280px;
     233        bottom: 61px;
    175234        height: auto;
    176235        width: auto;
    … …  
    180239
    181240.media-frame.hide-sidebar .media-content {
    182         left: 0;
     241        right: 0;
     242}
     243
     244.media-frame.hide-toolbar .media-content {
     245        bottom: 0;
    183246}
    184247
    … …  
    197260        color: #464646;
    198261        font-family: sans-serif;
     262}
     263
     264/**
     265 * Attachments
     266 */
     267.attachments {
     268        margin: 0;
     269        padding-right: 16px;
    199270}
    200271
    … …  
    389460}
    390461
     462
     463/**
     464 * Attachments Browser
     465 */
     466.media-frame .attachments-browser {
     467        overflow: hidden;
     468}
     469
     470.attachments-browser .media-toolbar {
     471        height: 50px;
     472}
     473
     474.attachments-browser .attachments {
     475        position: absolute;
     476        top: 50px;
     477        left: 0;
     478        right: 0;
     479        bottom: 0;
     480        overflow: auto;
     481}
     482
     483
    391484/**
    392485 * Progress Bar
    … …  
    446539.uploader-window-content {
    447540        position: absolute;
    448         top: 30px;
    449         left: 30px;
    450         right: 30px;
    451         bottom: 30px;
     541        top: 10px;
     542        left: 10px;
     543        right: 10px;
     544        bottom: 10px;
    452545        border: 1px dashed #fff;
    453546}
    … …  
    464557        transform:         translateY( -50% );
    465558
    466         font-size: 18px;
     559        font-size: 20px;
    467560        font-weight: 200;
    468561        color: #fff;
    … …  
    486579}
    487580
    488 .uploader-inline {
    489         display: none;
     581.media-content.uploader-inline {
     582        margin: 20px;
     583        padding: 20px;
     584        border: 1px dashed #aaa;
     585        text-align: center;
     586}
     587
     588.uploader-inline-content {
     589        position: absolute;
     590        top: 30%;
     591        left: 0;
     592        right: 0;
     593}
     594
     595.uploader-inline h3 {
     596        font-size: 20px;
     597        font-weight: 200;
     598        margin-bottom: 1.6em;
    490599}
    491600
    … …  
    498607}
    499608
    500 .media-sidebar .uploader-inline {
    501         display: block;
    502         position: absolute;
    503         left: 0;
    504         right: 0;
    505         bottom: 0;
    506         height: 100px;
    507         margin: 10px;
    508         padding-top: 10px;
    509         text-align: center;
    510         border: 1px dashed #aaa;
    511 }
    512 
    513 .media-sidebar .uploader-inline h3 {
    514         font-weight: 200;
    515         font-size: 16px;
    516         margin: 10px 0;
     609.uploader-inline .browser {
     610        display: inline-block !important;
     611}
     612
     613/**
     614 * Selection
     615 */
     616
     617.media-selection {
     618        position: absolute;
     619        top: 0;
     620        left: 0;
     621        right: 350px;
     622        height: 60px;
     623        padding: 0 0 0 16px;
     624        overflow: hidden;
     625        white-space: nowrap;
     626}
     627
     628.media-selection .selection-info {
     629        display: inline-block;
     630        height: 60px;
     631        margin-right: 10px;
     632        vertical-align: top;
     633}
     634
     635.media-selection.empty {
     636        display: none;
     637}
     638
     639.media-selection .count {
     640        display: block;
     641        padding-top: 12px;
     642        font-size: 14px;
     643        line-height: 20px;
     644        font-weight: bold;
     645}
     646
     647.media-selection .clear-selection {
     648        display: block;
     649        text-decoration: none;
     650        line-height: 16px;
     651}
     652
     653.media-selection .attachments {
     654        display: inline-block;
     655        height: 60px;
     656        margin-top: 5px;
     657        overflow: hidden;
     658        vertical-align: top;
     659}
     660
     661.media-selection .selected.attachment {
     662        box-shadow: none;
     663}
     664
     665.media-selection .details.attachment {
     666        box-shadow:
     667                0 0 0 1px #fff,
     668                0 0 0 3px #1e8cbe;
     669}
     670
     671.media-selection:after {
     672        content: '';
     673        display: block;
     674        position: absolute;
     675        top: 0;
     676        right: 0;
     677        bottom: 0;
     678        width: 25px;
     679        background-image: -webkit-gradient(linear, right top, right top, from( rgba( 255, 255, 255, 1 ) ), to( rgba( 255, 255, 255, 0 ) ));
     680        background-image: -webkit-linear-gradient(right, rgba( 255, 255, 255, 1 ) ,  rgba( 255, 255, 255, 0 ) );
     681        background-image:    -moz-linear-gradient(right, rgba( 255, 255, 255, 1 ) ,  rgba( 255, 255, 255, 0 ) );
     682        background-image:      -o-linear-gradient(right, rgba( 255, 255, 255, 1 ) ,  rgba( 255, 255, 255, 0 ) );
     683        background-image:   linear-gradient(to left, rgba( 255, 255, 255, 1 ) ,  rgba( 255, 255, 255, 0 ) );
     684}
     685
     686.media-selection .attachment .filename {
     687        display: none;
    517688}
    518689
    … …  
    585756 */
    586757
    587 .attachment-details {
    588         padding-top: 20px;
    589 }
    590 
    591758.attachment-details-preview {
    592759        cursor: default;
  • trunk/wp-includes/js/media-views.js

    r22356 r22362  
    8484                        var previous;
    8585
    86                         if ( id ) {
    87                                 if ( previous = this.state() )
    88                                         previous.trigger('deactivate');
    89                                 this._state = id;
    90                                 return this.state().trigger('activate');
    91                         }
    92 
    93                         if ( this._state )
    94                                 return this.get( this._state );
     86                        if ( ! id )
     87                                return this._state ? this.get( this._state ) : null;
     88
     89                        previous = this.state();
     90
     91                        // Bail if we're trying to select the current state, or a state
     92                        // that does not exist.
     93                        if ( previous && id === previous.id || ! this.states.get( id ) )
     94                                return;
     95
     96                        if ( previous )
     97                                previous.trigger('deactivate');
     98
     99                        this._state = id;
     100                        this.state().trigger('activate');
    95101                }
    96102        });
    … …  
    108114        });
    109115
     116
     117        // wp.media.controller.State
     118        // ---------------------------
     119        media.controller.State = Backbone.Model.extend({
     120                initialize: function() {
     121                        this.on( 'activate', this._activate, this );
     122                        this.on( 'activate', this.activate, this );
     123                        this.on( 'deactivate', this._deactivate, this );
     124                        this.on( 'deactivate', this.deactivate, this );
     125                },
     126
     127                activate: function() {},
     128                _activate: function() {
     129                        this.active = true;
     130
     131                        this.menu();
     132                        this.toolbar();
     133                        this.sidebar();
     134                        this.content();
     135                },
     136
     137                deactivate: function() {},
     138                _deactivate: function() {
     139                        this.active = false;
     140                },
     141
     142                menu: function() {
     143                        var menu = this.get('menu');
     144
     145                        if ( ! menu )
     146                                return;
     147
     148                        this.frame.menu( menu );
     149                        menu.select( this.id );
     150                },
     151
     152                toolbar: function() {},
     153                sidebar: function() {},
     154                content: function() {}
     155        });
     156
    110157        // wp.media.controller.Library
    111158        // ---------------------------
    112         media.controller.Library = Backbone.Model.extend({
     159        media.controller.Library = media.controller.State.extend({
    113160                defaults: {
    114161                        id:       'library',
    … …  
    134181                                this.set( 'gutter', 8 );
    135182
    136                         this.on( 'activate', this.activate, this );
    137                         this.on( 'deactivate', this.deactivate, this );
     183                        media.controller.State.prototype.initialize.apply( this, arguments );
    138184                },
    139185
    140186                activate: function() {
    141                         this.toolbar();
    142                         this.sidebar();
    143                         this.content();
    144 
    145187                        // If we're in a workflow that supports multiple attachments,
    146188                        // automatically select any uploading attachments.
    … …  
    154196                deactivate: function() {
    155197                        var toolbar = this._postLibraryToolbar;
     198
    156199                        if ( toolbar )
    157200                                this.get('selection').off( 'add remove', toolbar.visibility, toolbar );
    … …  
    185228
    186229                        this.details();
    187                         frame.sidebar().add({
    188                                 search: new media.view.Search({
    189                                         controller: frame,
    190                                         model:      this.get('library').props,
    191                                         priority:   20
    192                                 }),
    193 
    194                                 selection: new media.view.SelectionPreview({
    195                                         controller: frame,
    196                                         collection: this.get('selection'),
    197                                         priority:   40
    198                                 })
    199                         });
    200230                },
    201231
    … …  
    204234
    205235                        // Content.
    206                         frame.content( new media.view.Attachments({
     236                        frame.content( new media.view.AttachmentsBrowser({
    207237                                controller: frame,
    208238                                collection: this.get('library'),
    209                                 // The single `Attachment` view to be used in the `Attachments` view.
    210                                 AttachmentView: media.view.Attachment.Library
     239                                model:      this
    211240                        }).render() );
    212241                },
    … …  
    253282
    254283                        return this;
     284                }
     285        });
     286
     287
     288        // wp.media.controller.Upload
     289        // ---------------------------
     290        media.controller.Upload = media.controller.Library.extend({
     291                defaults: _.defaults({
     292                        id: 'upload'
     293                }, media.controller.Library.prototype.defaults ),
     294
     295                initialize: function() {
     296                        var library = this.get('library');
     297
     298                        // If a `library` attribute isn't provided, create a new
     299                        // `Attachments` collection that observes (and thereby receives
     300                        // all uploading) attachments.
     301                        if ( ! library ) {
     302                                library = new Attachments();
     303                                library.props.set({
     304                                        orderby: 'date',
     305                                        order:   'ASC'
     306                                });
     307                                library.observe( wp.Uploader.queue );
     308                                this.set( 'library', library );
     309                        }
     310
     311                        media.controller.Library.prototype.initialize.apply( this, arguments );
     312                },
     313
     314                activate: function() {
     315                        this.get('library').on( 'add remove reset', this.refresh, this );
     316                        media.controller.Library.prototype.activate.apply( this, arguments );
     317                        this.refresh();
     318                },
     319
     320                deactivate: function() {
     321                        this.get('library').off( 'add remove reset', this.refresh, this );
     322                        media.controller.Library.prototype.deactivate.apply( this, arguments );
     323                },
     324
     325                refresh: function() {
     326                        this.frame.$el.toggleClass( 'hide-sidebar hide-toolbar', ! this.get('library').length );
     327                        this.content();
     328                },
     329
     330                content: function() {
     331                        var frame = this.frame,
     332                                upload;
     333
     334                        if ( this.get('library').length ) {
     335                                media.controller.Library.prototype.content.apply( this, arguments );
     336                        } else {
     337                                upload = new media.view.UploaderInline({
     338                                        controller: frame
     339                                }).render();
     340
     341                                frame.content( upload );
     342                        }
    255343                }
    256344        });
    … …  
    297385                                controller: this.frame,
    298386                                collection: this.get('library'),
     387                                model:      this,
    299388                                sortable:   true,
    300389                                // The single `Attachment` view to be used in the `Attachments` view.
    … …  
    346435                initialize: function() {
    347436                        _.defaults( this.options, {
    348                                 state:     'library',
     437                                state:     'upload',
    349438                                title:     '',
    350439                                selection: [],
    … …  
    362451
    363452                render: function() {
    364                         var els = [ this.toolbar().el, this.sidebar().el, this.content().el ];
     453                        var els = [  this.menu().el, this.content().el, this.sidebar().el, this.toolbar().el ];
    365454
    366455                        if ( this.modal )
    367456                                this.modal.render();
    368457
    369                         // Detach any views that will be rebound to maintain event bidnings.
     458                        // Detach any views that will be rebound to maintain event bindings.
    370459                        this.$el.children().filter( els ).detach();
    371460                        this.$el.empty().append( els );
    … …  
    390479
    391480                createStates: function() {
    392                         var options = this.options;
     481                        var options = this.options,
     482                                menus = {
     483                                        landing: new media.view.Menu.Landing({
     484                                                controller: this
     485                                        })
     486                                };
    393487
    394488                        // Create the default `states` collection.
    … …  
    405499                                        selection: options.selection,
    406500                                        library:   media.query( options.library ),
    407                                         multiple:  this.options.multiple
     501                                        multiple:  this.options.multiple,
     502                                        menu:      menus.landing
     503                                }),
     504                                new media.controller.Upload({
     505                                        multiple: this.options.multiple,
     506                                        menu:     menus.landing
    408507                                }),
    409508                                new media.controller.Gallery({
    … …  
    420519                createSubviews: function() {
    421520                        // Initialize a stub view for each subview region.
    422                         _.each(['toolbar','sidebar','content'], function( subview ) {
     521                        _.each(['menu','content','sidebar','toolbar'], function( subview ) {
    423522                                this[ '_' + subview ] = new Backbone.View({
    424523                                        tagName:   'div',
    … …  
    451550
    452551        // Create methods to fetch and replace individual subviews.
    453         _.each(['toolbar','sidebar','content'], function( subview ) {
     552        _.each(['menu','content','sidebar','toolbar'], function( subview ) {
    454553                media.view.Frame.prototype[ subview ] = function( view ) {
    455554                        var previous = this[ '_' + subview ];
    … …  
    458557                                return previous;
    459558
     559                        if ( view === previous )
     560                                return;
     561
    460562                        view.$el.addClass( 'media-' + subview );
     563
     564                        // Remove the hide class.
     565                        this.$el.removeClass( 'hide-' + subview );
    461566
    462567                        if ( previous.destroy )
    … …  
    565670
    566671                        this.controller = this.options.controller;
    567                         this.inline = new media.view.UploaderInline({
    568                                 controller:     this.controller,
    569                                 uploaderWindow: this
    570                         }).render();
    571 
    572                         this.inline.$el.appendTo('body');
     672
     673                        this.$browser = $('<a href="#" class="browser" />').hide().appendTo('body');
    573674
    574675                        uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
    575                                 container: this.inline.$el,
    576676                                dropzone:  this.$el,
    577                                 browser:   this.inline.$('.browser'),
     677                                browser:   this.$browser,
    578678                                params:    {}
    579679                        });
    … …  
    648748                        this.controller = this.options.controller;
    649749
     750                        if ( ! this.options.$browser )
     751                                this.options.$browser = this.controller.uploader.$browser;
     752
    650753                        // Track uploading attachments.
    651754                        wp.Uploader.queue.on( 'add remove reset change:percent', this.renderUploadProgress, this );
    … …  
    657760
    658761                render: function() {
     762                        var $browser = this.options.$browser,
     763                                $placeholder;
     764
    659765                        this.renderUploadProgress();
    660766                        this.$el.html( this.template( this.options ) );
     767
     768                        $placeholder = this.$('.browser');
     769                        $browser.text( $placeholder.text() );
     770                        $browser[0].className = $placeholder[0].className;
     771                        $placeholder.replaceWith( $browser.show() );
     772
    661773                        this.$bar = this.$('.media-progress-bar div');
    662774                        return this;
    … …  
    679791                }
    680792        });
    681 
    682793
    683794        /**
    … …  
    766877
    767878                        this.options.items = {
     879                                selection: new media.view.Selection({
     880                                        controller: controller,
     881                                        collection: selection,
     882                                        priority:   -40
     883                                }).render(),
     884
    768885                                'create-new-gallery': {
    769886                                        style:    'primary',
    … …  
    778895                                'insert-into-post': new media.view.ButtonGroup({
    779896                                        priority: 30,
    780                                         classes:  'dropdown-flip-x',
     897                                        classes:  'dropdown-flip-x dropdown-flip-y',
    781898                                        buttons:  [
    782899                                                {
    … …  
    10341151
    10351152        /**
    1036          * wp.media.view.Sidebar
    1037          */
    1038         media.view.Sidebar = Backbone.View.extend({
     1153         * wp.media.view.PriorityList
     1154         */
     1155
     1156        media.view.PriorityList = Backbone.View.extend({
    10391157                tagName:   'div',
    1040                 className: 'media-sidebar',
    1041                 template:  media.template('sidebar'),
    10421158
    10431159                initialize: function() {
    … …  
    10451161                        this._views     = {};
    10461162
    1047                         if ( this.options.views )
    1048                                 this.add( this.options.views, { silent: true });
     1163                        this.add( _.extend( {}, this.views, this.options.views ), { silent: true });
     1164                        delete this.views;
     1165                        delete this.options.views;
    10491166
    10501167                        if ( ! this.options.silent )
    … …  
    10611178                        $( els ).detach();
    10621179
    1063                         this.$el.html( this.template({
    1064                                 title:    this.controller.state().get('title') || '',
    1065                                 uploader: this.controller.options.uploader
    1066                         }) );
    1067 
    1068                         this.$('.sidebar-content').html( els );
    1069 
    1070                         if ( this.controller.uploader ) {
    1071                                 this.$el.append( this.controller.uploader.inline.$el );
    1072                                 this.controller.uploader.refresh();
    1073                         }
    1074 
     1180                        this.$el.html( els );
    10751181                        return this;
    10761182                },
    … …  
    10901196                        }
    10911197
     1198                        if ( ! (view instanceof Backbone.View) )
     1199                                view = this.toView( view, id, options );
     1200
    10921201                        view.controller = view.controller || this.controller;
    10931202
    … …  
    11071216                                this.render();
    11081217                        return this;
    1109                 }
     1218                },
     1219
     1220                toView: function( options ) {
     1221                        return new Backbone.View( options );
     1222                }
     1223        });
     1224
     1225
     1226        /**
     1227         * wp.media.view.Menu
     1228         */
     1229        media.view.Menu = media.view.PriorityList.extend({
     1230                tagName:   'ul',
     1231                className: 'media-menu',
     1232
     1233                toView: function( options, id ) {
     1234                        options = options || {};
     1235                        options.id = id;
     1236                        return new media.view.MenuItem( options ).render();
     1237                },
     1238
     1239                select: function( id ) {
     1240                        var view = this.get( id );
     1241
     1242                        if ( ! view )
     1243                                return;
     1244
     1245                        this.deselect();
     1246                        view.$el.addClass('active');
     1247                },
     1248
     1249                deselect: function() {
     1250                        this.$el.children().removeClass('active');
     1251                }
     1252        });
     1253
     1254        media.view.MenuItem = Backbone.View.extend({
     1255                tagName:   'li',
     1256                className: 'media-menu-item',
     1257
     1258                events: {
     1259                        'click': 'toState'
     1260                },
     1261
     1262                toState: function() {
     1263                        this.controller.state( this.options.id );
     1264                },
     1265
     1266                render: function() {
     1267                        var options = this.options;
     1268
     1269                        if ( options.text )
     1270                                this.$el.text( options.text );
     1271                        else if ( options.html )
     1272                                this.$el.html( options.html );
     1273
     1274                        return this;
     1275                }
     1276        });
     1277
     1278        media.view.Menu.Landing = media.view.Menu.extend({
     1279                views: {
     1280                        upload: {
     1281                                text: l10n.uploadFilesTitle,
     1282                                priority: 20
     1283                        },
     1284                        library: {
     1285                                text: l10n.mediaLibraryTitle,
     1286                                priority: 40
     1287                        },
     1288                        separateLibrary: new Backbone.View({
     1289                                className: 'separator',
     1290                                priority: 60
     1291                        }),
     1292                        embed: {
     1293                                text: l10n.embedFromUrlTitle,
     1294                                priority: 80
     1295                        }
     1296                }
     1297        });
     1298
     1299        /**
     1300         * wp.media.view.Sidebar
     1301         */
     1302        media.view.Sidebar = media.view.PriorityList.extend({
     1303                className: 'media-sidebar'
    11101304        });
    11111305
    … …  
    11191313
    11201314                events: {
    1121                         'click .attachment-preview': 'toggleSelection',
     1315                        'mousedown .attachment-preview': 'toggleSelection',
    11221316                        'change .describe':          'describe'
    11231317                },
    … …  
    13231517                        this.initSortable();
    13241518
    1325                         this.controller.state().on( 'change:edge change:gutter', this.css, this );
     1519                        _.bindAll( this, 'css' );
     1520                        this.model.on( 'change:edge change:gutter', this.css, this );
     1521                        this._resizeCss = _.debounce( _.bind( this.css, this ), this.refreshSensitivity );
     1522                        $(window).on( 'resize.attachments', this._resizeCss );
    13261523                        this.css();
    13271524                },
    … …  
    13291526                destroy: function() {
    13301527                        this.collection.off( 'add remove reset', null, this );
    1331                         this.controller.state().off( 'change:edge change:gutter', this.css, this );
     1528                        this.model.off( 'change:edge change:gutter', this.css, this );
     1529                        $(window).off( 'resize.attachments', this._resizeCss );
    13321530                },
    13331531
    13341532                css: function() {
    1335                         var $css = $( '#' + this.el.id + '-css' ),
    1336                                 state = this.controller.state();
     1533                        var $css = $( '#' + this.el.id + '-css' );
    13371534
    13381535                        if ( $css.length )
    … …  
    13411538                        media.view.Attachments.$head().append( this.template({
    13421539                                id:     this.el.id,
    1343                                 edge:   state.get('edge'),
    1344                                 gutter: state.get('gutter')
     1540                                edge:   this.edge(),
     1541                                gutter: this.model.get('gutter')
    13451542                        }) );
     1543                },
     1544
     1545                edge: function() {
     1546                        var edge = this.model.get('edge'),
     1547                                gutter, width, columns;
     1548
     1549                        if ( ! this.$el.is(':visible') )
     1550                                return edge;
     1551
     1552
     1553                        gutter  = this.model.get('gutter') * 2;
     1554                        width   = this.$el.width() - gutter;
     1555                        columns = Math.ceil( width / ( edge + gutter ) );
     1556                        edge = Math.floor( ( width - ( columns * gutter ) ) / columns );
     1557                        return edge;
    13461558                },
    13471559
    … …  
    13941606                        // If there are no elements, load some.
    13951607                        if ( ! this.collection.length ) {
    1396                                 this.collection.more();
     1608                                this.collection.more().done( this.scroll );
    13971609                                this.$el.empty();
    13981610                                return this;
    … …  
    14111623                        // threshold to query for additional attachments.
    14121624                        this.scroll();
     1625
    14131626                        return this;
    14141627                },
    … …  
    14421655
    14431656                        if ( this.el.scrollHeight < this.el.scrollTop + ( this.el.clientHeight * this.options.refreshThreshold ) ) {
    1444                                 this.collection.more();
     1657                                this.collection.more().done( this.scroll );
    14451658                        }
    14461659                }
    … …  
    14831696        });
    14841697
     1698
     1699
     1700        /**
     1701         * wp.media.view.AttachmentsBrowser
     1702         */
     1703        media.view.AttachmentsBrowser = Backbone.View.extend({
     1704                tagName:   'div',
     1705                className: 'attachments-browser',
     1706
     1707                initialize: function() {
     1708                        this.controller = this.options.controller;
     1709
     1710                        _.defaults( this.options, {
     1711                                search: true,
     1712                                upload: false,
     1713                                total:  true
     1714                        });
     1715
     1716                        this.toolbar = new media.view.Toolbar({
     1717                                controller: this.controller
     1718                        });
     1719
     1720                        if ( this.options.search ) {
     1721                                this.toolbar.add( 'search', new media.view.Search({
     1722                                        controller: this.controller,
     1723                                        model:      this.collection.props,
     1724                                        priority:   -40
     1725                                }) );
     1726                        }
     1727
     1728                        this.attachments = new media.view.Attachments({
     1729                                controller: this.controller,
     1730                                collection: this.collection,
     1731                                model:      this.model,
     1732                                sortable:   this.options.sortable,
     1733                                // The single `Attachment` view to be used in the `Attachments` view.
     1734                                AttachmentView: media.view.Attachment.Library
     1735                        });
     1736                },
     1737
     1738                render: function() {
     1739                        this.toolbar.$el.detach();
     1740                        this.attachments.$el.detach();
     1741                        this.$el.html([ this.toolbar.render().el, this.attachments.render().el ]);
     1742                        return this;
     1743                }
     1744        });
     1745
    14851746        /**
    14861747         * wp.media.view.SelectionPreview
    … …  
    15311792                        event.preventDefault();
    15321793                        this.collection.clear();
     1794                }
     1795        });
     1796
     1797        /**
     1798         * wp.media.view.Selection
     1799         */
     1800        media.view.Selection = Backbone.View.extend({
     1801                tagName:   'div',
     1802                className: 'media-selection',
     1803                template:  media.template('media-selection'),
     1804
     1805                events: {
     1806                        'click .clear-selection': 'clear'
     1807                },
     1808
     1809                initialize: function() {
     1810                        _.defaults( this.options, {
     1811                                clearable: true
     1812                        });
     1813
     1814                        this.controller = this.options.controller;
     1815                        this.attachments = new media.view.Attachments({
     1816                                controller: this.controller,
     1817                                collection: this.collection,
     1818                                sortable:   true,
     1819                                model:      new Backbone.Model({
     1820                                        edge:   40,
     1821                                        gutter: 5
     1822                                }),
     1823
     1824                                // The single `Attachment` view to be used in the `Attachments` view.
     1825                                AttachmentView: media.view.Attachment.Selection
     1826                        });
     1827
     1828                        this.collection.on( 'add remove reset', this.refresh, this );
     1829                },
     1830
     1831                destroy: function() {
     1832                        this.collection.off( 'add remove reset', this.refresh, this );
     1833                },
     1834
     1835                render: function() {
     1836                        this.attachments.$el.detach();
     1837                        this.attachments.render();
     1838
     1839                        this.$el.html( this.template( this.options ) );
     1840
     1841                        this.$('.selection-view').replaceWith( this.attachments.$el );
     1842                        this.refresh();
     1843                        return this;
     1844                },
     1845
     1846                refresh: function() {
     1847                        // If the selection hasn't been rendered, bail.
     1848                        if ( ! this.$el.children().length )
     1849                                return;
     1850
     1851                        // If nothing is selected, display nothing.
     1852                        this.$el.toggleClass( 'empty', ! this.collection.length );
     1853                        this.$('.count').text( this.collection.length + ' ' + l10n.selected );
     1854                },
     1855
     1856                clear: function( event ) {
     1857                        event.preventDefault();
     1858                        this.collection.clear();
     1859                }
     1860        });
     1861
     1862
     1863        /**
     1864         * wp.media.view.Attachment.Selection
     1865         */
     1866        media.view.Attachment.Selection = media.view.Attachment.extend({
     1867                // On click, just select the model, instead of removing the model from
     1868                // the selection.
     1869                toggleSelection: function() {
     1870                        this.controller.state().get('selection').single( this.model );
    15331871                }
    15341872        });
  • trunk/wp-includes/media.php

    r22345 r22362  
    13011301                        <a class="media-modal-close" href="" title="<?php esc_attr_e('Close'); ?>">&times;</a>
    13021302                </div>
    1303                 <div class="media-modal-backdrop"></div>
     1303                <div class="media-modal-backdrop">
     1304                        <div></div>
     1305                </div>
    13041306        </script>
    13051307
    … …  
    13111313
    13121314        <script type="text/html" id="tmpl-uploader-inline">
    1313                 <h3><?php _e( 'Drop files here' ); ?></h3>
    1314                 <!--<span><?php _ex( 'or', 'Uploader: Drop files here - or - Select Files' ); ?></span>-->
    1315                 <a href="#" class="browser button-secondary"><?php _e( 'Select Files' ); ?></a>
    1316                 <div class="media-progress-bar"><div></div></div>
    1317         </script>
    1318 
    1319         <script type="text/html" id="tmpl-sidebar">
    1320                 <h2 class="sidebar-title"><%- title %></h2>
    1321                 <div class="sidebar-content"></div>
     1315                <div class="uploader-inline-content">
     1316                        <h3><?php _e( 'Drop files anywhere to upload' ); ?></h3>
     1317                        <a href="#" class="browser button button-hero"><?php _e( 'Select Files' ); ?></a>
     1318                        <div class="media-progress-bar"><div></div></div>
     1319                </div>
    13221320        </script>
    13231321
    … …  
    13611359
    13621360        <script type="text/html" id="tmpl-attachment-details">
     1361                <h3><?php _e('Edit Attachment Details'); ?></h3>
    13631362                <div class="attachment-preview attachment-details-preview type-<%- type %> subtype-<%- subtype %> <%- orientation %>">
    13641363                        <% if ( uploading ) { %>
    … …  
    13931392        </script>
    13941393
     1394        <script type="text/html" id="tmpl-media-selection">
     1395                <div class="selection-info">
     1396                        <span class="count"></span>
     1397                        <% if ( clearable ) { %>
     1398                                <a class="clear-selection" href="#"><?php _e('Clear'); ?></a>
     1399                        <% } %>
     1400                </div>
     1401                <div class="selection-view"></div>
     1402        </script>
     1403
    13951404        <script type="text/html" id="tmpl-media-selection-preview">
    13961405                <div class="selected-img selected-count-<%- count %>">
    … …  
    14871496                <style type="text/css" id="<%- id %>-css">
    14881497                        #<%- id %> {
    1489                                 padding: <%- gutter %>px;
     1498                                padding: 0 <%- gutter %>px;
    14901499                        }
    14911500
  • trunk/wp-includes/script-loader.php

    r22361 r22362  
    328328                'cancel'      => __( 'Cancel' ),
    329329                'addImages'   => __( 'Add images' ),
     330                'selected'    => __( 'selected' ),
     331
     332                // Upload
     333                'uploadFilesTitle' => __( 'Upload Files' ),
     334                'selectFiles'      => __( 'Select files' ),
    330335
    331336                // Library
    332                 'mediaLibraryTitle'     => __( 'Media Library' ),
    333                 'createNewGallery'      => __( 'Create a new gallery' ),
    334                 'insertIntoPost'        => __( 'Insert into post' ),
    335                 'addToGallery'          => __( 'Add to gallery' ),
     337                'mediaLibraryTitle' => __( 'Media Library' ),
     338                'createNewGallery'  => __( 'Create a new gallery' ),
     339                'insertIntoPost'    => __( 'Insert into post' ),
     340                'addToGallery'      => __( 'Add to gallery' ),
     341
     342                // Embed
     343                'embedFromUrlTitle' => __( 'Embed From URL' ),
    336344
    337345                // Gallery
Note: See TracChangeset for help on using the changeset viewer.