WordPress.org

Make WordPress Core

Changeset 22362


Ignore:
Timestamp:
11/04/2012 10:59:12 PM (6 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.