WordPress.org

Make WordPress Core

Changeset 22362


Ignore:
Timestamp:
11/04/12 22:59:12 (3 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.