WordPress.org

Make WordPress Core

Changeset 22320


Ignore:
Timestamp:
10/28/12 23:29:17 (18 months ago)
Author:
koopersmith
Message:

Give media a heart transplant.

Revises the concept of the media controller and workspace views (i.e. two central points of control) to be more granular. The main media object is now the Frame, which is a hybrid view and state machine.

The state machine is a collection of states, which are just generic instances of Backbone.Model. This circumvents the problem of juggling global parameters when changing states. Each state contains its own event loop. All events are also forwarded to the frame itself (as is the case in all model/collection relationships).

The frame view contains several regions, each of which can be overridden without harming or re-rendering the other regions. These work well when used in conjunction with the state machine events.

This removes the upload sidebar (don't worry, visible upload UI will return). Drag and drop uploading still works. The ability to upload has been abstracted into its own view (instead of being attached to the central workspace view).

Editing galleries is temporarily broken — the gallery creation and editing experiences will be unified in a future patch.

Adds events to detect dragging changes in wp.Uploader and adds methods to detect and leverage browser support for CSS3 transitions.

see #21390, #21809.

Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/includes/meta-boxes.php

    r22044 r22320  
    10191019            $thumbnailId = $element.find('input[name="thumbnail_id"]'), 
    10201020            title        = '<?php _e( "Choose a Featured Image" ); ?>', 
    1021             workflow, setFeaturedImage; 
     1021            workflow, selection, setFeaturedImage; 
    10221022 
    10231023        setFeaturedImage = function( thumbnailId ) { 
     
    10381038                }); 
    10391039 
    1040                 workflow.selection.on( 'add', function( model ) { 
     1040                selection = workflow.state().get('selection'); 
     1041 
     1042                selection.on( 'add', function( model ) { 
    10411043                    var sizes = model.get('sizes'), 
    10421044                        size; 
     
    10521054                    size = size || model.toJSON(); 
    10531055 
    1054                     workflow.modal.close(); 
    1055                     workflow.selection.clear(); 
     1056                    workflow.close(); 
     1057                    selection.clear(); 
    10561058 
    10571059                    $( '<img />', { 
     
    10621064            } 
    10631065 
    1064             workflow.modal.open(); 
     1066            workflow.open(); 
    10651067        }); 
    10661068 
  • trunk/wp-admin/js/media-upload.js

    r22220 r22320  
    105105            workflow = workflows[ id ] = wp.media( _.defaults( options || {}, { 
    106106                title:    wp.media.view.l10n.insertMedia, 
    107                 multiple: true, 
    108                 describe: true 
     107                multiple: true 
    109108            } ) ); 
    110109 
    111             workflow.on( 'update:insert', function( selection ) { 
     110            workflow.get('library').on( 'insert', function( selection ) { 
    112111                this.insert( selection.map( function( attachment ) { 
    113112                    if ( 'image' === attachment.get('type') ) 
     
    118117            }, this ); 
    119118 
    120             workflow.on( 'update:gallery', function( selection ) { 
     119            workflow.get('gallery').on( 'update', function( selection ) { 
    121120                var view = wp.mce.view.get('gallery'), 
    122121                    shortcode; 
  • trunk/wp-includes/css/media-views.css

    r22247 r22320  
    7676    z-index: 50; 
    7777    height: 60px; 
     78    padding: 0 10px; 
    7879    border-bottom: 1px solid #dfdfdf; 
    7980} 
     
    102103 
    103104/** 
    104  * Workspace 
    105  */ 
    106 .media-workspace { 
    107     position: relative; 
    108     width: 100%; 
    109     height: 100%; 
    110 } 
    111  
    112 .upload-attachments { 
    113     position: absolute; 
    114     top: 0; 
    115     left: 0; 
    116     bottom: 0; 
    117     width: 180px; 
    118     margin: 10px; 
    119     text-align: center; 
    120     border: 3px dashed #dfdfdf; 
    121     background: #fff; 
    122     z-index: 100; 
    123 } 
    124  
    125 .upload-attachments h3 { 
    126     font-size: 18px; 
    127     font-weight: 200; 
    128     color: #777; 
    129     padding: 40px 0 0; 
    130     margin: 0; 
    131 } 
    132  
    133 .upload-attachments span { 
    134     display: block; 
    135     color: #777; 
    136     margin: 10px 0; 
    137 } 
    138  
    139 .upload-attachments a { 
    140     display: inline-block; 
    141     margin: 0 auto; 
    142 } 
    143  
    144 .drag-over .upload-attachments { 
    145     width: auto; 
    146     right: 0; 
    147     border-color: #83B4D8; 
    148     box-shadow: 0 0 0 10px #fff; 
    149 } 
    150  
    151 .existing-attachments { 
    152     position: absolute; 
    153     top: 0; 
    154     left: 200px; 
    155     right: 0; 
    156     bottom: 0; 
    157     margin: 0 20px; 
    158 } 
    159  
    160 .media-workspace .attachments, 
    161 .media-workspace .media-toolbar { 
     105 * Frame 
     106 */ 
     107 
     108.media-frame .attachments, 
     109.media-frame .media-toolbar { 
    162110    -webkit-transition-property: left, right, top, bottom, margin; 
    163111    -moz-transition-property:    left, right, top, bottom, margin; 
     
    173121} 
    174122 
    175 .media-workspace .attachments { 
    176     position: absolute; 
    177     top: 0; 
     123.media-frame .attachments { 
     124    position: absolute; 
     125    top: 61px; 
    178126    left: 0; 
    179127    right: 0; 
     
    183131} 
    184132 
    185 .media-workspace.with-toolbar .attachments { 
    186     top: 61px; 
    187 } 
    188  
    189 .media-workspace .media-toolbar { 
     133.media-frame.hide-toolbar .attachments { 
     134    top: 0; 
     135} 
     136 
     137.media-frame .media-toolbar { 
     138    margin-top: 0; 
     139} 
     140 
     141.media-frame.hide-toolbar .media-toolbar { 
    190142    margin-top: -61px; 
    191143} 
    192144 
    193 .media-workspace.with-toolbar .media-toolbar { 
    194     margin-top: 0; 
    195 } 
    196  
    197 .media-workspace .media-toolbar .add-to-gallery { 
     145.media-frame .media-toolbar .add-to-gallery { 
    198146    display: none; 
    199147} 
    200  
    201148/** 
    202149 * Attachments 
     
    214161    right: 0; 
    215162    height: 50px; 
     163    padding: 0 10px; 
    216164    background: #fff; 
    217165} 
     
    243191    bottom: 0; 
    244192    overflow: auto; 
    245     margin: 0 -10px 20px; 
     193    margin: 0 0 20px; 
    246194} 
    247195 
     
    446394} 
    447395 
    448 .upload-attachments .media-progress-bar { 
    449     margin-top: 80px; 
     396.uploader-window { 
     397    position: fixed; 
     398    top: 0; 
     399    left: 0; 
     400    right: 0; 
     401    bottom: 0; 
     402    background: rgba( 0, 86, 132, 0.9 ); 
     403 
     404    /*z-index: -200;*/ 
     405    z-index: 250000; 
    450406    display: none; 
    451 } 
    452  
    453 .uploading .upload-attachments .media-progress-bar { 
     407    text-align: center; 
     408    opacity: 0; 
     409 
     410    -webkit-transition: opacity 250ms; 
     411    -moz-transition:    opacity 250ms; 
     412    -ms-transition:     opacity 250ms; 
     413    -o-transition:      opacity 250ms; 
     414    transition:         opacity 250ms; 
     415} 
     416 
     417/*.drag-over .uploader-window { 
     418    z-index: 250000; 
     419}*/ 
     420 
     421.uploader-window-content { 
     422    position: absolute; 
     423    top: 30px; 
     424    left: 30px; 
     425    right: 30px; 
     426    bottom: 30px; 
     427    border: 1px dashed #fff; 
     428} 
     429 
     430.uploader-window h3 { 
     431    position: absolute; 
     432    top: 50%; 
     433    left: 0; 
     434    right: 0; 
     435    -webkit-transform: translateY( -50% ); 
     436    -moz-transform:    translateY( -50% ); 
     437    -ms-transform:     translateY( -50% ); 
     438    -o-transform:      translateY( -50% ); 
     439    transform:         translateY( -50% ); 
     440 
     441    font-size: 18px; 
     442    font-weight: 200; 
     443    color: #fff; 
     444    padding: 0; 
     445} 
     446 
     447.uploader-window .media-progress-bar { 
     448    margin-top: 20px; 
     449    max-width: 300px; 
     450    background: transparent; 
     451    border-color: #fff; 
     452    /*display: none;*/ 
     453} 
     454 
     455.uploader-window .media-progress-bar div { 
     456    background: #fff; 
     457} 
     458 
     459.uploading .uploader-window .media-progress-bar { 
    454460    display: block; 
    455461} 
  • trunk/wp-includes/js/mce-view.js

    r22220 r22320  
    693693 
    694694                this.workflow = wp.media({ 
    695                     view:      'gallery', 
     695                    state:     'gallery', 
    696696                    selection: this.attachments.models, 
    697697                    title:     mceview.l10n.editGallery, 
    698698                    editing:   true, 
    699                     multiple:  true, 
    700                     describe:  true 
     699                    multiple:  true 
    701700                }); 
    702701 
  • trunk/wp-includes/js/media-models.js

    r22212 r22320  
    1515     */ 
    1616    media = wp.media = function( attributes ) { 
    17         if ( media.controller.Workflow ) 
    18             return new media.controller.Workflow( attributes ).attach().render(); 
     17        if ( media.view.Frame ) 
     18            return new media.view.Frame( attributes ).render().attach().open(); 
    1919    }; 
    2020 
  • trunk/wp-includes/js/media-views.js

    r22267 r22320  
    99    l10n = media.view.l10n = _.isUndefined( _wpMediaViewsL10n ) ? {} : _wpMediaViewsL10n; 
    1010 
     11    // Check if the browser supports CSS 3.0 transitions 
     12    $.support.transition = (function(){ 
     13        var style = document.documentElement.style, 
     14            transitions = { 
     15                WebkitTransition: 'webkitTransitionEnd', 
     16                MozTransition:    'transitionend', 
     17                OTransition:      'oTransitionEnd otransitionend', 
     18                transition:       'transitionend' 
     19            }, transition; 
     20 
     21        transition = _.find( _.keys( transitions ), function( transition ) { 
     22            return ! _.isUndefined( style[ transition ] ); 
     23        }); 
     24 
     25        return transition && { 
     26            end: transitions[ transition ] 
     27        }; 
     28    }()); 
     29 
     30    // Makes it easier to bind events using transitions. 
     31    media.transition = function( selector ) { 
     32        var deferred = $.Deferred(); 
     33 
     34        if ( $.support.transition ) { 
     35            if ( ! (selector instanceof $) ) 
     36                selector = $( selector ); 
     37 
     38            // Resolve the deferred when the first element finishes animating. 
     39            selector.first().one( $.support.transition.end, deferred.resolve ); 
     40 
     41        // Otherwise, execute on the spot. 
     42        } else { 
     43            deferred.resolve(); 
     44        } 
     45 
     46        return deferred.promise(); 
     47    }; 
     48 
    1149    /** 
    1250     * ======================================================================== 
     
    1654 
    1755    /** 
    18      * wp.media.controller.Workflow 
     56     * wp.media.controller.StateMachine 
    1957     */ 
    20     media.controller.Workflow = Backbone.Model.extend({ 
     58    media.controller.StateMachine = function( states ) { 
     59        this.states = new Backbone.Collection( states ); 
     60    }; 
     61 
     62    // Use Backbone's self-propagating `extend` inheritance method. 
     63    media.controller.StateMachine.extend = Backbone.Model.extend; 
     64 
     65    _.extend( media.controller.StateMachine.prototype, { 
     66        // Fetch a state model. 
     67        // 
     68        // Implicitly creates states. 
     69        get: function( id ) { 
     70            // Ensure that the `states` collection exists so the `StateMachine` 
     71            // can be used as a mixin. 
     72            this.states = this.states || new Backbone.Collection(); 
     73 
     74            if ( ! this.states.get( id ) ) 
     75                this.states.add({ id: id }); 
     76            return this.states.get( id ); 
     77        }, 
     78 
     79        // Selects or returns the active state. 
     80        // 
     81        // If a `id` is provided, sets that as the current state. 
     82        // If no parameters are provided, returns the current state object. 
     83        state: function( id ) { 
     84            var previous; 
     85 
     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 ); 
     95        } 
     96    }); 
     97 
     98    // Map methods from the `states` collection to the `StateMachine` itself. 
     99    _.each([ 'on', 'off', 'trigger' ], function( method ) { 
     100        media.controller.StateMachine.prototype[ method ] = function() { 
     101            // Ensure that the `states` collection exists so the `StateMachine` 
     102            // can be used as a mixin. 
     103            this.states = this.states || new Backbone.Collection(); 
     104            // Forward the method to the `states` collection. 
     105            this.states[ method ].apply( this.states, arguments ); 
     106            return this; 
     107        }; 
     108    }); 
     109 
     110    // wp.media.controller.Library 
     111    // --------------------------- 
     112    media.controller.Library = Backbone.Model.extend({ 
    21113        defaults: { 
    22             title:     '', 
    23             multiple:  false, 
    24             view:      'library', 
    25             library:   {}, 
    26             selection: [] 
     114            id:       'library', 
     115            multiple: false, 
     116            describe: false 
    27117        }, 
    28118 
    29119        initialize: function() { 
     120            if ( ! this.get('selection') ) 
     121                this.set( 'selection', new Attachments() ); 
     122 
     123            if ( ! this.get('library') ) 
     124                this.set( 'library', media.query() ); 
     125 
     126            this.on( 'activate', this.activate, this ); 
     127        }, 
     128 
     129        activate: function() { 
     130            var frame = this.frame, 
     131                toolbar; 
     132 
     133            toolbar = this._postLibraryToolbar = new media.view.Toolbar.PostLibrary({ 
     134                controller: frame, 
     135                selection:  this.get('selection') 
     136            }); 
     137 
     138            frame.toolbar( toolbar ); 
     139            this.get('selection').on( 'add remove', toolbar.visibility, toolbar ); 
     140 
     141            frame.content( new media.view.Attachments({ 
     142                directions: this.get('multiple') ? l10n.selectMediaMultiple : l10n.selectMediaSingular, 
     143                controller: frame, 
     144                collection: this.get('library'), 
     145                // The single `Attachment` view to be used in the `Attachments` view. 
     146                AttachmentView: media.view.Attachment.Library 
     147            }).render() ); 
     148 
     149            if ( ! this.get('selection').length ) 
     150                frame.$el.addClass('hide-toolbar'); 
     151 
     152            // If we're in a workflow that supports multiple attachments, 
     153            // automatically select any uploading attachments. 
     154            if ( this.get('multiple') ) 
     155                wp.Uploader.queue.on( 'add', this.selectUpload, this ); 
     156        }, 
     157 
     158        deactivate: function() { 
     159            var toolbar = this._postLibraryToolbar; 
     160 
     161            wp.Uploader.queue.off( 'add', this.selectUpload, this ); 
     162            this.get('selection').off( 'add remove', toolbar.visibility, toolbar ); 
     163        }, 
     164 
     165        selectUpload: function( attachment ) { 
     166            this.get('selection').add( attachment ); 
     167        } 
     168    }); 
     169 
     170    // wp.media.controller.Gallery 
     171    // --------------------------- 
     172    media.controller.Gallery = Backbone.Model.extend({ 
     173        defaults: { 
     174            id:         'gallery', 
     175            multiple:   true, 
     176            describe:   true 
     177        }, 
     178 
     179        initialize: function() { 
     180            if ( ! this.get('selection') ) 
     181                this.set( 'selection', new Attachments() ); 
     182 
     183            this.on( 'activate', this.activate, this ); 
     184        }, 
     185 
     186        activate: function() { 
     187            var frame = this.frame; 
     188 
     189            frame.toolbar( new media.view.Toolbar.Gallery({ 
     190                controller: frame, 
     191                editing:    this.get('editing'), 
     192                selection:  this.get('selection') 
     193            }) ); 
     194 
     195            frame.content( new media.view.Attachments({ 
     196                directions: 'Gallery time!', 
     197                controller: frame, 
     198                collection: this.get('selection'), 
     199                sortable:   true, 
     200                // The single `Attachment` view to be used in the `Attachments` view. 
     201                AttachmentView: media.view.Attachment.Gallery 
     202            }).render() ); 
     203 
     204            // Automatically select any uploading attachments. 
     205            wp.Uploader.queue.on( 'add', this.selectUpload, this ); 
     206        }, 
     207 
     208        deactivate: function() { 
     209            wp.Uploader.queue.off( 'add', this.selectUpload, this ); 
     210        }, 
     211 
     212        selectUpload: function( attachment ) { 
     213            this.get('selection').add( attachment ); 
     214        } 
     215    }); 
     216 
     217    /** 
     218     * ======================================================================== 
     219     * VIEWS 
     220     * ======================================================================== 
     221     */ 
     222 
     223    /** 
     224     * wp.media.view.Frame 
     225     */ 
     226    media.view.Frame = Backbone.View.extend({ 
     227        tagName:   'div', 
     228        className: 'media-frame', 
     229        template:  media.template('media-frame'), 
     230 
     231        initialize: function() { 
     232            _.defaults( this.options, { 
     233                state:     'library', 
     234                title:     '', 
     235                selection: [], 
     236                library:   {}, 
     237                modal:     true, 
     238                multiple:  false, 
     239                uploader:  true 
     240            }); 
     241 
    30242            this.createSelection(); 
    31  
    32             // Initialize view storage. 
    33             this._views        = {}; 
    34             this._pendingViews = {}; 
    35  
    36             // Initialize modal container view. 
    37             this.modal = new media.view.Modal({ controller: this }); 
    38  
    39             // Add default views. 
    40             // 
    41             // Use the `library` property to initialize the corresponding view, 
    42             // then unset the property. 
    43             this.add( 'library', media.view.Workspace.Library, { 
    44                 collection: media.query( this.get('library') ) 
    45             }); 
    46             this.unset('library'); 
    47  
    48             // Add the gallery view. 
    49             this.add( 'gallery', media.view.Workspace.Gallery, { 
    50                 collection: this.selection 
    51             }); 
    52             this.add( 'gallery-library', media.view.Workspace.Library.Gallery, { 
    53                 collection: media.query({ type: 'image' }) 
    54             }); 
    55         }, 
    56  
    57  
    58         // Registers a view. 
    59         // 
    60         // `id` is a unique ID for the view relative to the workflow instance. 
    61         // `constructor` is a `Backbone.View` constructor. `options` are the 
    62         // options to be passed when the view is initialized. 
    63         // 
    64         // Triggers the `add` and `add:VIEW_ID` events. 
    65         add: function( id, constructor, options ) { 
    66             this.remove( id ); 
    67             this._pendingViews[ id ] = { 
    68                 view:    constructor, 
    69                 options: options 
    70             }; 
    71             this.trigger( 'add add:' + id, constructor, options ); 
    72             return this; 
    73         }, 
    74  
    75         // Returns a registered view instance. If an `id` is not provided, 
    76         // it will return the active view. 
    77         // 
    78         // Lazily instantiates a registered view. 
    79         // 
    80         // Triggers the `init` and `init:VIEW_ID` events. 
    81         view: function( id ) { 
    82             var pending; 
    83  
    84             id = id || this.get('view'); 
    85             pending = this._pendingViews[ id ]; 
    86  
    87             if ( ! this._views[ id ] && pending ) { 
    88                 this._views[ id ] = new pending.view( _.extend({ controller: this }, pending.options || {} ) ); 
    89                 delete this._pendingViews[ id ]; 
    90                 this.trigger( 'init init:' + id, this._views[ id ] ); 
    91             } 
    92  
    93             return this._views[ id ]; 
    94         }, 
    95  
    96         // Unregisters a view from the workflow. 
    97         // 
    98         // Triggers the `remove` and `remove:VIEW_ID` events. 
    99         remove: function( id ) { 
    100             delete this._views[ id ]; 
    101             delete this._pendingViews[ id ]; 
    102             this.trigger( 'remove remove:' + id ); 
    103             return this; 
    104         }, 
    105  
    106         // Renders a view and places it within the modal window. 
    107         // Automatically adds a view if `constructor` is provided. 
    108         render: function( id, constructor, options ) { 
    109             var view; 
    110             id = id || this.get('view'); 
    111  
    112             if ( constructor ) 
    113                 this.add( id, constructor, options ); 
    114  
    115             view = this.view( id ); 
    116  
    117             if ( ! view ) 
    118                 return this; 
    119  
    120             view.render(); 
    121             this.modal.content( view ); 
    122             return this; 
    123         }, 
    124  
    125         update: function( event ) { 
    126             this.close(); 
    127             this.trigger( 'update', this.selection, this ); 
    128             this.trigger( 'update:' + event, this.selection, this ); 
    129             this.selection.clear(); 
     243            this.createSubviews(); 
     244            this.createStates(); 
     245        }, 
     246 
     247        render: function() { 
     248            var els = [ this.sidebar().el, this.toolbar().el, this.content().el ]; 
     249 
     250            if ( this.modal ) 
     251                this.modal.render(); 
     252 
     253            // Detach any views that will be rebound to maintain event bidnings. 
     254            this.$el.children().filter( els ).detach(); 
     255            this.$el.empty().append( els ); 
     256 
     257            // Render the window uploader if it exists. 
     258            if ( this.uploader ) 
     259                this.uploader.render().$el.appendTo( this.$el ); 
     260 
     261            return this; 
    130262        }, 
    131263 
    132264        createSelection: function() { 
    133             var controller = this; 
    134  
    135             // Initialize workflow-specific models. 
    136             // Use the `selection` property to initialize the Attachments 
    137             // collection, then unset the property. 
    138             this.selection = new Attachments( this.get('selection') ); 
    139             this.unset('selection'); 
    140  
    141             _.extend( this.selection, { 
     265            var controller = this, 
     266                selection = this.options.selection; 
     267 
     268            if ( ! (selection instanceof Attachments) ) 
     269                selection = this.options.selection = new Attachments( selection ); 
     270 
     271            _.extend( selection, { 
    142272                // Override the selection's add method. 
    143273                // If the workflow does not support multiple 
    144274                // selected attachments, reset the selection. 
    145275                add: function( models, options ) { 
    146                     if ( ! controller.get('multiple') ) { 
     276                    if ( ! controller.state().get('multiple') ) { 
    147277                        models = _.isArray( models ) ? _.first( models ) : models; 
    148278                        this.clear( options ); 
     
    171301                } 
    172302            }); 
    173         } 
    174     }); 
    175  
    176     // Map modal methods to the workflow. 
    177     _.each(['attach','detach','open','close'], function( method ) { 
    178         media.controller.Workflow.prototype[ method ] = function() { 
    179             this.modal[ method ].apply( this.modal, arguments ); 
    180             return this; 
     303        }, 
     304 
     305        createStates: function() { 
     306            var options = this.options; 
     307 
     308            // Create the default `states` collection. 
     309            this.states = new Backbone.Collection(); 
     310 
     311            // Ensure states have a reference to the frame. 
     312            this.states.on( 'add', function( model ) { 
     313                model.frame = this; 
     314            }, this ); 
     315 
     316            // Add the default states. 
     317            this.states.add([ 
     318                new media.controller.Library({ 
     319                    selection:  options.selection, 
     320                    collection: media.query( options.library ), 
     321                    multiple:   this.options.multiple 
     322                }), 
     323                new media.controller.Gallery({ 
     324                    selection:  options.selection 
     325                }) 
     326            ]); 
     327 
     328            // Set the default state. 
     329            this.state( options.state ); 
     330        }, 
     331 
     332        createSubviews: function() { 
     333            // Initialize a stub view for each subview region. 
     334            _.each(['toolbar','sidebar','content'], function( subview ) { 
     335                this[ '_' + subview ] = new Backbone.View({ 
     336                    tagName:   'div', 
     337                    className: 'media-' + subview 
     338                }); 
     339            }, this ); 
     340 
     341            // Initialize modal container view. 
     342            if ( this.options.modal ) { 
     343                this.modal = new media.view.Modal({ 
     344                    controller: this, 
     345                    $content:   this.$el, 
     346                    title:      this.options.title 
     347                }); 
     348            } 
     349 
     350            // Initialize window-wide uploader. 
     351            if ( this.options.uploader ) { 
     352                this.uploader = new media.view.UploaderWindow({ 
     353                    uploader: { 
     354                        dropzone: this.modal ? this.modal.$el : this.$el 
     355                    } 
     356                }); 
     357            } 
     358        } 
     359    }); 
     360 
     361    // Make the `Frame` a `StateMachine`. 
     362    _.extend( media.view.Frame.prototype, media.controller.StateMachine.prototype ); 
     363 
     364    // Create methods to fetch and replace individual subviews. 
     365    _.each(['toolbar','sidebar','content'], function( subview ) { 
     366        media.view.Frame.prototype[ subview ] = function( view ) { 
     367            var previous = this[ '_' + subview ]; 
     368 
     369            if ( ! view ) 
     370                return previous; 
     371 
     372            view.$el.addClass( 'media-' + subview ); 
     373 
     374            if ( previous.destroy ) 
     375                previous.destroy(); 
     376            previous.undelegateEvents(); 
     377            previous.$el.replaceWith( view.$el ); 
     378            this[ '_' + subview ] = view; 
    181379        }; 
    182380    }); 
    183381 
    184     /** 
    185      * ======================================================================== 
    186      * VIEWS 
    187      * ======================================================================== 
    188      */ 
     382    // Map some of the modal's methods to the frame. 
     383    _.each(['open','close','attach','detach'], function( method ) { 
     384        media.view.Frame.prototype[ method ] = function( view ) { 
     385            if ( this.modal ) 
     386                this.modal[ method ].apply( this.modal, arguments ); 
     387            return this; 
     388        }; 
     389    }); 
    189390 
    190391    /** 
     
    201402        initialize: function() { 
    202403            this.controller = this.options.controller; 
    203             this.controller.on( 'change:title', this.render, this ); 
    204404 
    205405            _.defaults( this.options, { 
    206                 container: document.body 
     406                container: document.body, 
     407                title:     '' 
    207408            }); 
    208409        }, 
     
    216417            this.options.$content.detach(); 
    217418 
    218             this.$el.html( this.template( this.controller.toJSON() ) ); 
    219             this.$('.media-modal-content').append( this.options.$content ); 
     419            this.$el.html( this.template({ 
     420                title: this.options.title 
     421            }) ); 
     422 
     423            this.options.$content.addClass('media-modal-content'); 
     424            this.$('.media-modal').append( this.options.$content ); 
    220425            return this; 
    221426        }, 
     
    224429            this.$el.appendTo( this.options.container ); 
    225430            this.controller.trigger( 'attach', this.controller ); 
     431            return this; 
    226432        }, 
    227433 
     
    229435            this.$el.detach(); 
    230436            this.controller.trigger( 'detach', this.controller ); 
     437            return this; 
    231438        }, 
    232439 
     
    234441            this.$el.show(); 
    235442            this.controller.trigger( 'open', this.controller ); 
     443            return this; 
    236444        }, 
    237445 
     
    239447            this.$el.hide(); 
    240448            this.controller.trigger( 'close', this.controller ); 
     449            return this; 
    241450        }, 
    242451 
     
    256465        } 
    257466    }); 
     467 
     468    // wp.media.view.UploaderWindow 
     469    // ---------------------------- 
     470    media.view.UploaderWindow = Backbone.View.extend({ 
     471        tagName:   'div', 
     472        className: 'uploader-window', 
     473        template:  media.template('uploader-window'), 
     474 
     475        initialize: function() { 
     476            var uploader; 
     477 
     478            this.controller = this.options.controller; 
     479 
     480            uploader = this.options.uploader = _.defaults( this.options.uploader || {}, { 
     481                container: this.$el, 
     482                dropzone:  this.$el, 
     483                browser:   this.$('.upload-attachments a'), 
     484                params:    {} 
     485            }); 
     486 
     487            // Track uploading attachments. 
     488            wp.Uploader.queue.on( 'add remove reset change:percent', this.renderUploadProgress, this ); 
     489 
     490            if ( uploader.dropzone ) { 
     491                // Ensure the dropzone is a jQuery collection. 
     492                if ( ! (uploader.dropzone instanceof $) ) 
     493                    uploader.dropzone = $( uploader.dropzone ); 
     494 
     495                // Attempt to initialize the uploader whenever the dropzone is hovered. 
     496                uploader.dropzone.one( 'mouseenter dragenter', _.bind( this.maybeInitUploader, this ) ); 
     497            } 
     498        }, 
     499 
     500        render: function() { 
     501            this.maybeInitUploader(); 
     502            this.renderUploadProgress(); 
     503            this.$el.html( this.template( this.options ) ); 
     504            this.$bar = this.$('.upload-attachments .media-progress-bar div'); 
     505            return this; 
     506        }, 
     507 
     508        maybeInitUploader: function() { 
     509            var $id, dropzone; 
     510 
     511            // If the uploader already exists or the body isn't in the DOM, bail. 
     512            if ( this.uploader || ! this.$el.closest('body').length ) 
     513                return; 
     514 
     515            $id = $('#post_ID'); 
     516            if ( $id.length ) 
     517                this.options.uploader.params.post_id = $id.val(); 
     518 
     519            this.uploader = new wp.Uploader( this.options.uploader ); 
     520 
     521            dropzone = this.uploader.dropzone; 
     522            dropzone.on( 'dropzone:enter', _.bind( this.show, this ) ); 
     523            dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) ); 
     524        }, 
     525 
     526        show: function() { 
     527            var $el = this.$el.show(); 
     528 
     529            // Ensure that the animation is triggered by waiting until 
     530            // the transparent element is painted into the DOM. 
     531            _.defer( function() { 
     532                $el.css({ opacity: 1 }); 
     533            }); 
     534        }, 
     535 
     536        hide: function() { 
     537            var $el = this.$el.css({ opacity: 0 }); 
     538 
     539            media.transition( $el ).done( function() { 
     540                // Transition end events are subject to race conditions. 
     541                // Make sure that the value is set as intended. 
     542                if ( '0' === $el.css('opacity') ) 
     543                    $el.hide(); 
     544            }); 
     545        }, 
     546 
     547        renderUploadProgress: function() { 
     548            var queue = wp.Uploader.queue; 
     549 
     550            this.$el.toggleClass( 'uploading', !! queue.length ); 
     551 
     552            if ( ! this.$bar || ! queue.length ) 
     553                return; 
     554 
     555            this.$bar.width( ( queue.reduce( function( memo, attachment ) { 
     556                if ( attachment.get('uploading') ) 
     557                    return memo + ( attachment.get('percent') || 0 ); 
     558                else 
     559                    return memo + 100; 
     560            }, 0 ) / queue.length ) + '%' ); 
     561        } 
     562    }); 
     563 
    258564 
    259565    /** 
     
    265571 
    266572        initialize: function() { 
    267             this._views    = {}; 
     573            this.controller = this.options.controller; 
     574 
     575            this._views     = {}; 
    268576            this.$primary   = $('<div class="media-toolbar-primary" />').prependTo( this.$el ); 
    269577            this.$secondary = $('<div class="media-toolbar-secondary" />').prependTo( this.$el ); 
    270578 
    271             if ( this.options.items ) { 
    272                 _.each( this.options.items, function( view, id ) { 
    273                     this.add( id, view, { silent: true } ); 
    274                 }, this ); 
    275                 this.render(); 
    276             } 
     579            if ( this.options.items ) 
     580                this.add( this.options.items, { silent: true }).render(); 
    277581        }, 
    278582 
     
    294598 
    295599        add: function( id, view, options ) { 
     600            // Accept an object with an `id` : `view` mapping. 
     601            if ( _.isObject( id ) ) { 
     602                _.each( id, function( view, id ) { 
     603                    this.add( id, view, options ); 
     604                }, this ); 
     605                return this; 
     606            } 
     607 
    296608            if ( ! ( view instanceof Backbone.View ) ) { 
    297609                view.classes = [ id ].concat( view.classes || [] ); 
     
    299611            } 
    300612 
     613            view.controller = view.controller || this.controller; 
     614 
    301615            this._views[ id ] = view; 
    302616            if ( ! options || ! options.silent ) 
     
    317631    }); 
    318632 
     633    // wp.media.view.Toolbar.PostLibrary 
     634    // --------------------------------- 
     635    media.view.Toolbar.PostLibrary = media.view.Toolbar.extend({ 
     636        initialize: function() { 
     637            var selection = this.options.selection, 
     638                controller = this.options.controller; 
     639 
     640            this.options.items = { 
     641                'selection-preview': new media.view.SelectionPreview({ 
     642                    controller: controller, 
     643                    collection: selection, 
     644                    priority: -40 
     645                }), 
     646 
     647                'create-new-gallery': { 
     648                    style:    'primary', 
     649                    text:     l10n.createNewGallery, 
     650                    priority: 40, 
     651 
     652                    click: function() { 
     653                        this.controller.state('gallery'); 
     654                    } 
     655                }, 
     656 
     657                'insert-into-post': new media.view.ButtonGroup({ 
     658                    priority: 30, 
     659                    classes:  'dropdown-flip-x', 
     660                    buttons:  [ 
     661                        { 
     662                            text:  l10n.insertIntoPost, 
     663                            click: function() { 
     664                                controller.close(); 
     665                                controller.state().trigger( 'insert', selection ); 
     666                                selection.clear(); 
     667                            } 
     668                        }, 
     669                        { 
     670                            classes:  ['down-arrow'], 
     671                            dropdown: new media.view.AttachmentDisplaySettings().render().$el, 
     672 
     673                            click: function( event ) { 
     674                                var $el = this.$el; 
     675 
     676                                if ( ! $( event.target ).closest('.dropdown').length ) 
     677                                    $el.toggleClass('active'); 
     678 
     679                                // Stop the event from propagating further so we can bind 
     680                                // a one-time event to the body (and ensure that a click 
     681                                // on the dropdown won't trigger said event). 
     682                                event.stopPropagation(); 
     683 
     684                                if ( $el.is(':visible') ) { 
     685                                    $(document.body).one( 'click', function() { 
     686                                        $el.removeClass('active'); 
     687                                    }); 
     688                                } 
     689                            } 
     690                        } 
     691                    ] 
     692                }).render(), 
     693 
     694                'add-to-gallery': { 
     695                    text:     l10n.addToGallery, 
     696                    priority: 20 
     697                } 
     698            }; 
     699 
     700            media.view.Toolbar.prototype.initialize.apply( this, arguments ); 
     701        }, 
     702 
     703        visibility: function() { 
     704            var selection = this.options.selection, 
     705                controller = this.options.controller, 
     706                count = selection.length, 
     707                showGallery; 
     708 
     709            controller.$el.toggleClass( 'hide-toolbar', ! count ); 
     710 
     711            // Check if every attachment in the selection is an image. 
     712            showGallery = count > 1 && selection.all( function( attachment ) { 
     713                return 'image' === attachment.get('type'); 
     714            }); 
     715 
     716            this.get('create-new-gallery').$el.toggle( showGallery ); 
     717            insert = this.get('insert-into-post'); 
     718            _.each( insert.buttons, function( button ) { 
     719                button.model.set( 'style', showGallery ? '' : 'primary' ); 
     720            }); 
     721        } 
     722    }); 
     723 
     724    // wp.media.view.Toolbar.Gallery 
     725    // ----------------------------- 
     726    media.view.Toolbar.Gallery = media.view.Toolbar.extend({ 
     727        initialize: function() { 
     728            var editing = this.options.editing, 
     729                selection = this.options.selection, 
     730                controller = this.options.controller; 
     731 
     732            this.options.items = { 
     733                'update-gallery': { 
     734                    style:    'primary', 
     735                    text:     editing ? l10n.updateGallery : l10n.insertGalleryIntoPost, 
     736                    priority: 40, 
     737                    click:    function() { 
     738                        controller.close(); 
     739                        controller.state().trigger( 'update', selection ); 
     740                        selection.clear(); 
     741                        controller.state('library'); 
     742                    } 
     743                }, 
     744 
     745                'return-to-library': { 
     746                    text:     editing ? l10n.addImagesFromLibrary : l10n.returnToLibrary, 
     747                    priority: -40, 
     748 
     749                    click: function() { 
     750                        this.controller.state('library'); 
     751                    } 
     752                } 
     753            }; 
     754 
     755            media.view.Toolbar.prototype.initialize.apply( this, arguments ); 
     756        } 
     757    }); 
    319758 
    320759    /** 
     
    460899 
    461900            options.buttons  = this.buttons; 
    462             options.describe = this.controller.get('describe'); 
     901            options.describe = this.controller.state().get('describe'); 
    463902 
    464903            if ( 'image' === options.type ) 
     
    473912 
    474913            // Check if the model is selected. 
    475             if ( this.controller.selection.has( this.model ) ) 
     914            if ( this.selected() ) 
    476915                this.select(); 
    477916 
     
    485924 
    486925        toggleSelection: function( event ) { 
    487             var selection = this.controller.selection; 
     926            var selection = this.controller.state().get('selection'); 
     927 
     928            if ( ! selection ) 
     929                return; 
     930 
    488931            selection[ selection.has( this.model ) ? 'remove' : 'add' ]( this.model ); 
    489932        }, 
    490933 
     934        selected: function() { 
     935            var selection = this.controller.state().get('selection'); 
     936            if ( selection ) 
     937                return selection.has( this.model ); 
     938        }, 
     939 
    491940        select: function( model, collection ) { 
    492             // If a collection is provided, check if it's the selection. 
    493             // If it's not, bail; we're in another selection's event loop. 
    494             if ( collection && collection !== this.controller.selection ) 
     941            var selection = this.controller.state().get('selection'); 
     942 
     943            // Check if a selection exists and if it's the collection provided. 
     944            // If they're not the same collection, bail; we're in another 
     945            // selection's event loop. 
     946            if ( ! selection || ( collection && collection !== selection ) ) 
    495947                return; 
    496948 
     
    499951 
    500952        deselect: function( model, collection ) { 
    501             // If a collection is provided, check if it's the selection. 
    502             // If it's not, bail; we're in another selection's event loop. 
    503             if ( collection && collection !== this.controller.selection ) 
     953            var selection = this.controller.state().get('selection'); 
     954 
     955            // Check if a selection exists and if it's the collection provided. 
     956            // If they're not the same collection, bail; we're in another 
     957            // selection's event loop. 
     958            if ( ! selection || ( collection && collection !== selection ) ) 
    504959                return; 
    505960 
     
    6091064        }()) 
    6101065    }); 
    611  
    612     /** 
    613      * wp.media.view.Workspace 
    614      */ 
    615     media.view.Workspace = Backbone.View.extend({ 
    616         tagName:   'div', 
    617         className: 'media-workspace', 
    618         template:  media.template('media-workspace'), 
    619  
    620         // The `options` to be passed to `Attachments` view. 
    621         attachmentsView: {}, 
    622  
    623         events: { 
    624             'dragenter':  'maybeInitUploader', 
    625             'mouseenter': 'maybeInitUploader' 
    626         }, 
    627  
    628         initialize: function() { 
    629             this.controller = this.options.controller; 
    630  
    631             _.defaults( this.options, { 
    632                 selectOne:       false, 
    633                 uploader:        {}, 
    634                 attachmentsView: {} 
    635             }); 
    636  
    637             this.$content = $('<div class="existing-attachments" />'); 
    638  
    639             // Generate the `options` passed to the `Attachments` view. 
    640             // Order of priority from lowest to highest: the provided defaults, 
    641             // the prototypal `attachmentsView` property, the `attachmentsView` 
    642             // option for the current instance, and then the `controller` and 
    643             // `collection` keys, to ensure they're correctly set. 
    644             this.attachmentsView = _.extend( { 
    645                 directions: this.controller.get('multiple') ? l10n.selectMediaMultiple : l10n.selectMediaSingular 
    646             }, this.attachmentsView, this.options.attachmentsView, { 
    647                 controller: this.controller, 
    648                 collection: this.collection 
    649             }); 
    650  
    651             // Initialize the `Attachments` view. 
    652             this.attachmentsView = new media.view.Attachments( this.attachmentsView ); 
    653             this.$content.append( this.attachmentsView.$el ); 
    654  
    655             // Track uploading attachments. 
    656             wp.Uploader.queue.on( 'add remove reset change:percent', this.renderUploadProgress, this ); 
    657  
    658             // If we're in a workflow that supports multiple attachments, 
    659             // automatically select any uploading attachments. 
    660             if ( this.controller.get('multiple') ) 
    661                 wp.Uploader.queue.on( 'add', this.selectUpload, this ); 
    662         }, 
    663  
    664         render: function() { 
    665             this.$content.detach(); 
    666  
    667             this.attachmentsView.render(); 
    668             this.renderUploadProgress(); 
    669             this.$el.html( this.template( this.options ) ).append( this.$content ); 
    670             this.$bar = this.$('.upload-attachments .media-progress-bar div'); 
    671             return this; 
    672         }, 
    673  
    674         maybeInitUploader: function() { 
    675             var workspace = this, 
    676                 params = {}, 
    677                 $id; 
    678  
    679             // If the uploader already exists or the body isn't in the DOM, bail. 
    680             if ( this.uploader || ! this.$el.closest('body').length ) 
    681                 return; 
    682  
    683             $id = $('#post_ID'); 
    684             if ( $id.length ) 
    685                 params.post_id = $id.val(); 
    686  
    687             this.uploader = new wp.Uploader( _.extend({ 
    688                 container: this.$el, 
    689                 dropzone:  this.$el, 
    690                 browser:   this.$('.upload-attachments a'), 
    691                 params:    params 
    692             }, this.options.uploader ) ); 
    693         }, 
    694  
    695         selectUpload: function( attachment ) { 
    696             this.controller.selection.add( attachment ); 
    697         }, 
    698  
    699         renderUploadProgress: function() { 
    700             var queue = wp.Uploader.queue; 
    701  
    702             this.$el.toggleClass( 'uploading', !! queue.length ); 
    703  
    704             if ( ! this.$bar || ! queue.length ) 
    705                 return; 
    706  
    707             this.$bar.width( ( queue.reduce( function( memo, attachment ) { 
    708                 if ( attachment.get('uploading') ) 
    709                     return memo + ( attachment.get('percent') || 0 ); 
    710                 else 
    711                     return memo + 100; 
    712             }, 0 ) / queue.length ) + '%' ); 
    713         } 
    714     }); 
    715  
    716     /** 
    717      * wp.media.view.Workspace.Library 
    718      */ 
    719     media.view.Workspace.Library = media.view.Workspace.extend({ 
    720  
    721         attachmentsView: { 
    722             // The single `Attachment` view to be used in the `Attachments` view. 
    723             AttachmentView: media.view.Attachment.Library 
    724         }, 
    725  
    726         initialize: function() { 
    727             media.view.Workspace.prototype.initialize.apply( this, arguments ); 
    728  
    729             // If this supports multiple attachments, initialize the sample toolbar view. 
    730             if ( this.controller.get('multiple') ) 
    731                 this.initToolbarView(); 
    732         }, 
    733  
    734         // Initializes the toolbar view. Currently uses defaults set for 
    735         // inserting media into a post. This should be pulled out into the 
    736         // appropriate workflow when the time comes, but is currently here 
    737         // to test multiple selections. 
    738         initToolbarView: function() { 
    739             var controller = this.controller; 
    740  
    741             this.toolbarView = new media.view.Toolbar({ 
    742                 items: { 
    743                     'selection-preview': new media.view.SelectionPreview({ 
    744                         controller: this.controller, 
    745                         collection: this.controller.selection, 
    746                         priority: -40 
    747                     }), 
    748  
    749                     'create-new-gallery': { 
    750                         style:    'primary', 
    751                         text:     l10n.createNewGallery, 
    752                         priority: 40, 
    753  
    754                         click: function() { 
    755                             controller.render('gallery'); 
    756                         } 
    757                     }, 
    758  
    759                     'insert-into-post': new media.view.ButtonGroup({ 
    760                         priority: 30, 
    761                         classes:  'dropdown-flip-x', 
    762                         buttons:  [ 
    763                             { 
    764                                 text:  l10n.insertIntoPost, 
    765                                 click: _.bind( controller.update, controller, 'insert' ) 
    766                             }, 
    767                             { 
    768                                 classes:  ['down-arrow'], 
    769                                 dropdown: new media.view.AttachmentDisplaySettings().render().$el, 
    770  
    771                                 click: function( event ) { 
    772                                     var $el = this.$el; 
    773  
    774                                     if ( ! $( event.target ).closest('.dropdown').length ) 
    775                                         $el.toggleClass('active'); 
    776  
    777                                     // Stop the event from propagating further so we can bind 
    778                                     // a one-time event to the body (and ensure that a click 
    779                                     // on the dropdown won't trigger said event). 
    780                                     event.stopPropagation(); 
    781  
    782                                     if ( $el.is(':visible') ) { 
    783                                         $(document.body).one( 'click', function() { 
    784                                             $el.removeClass('active'); 
    785                                         }); 
    786                                     } 
    787                                 } 
    788                             } 
    789                         ] 
    790                     }).render(), 
    791  
    792                     'add-to-gallery': { 
    793                         text:     l10n.addToGallery, 
    794                         priority: 20 
    795                     } 
    796                 } 
    797             }); 
    798  
    799             this.controller.selection.on( 'add remove', function() { 
    800                 var count = this.controller.selection.length, 
    801                     showGallery; 
    802  
    803                 this.$el.toggleClass( 'with-toolbar', !! count ); 
    804  
    805                 // Check if every attachment in the selection is an image. 
    806                 showGallery = count > 1 && this.controller.selection.all( function( attachment ) { 
    807                     return 'image' === attachment.get('type'); 
    808                 }); 
    809  
    810                 this.toolbarView.get('create-new-gallery').$el.toggle( showGallery ); 
    811                 insert = this.toolbarView.get('insert-into-post'); 
    812                 _.each( insert.buttons, function( button ) { 
    813                     button.model.set( 'style', showGallery ? '' : 'primary' ); 
    814                 }); 
    815             }, this ); 
    816  
    817             this.$content.append( this.toolbarView.$el ); 
    818         } 
    819     }); 
    820  
    821     media.view.Workspace.Library.Gallery = media.view.Workspace.Library.extend({ 
    822         initToolbarView: function() { 
    823             var controller = this.controller, 
    824                 editing = controller.get('editing'), 
    825                 items = { 
    826                     'selection-preview': new media.view.SelectionPreview({ 
    827                         controller: this.controller, 
    828                         collection: this.controller.selection, 
    829                         priority:   -40, 
    830                         clearable:  false 
    831                     }), 
    832  
    833                     'continue-editing-gallery': { 
    834                         style:    'primary', 
    835                         text:     l10n.continueEditingGallery, 
    836                         priority: 40, 
    837  
    838                         click: function() { 
    839                             controller.render( 'gallery' ); 
    840                         } 
    841                     } 
    842                 }; 
    843  
    844             this.toolbarView = new media.view.Toolbar({ 
    845                 items: items 
    846             }); 
    847  
    848             this.$el.addClass('with-toolbar'); 
    849             this.$content.append( this.toolbarView.$el ); 
    850         } 
    851     }); 
    852  
    853     /** 
    854      * wp.media.view.Workspace.Gallery 
    855      */ 
    856     media.view.Workspace.Gallery = media.view.Workspace.extend({ 
    857  
    858         attachmentsView: { 
    859             // The single `Attachment` view to be used in the `Attachments` view. 
    860             AttachmentView: media.view.Attachment.Gallery, 
    861             sortable:       true 
    862         }, 
    863  
    864         initialize: function() { 
    865             media.view.Workspace.prototype.initialize.apply( this, arguments ); 
    866             this.initToolbarView(); 
    867         }, 
    868  
    869         // Initializes the toolbar view. Currently uses defaults set for 
    870         // inserting media into a post. This should be pulled out into the 
    871         // appropriate workflow when the time comes, but is currently here 
    872         // to test multiple selections. 
    873         initToolbarView: function() { 
    874             var controller = this.controller, 
    875                 editing = controller.get('editing'), 
    876                 items = { 
    877                     'update-gallery': { 
    878                         style:    'primary', 
    879                         text:     editing ? l10n.updateGallery : l10n.insertGalleryIntoPost, 
    880                         priority: 40, 
    881                         click:    _.bind( controller.update, controller, 'gallery' ) 
    882                     }, 
    883  
    884                     'return-to-library': { 
    885                         text:     editing ? l10n.addImagesFromLibrary : l10n.returnToLibrary, 
    886                         priority: -40, 
    887  
    888                         click: function() { 
    889                             controller.render( editing ? 'gallery-library' : 'library' ); 
    890                         } 
    891                     } 
    892                 }; 
    893  
    894             this.toolbarView = new media.view.Toolbar({ 
    895                 items: items 
    896             }); 
    897  
    898             this.$el.addClass('with-toolbar'); 
    899             this.$content.append( this.toolbarView.$el ); 
    900         } 
    901     }); 
    902  
    9031066 
    9041067    /** 
  • trunk/wp-includes/js/plupload/wp-plupload.js

    r22126 r22320  
    115115                    return; 
    116116 
    117                 dropzone.addClass('drag-over'); 
     117                dropzone.trigger('dropzone:enter').addClass('drag-over'); 
    118118                active = true; 
    119119            }); 
     
    127127                timer = setTimeout( function() { 
    128128                    active = false; 
    129                     dropzone.removeClass('drag-over'); 
     129                    dropzone.trigger('dropzone:leave').removeClass('drag-over'); 
    130130                }, 0 ); 
    131131            }); 
  • trunk/wp-includes/media.php

    r22247 r22320  
    13001300            <h3 class="media-modal-title"><%- title %></h3> 
    13011301            <a class="media-modal-close" href="" title="<?php esc_attr_e('Close'); ?>">&times;</a> 
    1302             <div class="media-modal-content"></div> 
    13031302        </div> 
    13041303        <div class="media-modal-backdrop"></div> 
    13051304    </script> 
    13061305 
    1307     <script type="text/html" id="tmpl-media-workspace"> 
    1308         <div class="upload-attachments"> 
    1309             <% if ( selectOne ) { %> 
    1310                 <h3><?php _e( 'Drop a file here' ); ?></h3> 
    1311                 <span><?php _ex( 'or', 'Uploader: Drop a file here - or - Select a File' ); ?></span> 
    1312                 <a href="#" class="button-secondary"><?php _e( 'Select a File' ); ?></a> 
    1313             <% } else { %> 
    1314                 <h3><?php _e( 'Drop files here' ); ?></h3> 
    1315                 <span><?php _ex( 'or', 'Uploader: Drop files here - or - Select Files' ); ?></span> 
    1316                 <a href="#" class="button-secondary"><?php _e( 'Select Files' ); ?></a> 
    1317             <% } %> 
    1318  
    1319             <div class="media-progress-bar"><div></div></div> 
     1306    <script type="text/html" id="tmpl-uploader-window"> 
     1307        <div class="uploader-window-content"> 
     1308            <h3><?php _e( 'Drop files here to upload' ); ?></h3> 
    13201309        </div> 
    13211310    </script> 
Note: See TracChangeset for help on using the changeset viewer.