Make WordPress Core

Changeset 50019 for branches/4.3


Ignore:
Timestamp:
01/25/2021 08:15:27 PM (3 years ago)
Author:
desrosj
Message:

Build/Test Tools: Correct JavaScript file in the 4.3 branch.

In [46499], a JavaScript file was unintentionally changed. This restores that file to the correct state.

Partially reverts [46499].
See #52367.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/4.3/src/wp-includes/js/media-views.js

    r46499 r50019  
    1 /******/ (function(modules) { // webpackBootstrap
    2 /******/    // The module cache
    3 /******/    var installedModules = {};
    4 /******/
    5 /******/    // The require function
    6 /******/    function __webpack_require__(moduleId) {
    7 /******/
    8 /******/        // Check if module is in cache
    9 /******/        if(installedModules[moduleId]) {
    10 /******/            return installedModules[moduleId].exports;
    11 /******/        }
    12 /******/        // Create a new module (and put it into the cache)
    13 /******/        var module = installedModules[moduleId] = {
    14 /******/            i: moduleId,
    15 /******/            l: false,
    16 /******/            exports: {}
    17 /******/        };
    18 /******/
    19 /******/        // Execute the module function
    20 /******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    21 /******/
    22 /******/        // Flag the module as loaded
    23 /******/        module.l = true;
    24 /******/
    25 /******/        // Return the exports of the module
    26 /******/        return module.exports;
    27 /******/    }
    28 /******/
    29 /******/
    30 /******/    // expose the modules object (__webpack_modules__)
    31 /******/    __webpack_require__.m = modules;
    32 /******/
    33 /******/    // expose the module cache
    34 /******/    __webpack_require__.c = installedModules;
    35 /******/
    36 /******/    // define getter function for harmony exports
    37 /******/    __webpack_require__.d = function(exports, name, getter) {
    38 /******/        if(!__webpack_require__.o(exports, name)) {
    39 /******/            Object.defineProperty(exports, name, {
    40 /******/                configurable: false,
    41 /******/                enumerable: true,
    42 /******/                get: getter
    43 /******/            });
    44 /******/        }
    45 /******/    };
    46 /******/
    47 /******/    // getDefaultExport function for compatibility with non-harmony modules
    48 /******/    __webpack_require__.n = function(module) {
    49 /******/        var getter = module && module.__esModule ?
    50 /******/            function getDefault() { return module['default']; } :
    51 /******/            function getModuleExports() { return module; };
    52 /******/        __webpack_require__.d(getter, 'a', getter);
    53 /******/        return getter;
    54 /******/    };
    55 /******/
    56 /******/    // Object.prototype.hasOwnProperty.call
    57 /******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    58 /******/
    59 /******/    // __webpack_public_path__
    60 /******/    __webpack_require__.p = "";
    61 /******/
    62 /******/    // Load entry module and return exports
    63 /******/    return __webpack_require__(__webpack_require__.s = 26);
    64 /******/ })
    65 /************************************************************************/
    66 /******/ (Array(26).concat([
    67 /* 26 */
    68 /***/ (function(module, exports, __webpack_require__) {
    69 
    70 var media = wp.media,
     1(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
     2/**
     3 * wp.media.controller.CollectionAdd
     4 *
     5 * A state for adding attachments to a collection (e.g. video playlist).
     6 *
     7 * @class
     8 * @augments wp.media.controller.Library
     9 * @augments wp.media.controller.State
     10 * @augments Backbone.Model
     11 *
     12 * @param {object}                     [attributes]                         The attributes hash passed to the state.
     13 * @param {string}                     [attributes.id=library]      Unique identifier.
     14 * @param {string}                     attributes.title                    Title for the state. Displays in the frame's title region.
     15 * @param {boolean}                    [attributes.multiple=add]            Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
     16 * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
     17 *                                                                          If one is not supplied, a collection of attachments of the specified type will be created.
     18 * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
     19 *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
     20 * @param {string}                     [attributes.menu=gallery]            Initial mode for the menu region.
     21 * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
     22 *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
     23 * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
     24 * @param {string}                     [attributes.toolbar=gallery-add]     Initial mode for the toolbar region.
     25 * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
     26 * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
     27 * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
     28 * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
     29 * @param {int}                        [attributes.priority=100]            The priority for the state link in the media menu.
     30 * @param {boolean}                    [attributes.syncSelection=false]     Whether the Attachments selection should be persisted from the last state.
     31 *                                                                          Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
     32 * @param {string}                     attributes.type                   The collection's media type. (e.g. 'video').
     33 * @param {string}                     attributes.collectionType         The collection type. (e.g. 'playlist').
     34 */
     35var Selection = wp.media.model.Selection,
     36    Library = wp.media.controller.Library,
     37    CollectionAdd;
     38
     39CollectionAdd = Library.extend({
     40    defaults: _.defaults( {
     41        // Selection defaults. @see media.model.Selection
     42        multiple:      'add',
     43        // Attachments browser defaults. @see media.view.AttachmentsBrowser
     44        filterable:    'uploaded',
     45
     46        priority:      100,
     47        syncSelection: false
     48    }, Library.prototype.defaults ),
     49
     50    /**
     51     * @since 3.9.0
     52     */
     53    initialize: function() {
     54        var collectionType = this.get('collectionType');
     55
     56        if ( 'video' === this.get( 'type' ) ) {
     57            collectionType = 'video-' + collectionType;
     58        }
     59
     60        this.set( 'id', collectionType + '-library' );
     61        this.set( 'toolbar', collectionType + '-add' );
     62        this.set( 'menu', collectionType );
     63
     64        // If we haven't been provided a `library`, create a `Selection`.
     65        if ( ! this.get('library') ) {
     66            this.set( 'library', wp.media.query({ type: this.get('type') }) );
     67        }
     68        Library.prototype.initialize.apply( this, arguments );
     69    },
     70
     71    /**
     72     * @since 3.9.0
     73     */
     74    activate: function() {
     75        var library = this.get('library'),
     76            editLibrary = this.get('editLibrary'),
     77            edit = this.frame.state( this.get('collectionType') + '-edit' ).get('library');
     78
     79        if ( editLibrary && editLibrary !== edit ) {
     80            library.unobserve( editLibrary );
     81        }
     82
     83        // Accepts attachments that exist in the original library and
     84        // that do not exist in gallery's library.
     85        library.validator = function( attachment ) {
     86            return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
     87        };
     88
     89        // Reset the library to ensure that all attachments are re-added
     90        // to the collection. Do so silently, as calling `observe` will
     91        // trigger the `reset` event.
     92        library.reset( library.mirroring.models, { silent: true });
     93        library.observe( edit );
     94        this.set('editLibrary', edit);
     95
     96        Library.prototype.activate.apply( this, arguments );
     97    }
     98});
     99
     100module.exports = CollectionAdd;
     101
     102},{}],2:[function(require,module,exports){
     103/**
     104 * wp.media.controller.CollectionEdit
     105 *
     106 * A state for editing a collection, which is used by audio and video playlists,
     107 * and can be used for other collections.
     108 *
     109 * @class
     110 * @augments wp.media.controller.Library
     111 * @augments wp.media.controller.State
     112 * @augments Backbone.Model
     113 *
     114 * @param {object}                     [attributes]                      The attributes hash passed to the state.
     115 * @param {string}                     attributes.title                  Title for the state. Displays in the media menu and the frame's title region.
     116 * @param {wp.media.model.Attachments} [attributes.library]              The attachments collection to edit.
     117 *                                                                       If one is not supplied, an empty media.model.Selection collection is created.
     118 * @param {boolean}                    [attributes.multiple=false]       Whether multi-select is enabled.
     119 * @param {string}                     [attributes.content=browse]       Initial mode for the content region.
     120 * @param {string}                     attributes.menu                   Initial mode for the menu region. @todo this needs a better explanation.
     121 * @param {boolean}                    [attributes.searchable=false]     Whether the library is searchable.
     122 * @param {boolean}                    [attributes.sortable=true]        Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
     123 * @param {boolean}                    [attributes.date=true]            Whether to show the date filter in the browser's toolbar.
     124 * @param {boolean}                    [attributes.describe=true]        Whether to offer UI to describe the attachments - e.g. captioning images in a gallery.
     125 * @param {boolean}                    [attributes.dragInfo=true]        Whether to show instructional text about the attachments being sortable.
     126 * @param {boolean}                    [attributes.dragInfoText]         Instructional text about the attachments being sortable.
     127 * @param {int}                        [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
     128 * @param {boolean}                    [attributes.editing=false]        Whether the gallery is being created, or editing an existing instance.
     129 * @param {int}                        [attributes.priority=60]          The priority for the state link in the media menu.
     130 * @param {boolean}                    [attributes.syncSelection=false]  Whether the Attachments selection should be persisted from the last state.
     131 *                                                                       Defaults to false for this state, because the library passed in  *is* the selection.
     132 * @param {view}                       [attributes.SettingsView]         The view to edit the collection instance settings (e.g. Playlist settings with "Show tracklist" checkbox).
     133 * @param {view}                       [attributes.AttachmentView]       The single `Attachment` view to be used in the `Attachments`.
     134 *                                                                       If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
     135 * @param {string}                     attributes.type                   The collection's media type. (e.g. 'video').
     136 * @param {string}                     attributes.collectionType         The collection type. (e.g. 'playlist').
     137 */
     138var Library = wp.media.controller.Library,
     139    l10n = wp.media.view.l10n,
    71140    $ = jQuery,
    72     l10n;
    73 
    74 media.isTouchDevice = ( 'ontouchend' in document );
    75 
    76 // Link any localized strings.
    77 l10n = media.view.l10n = window._wpMediaViewsL10n || {};
    78 
    79 // Link any settings.
    80 media.view.settings = l10n.settings || {};
    81 delete l10n.settings;
    82 
    83 // Copy the `post` setting over to the model settings.
    84 media.model.settings.post = media.view.settings.post;
    85 
    86 // Check if the browser supports CSS 3.0 transitions
    87 $.support.transition = (function(){
    88     var style = document.documentElement.style,
    89         transitions = {
    90             WebkitTransition: 'webkitTransitionEnd',
    91             MozTransition:    'transitionend',
    92             OTransition:      'oTransitionEnd otransitionend',
    93             transition:       'transitionend'
    94         }, transition;
    95 
    96     transition = _.find( _.keys( transitions ), function( transition ) {
    97         return ! _.isUndefined( style[ transition ] );
    98     });
    99 
    100     return transition && {
    101         end: transitions[ transition ]
    102     };
    103 }());
    104 
     141    CollectionEdit;
     142
     143CollectionEdit = Library.extend({
     144    defaults: {
     145        multiple:         false,
     146        sortable:         true,
     147        date:             false,
     148        searchable:       false,
     149        content:          'browse',
     150        describe:         true,
     151        dragInfo:         true,
     152        idealColumnWidth: 170,
     153        editing:          false,
     154        priority:         60,
     155        SettingsView:     false,
     156        syncSelection:    false
     157    },
     158
     159    /**
     160     * @since 3.9.0
     161     */
     162    initialize: function() {
     163        var collectionType = this.get('collectionType');
     164
     165        if ( 'video' === this.get( 'type' ) ) {
     166            collectionType = 'video-' + collectionType;
     167        }
     168
     169        this.set( 'id', collectionType + '-edit' );
     170        this.set( 'toolbar', collectionType + '-edit' );
     171
     172        // If we haven't been provided a `library`, create a `Selection`.
     173        if ( ! this.get('library') ) {
     174            this.set( 'library', new wp.media.model.Selection() );
     175        }
     176        // The single `Attachment` view to be used in the `Attachments` view.
     177        if ( ! this.get('AttachmentView') ) {
     178            this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
     179        }
     180        Library.prototype.initialize.apply( this, arguments );
     181    },
     182
     183    /**
     184     * @since 3.9.0
     185     */
     186    activate: function() {
     187        var library = this.get('library');
     188
     189        // Limit the library to images only.
     190        library.props.set( 'type', this.get( 'type' ) );
     191
     192        // Watch for uploaded attachments.
     193        this.get('library').observe( wp.Uploader.queue );
     194
     195        this.frame.on( 'content:render:browse', this.renderSettings, this );
     196
     197        Library.prototype.activate.apply( this, arguments );
     198    },
     199
     200    /**
     201     * @since 3.9.0
     202     */
     203    deactivate: function() {
     204        // Stop watching for uploaded attachments.
     205        this.get('library').unobserve( wp.Uploader.queue );
     206
     207        this.frame.off( 'content:render:browse', this.renderSettings, this );
     208
     209        Library.prototype.deactivate.apply( this, arguments );
     210    },
     211
     212    /**
     213     * Render the collection embed settings view in the browser sidebar.
     214     *
     215     * @todo This is against the pattern elsewhere in media. Typically the frame
     216     *       is responsible for adding region mode callbacks. Explain.
     217     *
     218     * @since 3.9.0
     219     *
     220     * @param {wp.media.view.attachmentsBrowser} The attachments browser view.
     221     */
     222    renderSettings: function( attachmentsBrowserView ) {
     223        var library = this.get('library'),
     224            collectionType = this.get('collectionType'),
     225            dragInfoText = this.get('dragInfoText'),
     226            SettingsView = this.get('SettingsView'),
     227            obj = {};
     228
     229        if ( ! library || ! attachmentsBrowserView ) {
     230            return;
     231        }
     232
     233        library[ collectionType ] = library[ collectionType ] || new Backbone.Model();
     234
     235        obj[ collectionType ] = new SettingsView({
     236            controller: this,
     237            model:      library[ collectionType ],
     238            priority:   40
     239        });
     240
     241        attachmentsBrowserView.sidebar.set( obj );
     242
     243        if ( dragInfoText ) {
     244            attachmentsBrowserView.toolbar.set( 'dragInfo', new wp.media.View({
     245                el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0],
     246                priority: -40
     247            }) );
     248        }
     249
     250        // Add the 'Reverse order' button to the toolbar.
     251        attachmentsBrowserView.toolbar.set( 'reverse', {
     252            text:     l10n.reverseOrder,
     253            priority: 80,
     254
     255            click: function() {
     256                library.reset( library.toArray().reverse() );
     257            }
     258        });
     259    }
     260});
     261
     262module.exports = CollectionEdit;
     263
     264},{}],3:[function(require,module,exports){
    105265/**
    106  * A shared event bus used to provide events into
    107  * the media workflows that 3rd-party devs can use to hook
    108  * in.
     266 * wp.media.controller.Cropper
     267 *
     268 * A state for cropping an image.
     269 *
     270 * @class
     271 * @augments wp.media.controller.State
     272 * @augments Backbone.Model
    109273 */
    110 media.events = _.extend( {}, Backbone.Events );
    111 
     274var l10n = wp.media.view.l10n,
     275    Cropper;
     276
     277Cropper = wp.media.controller.State.extend({
     278    defaults: {
     279        id:          'cropper',
     280        title:       l10n.cropImage,
     281        // Region mode defaults.
     282        toolbar:     'crop',
     283        content:     'crop',
     284        router:      false,
     285
     286        canSkipCrop: false
     287    },
     288
     289    activate: function() {
     290        this.frame.on( 'content:create:crop', this.createCropContent, this );
     291        this.frame.on( 'close', this.removeCropper, this );
     292        this.set('selection', new Backbone.Collection(this.frame._selection.single));
     293    },
     294
     295    deactivate: function() {
     296        this.frame.toolbar.mode('browse');
     297    },
     298
     299    createCropContent: function() {
     300        this.cropperView = new wp.media.view.Cropper({
     301            controller: this,
     302            attachment: this.get('selection').first()
     303        });
     304        this.cropperView.on('image-loaded', this.createCropToolbar, this);
     305        this.frame.content.set(this.cropperView);
     306
     307    },
     308    removeCropper: function() {
     309        this.imgSelect.cancelSelection();
     310        this.imgSelect.setOptions({remove: true});
     311        this.imgSelect.update();
     312        this.cropperView.remove();
     313    },
     314    createCropToolbar: function() {
     315        var canSkipCrop, toolbarOptions;
     316
     317        canSkipCrop = this.get('canSkipCrop') || false;
     318
     319        toolbarOptions = {
     320            controller: this.frame,
     321            items: {
     322                insert: {
     323                    style:    'primary',
     324                    text:     l10n.cropImage,
     325                    priority: 80,
     326                    requires: { library: false, selection: false },
     327
     328                    click: function() {
     329                        var controller = this.controller,
     330                            selection;
     331
     332                        selection = controller.state().get('selection').first();
     333                        selection.set({cropDetails: controller.state().imgSelect.getSelection()});
     334
     335                        this.$el.text(l10n.cropping);
     336                        this.$el.attr('disabled', true);
     337
     338                        controller.state().doCrop( selection ).done( function( croppedImage ) {
     339                            controller.trigger('cropped', croppedImage );
     340                            controller.close();
     341                        }).fail( function() {
     342                            controller.trigger('content:error:crop');
     343                        });
     344                    }
     345                }
     346            }
     347        };
     348
     349        if ( canSkipCrop ) {
     350            _.extend( toolbarOptions.items, {
     351                skip: {
     352                    style:      'secondary',
     353                    text:       l10n.skipCropping,
     354                    priority:   70,
     355                    requires:   { library: false, selection: false },
     356                    click:      function() {
     357                        var selection = this.controller.state().get('selection').first();
     358                        this.controller.state().cropperView.remove();
     359                        this.controller.trigger('skippedcrop', selection);
     360                        this.controller.close();
     361                    }
     362                }
     363            });
     364        }
     365
     366        this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) );
     367    },
     368
     369    doCrop: function( attachment ) {
     370        return wp.ajax.post( 'custom-header-crop', {
     371            nonce: attachment.get('nonces').edit,
     372            id: attachment.get('id'),
     373            cropDetails: attachment.get('cropDetails')
     374        } );
     375    }
     376});
     377
     378module.exports = Cropper;
     379
     380},{}],4:[function(require,module,exports){
    112381/**
    113  * Makes it easier to bind events using transitions.
    114  *
    115  * @param {string} selector
    116  * @param {Number} sensitivity
    117  * @returns {Promise}
     382 * wp.media.controller.CustomizeImageCropper
     383 *
     384 * A state for cropping an image.
     385 *
     386 * @class
     387 * @augments wp.media.controller.Cropper
     388 * @augments wp.media.controller.State
     389 * @augments Backbone.Model
    118390 */
    119 media.transition = function( selector, sensitivity ) {
    120     var deferred = $.Deferred();
    121 
    122     sensitivity = sensitivity || 2000;
    123 
    124     if ( $.support.transition ) {
    125         if ( ! (selector instanceof $) ) {
    126             selector = $( selector );
    127         }
    128 
    129         // Resolve the deferred when the first element finishes animating.
    130         selector.first().one( $.support.transition.end, deferred.resolve );
    131 
    132         // Just in case the event doesn't trigger, fire a callback.
    133         _.delay( deferred.resolve, sensitivity );
    134 
    135     // Otherwise, execute on the spot.
    136     } else {
    137         deferred.resolve();
     391var Controller = wp.media.controller,
     392    CustomizeImageCropper;
     393
     394CustomizeImageCropper = Controller.Cropper.extend({
     395    doCrop: function( attachment ) {
     396        var cropDetails = attachment.get( 'cropDetails' ),
     397            control = this.get( 'control' );
     398
     399        cropDetails.dst_width  = control.params.width;
     400        cropDetails.dst_height = control.params.height;
     401
     402        return wp.ajax.post( 'crop-image', {
     403            wp_customize: 'on',
     404            nonce: attachment.get( 'nonces' ).edit,
     405            id: attachment.get( 'id' ),
     406            context: control.id,
     407            cropDetails: cropDetails
     408        } );
    138409    }
    139 
    140     return deferred.promise();
    141 };
    142 
    143 media.controller.Region = __webpack_require__( 27 );
    144 media.controller.StateMachine = __webpack_require__( 28 );
    145 media.controller.State = __webpack_require__( 29 );
    146 
    147 media.selectionSync = __webpack_require__( 30 );
    148 media.controller.Library = __webpack_require__( 31 );
    149 media.controller.ImageDetails = __webpack_require__( 32 );
    150 media.controller.GalleryEdit = __webpack_require__( 33 );
    151 media.controller.GalleryAdd = __webpack_require__( 34 );
    152 media.controller.CollectionEdit = __webpack_require__( 35 );
    153 media.controller.CollectionAdd = __webpack_require__( 36 );
    154 media.controller.FeaturedImage = __webpack_require__( 37 );
    155 media.controller.ReplaceImage = __webpack_require__( 38 );
    156 media.controller.EditImage = __webpack_require__( 39 );
    157 media.controller.MediaLibrary = __webpack_require__( 40 );
    158 media.controller.Embed = __webpack_require__( 41 );
    159 media.controller.Cropper = __webpack_require__( 42 );
    160 media.controller.CustomizeImageCropper = __webpack_require__( 43 );
    161 media.controller.SiteIconCropper = __webpack_require__( 44 );
    162 
    163 media.View = __webpack_require__( 45 );
    164 media.view.Frame = __webpack_require__( 46 );
    165 media.view.MediaFrame = __webpack_require__( 47 );
    166 media.view.MediaFrame.Select = __webpack_require__( 48 );
    167 media.view.MediaFrame.Post = __webpack_require__( 49 );
    168 media.view.MediaFrame.ImageDetails = __webpack_require__( 50 );
    169 media.view.Modal = __webpack_require__( 51 );
    170 media.view.FocusManager = __webpack_require__( 52 );
    171 media.view.UploaderWindow = __webpack_require__( 53 );
    172 media.view.EditorUploader = __webpack_require__( 54 );
    173 media.view.UploaderInline = __webpack_require__( 55 );
    174 media.view.UploaderStatus = __webpack_require__( 56 );
    175 media.view.UploaderStatusError = __webpack_require__( 57 );
    176 media.view.Toolbar = __webpack_require__( 58 );
    177 media.view.Toolbar.Select = __webpack_require__( 59 );
    178 media.view.Toolbar.Embed = __webpack_require__( 60 );
    179 media.view.Button = __webpack_require__( 61 );
    180 media.view.ButtonGroup = __webpack_require__( 62 );
    181 media.view.PriorityList = __webpack_require__( 63 );
    182 media.view.MenuItem = __webpack_require__( 64 );
    183 media.view.Menu = __webpack_require__( 65 );
    184 media.view.RouterItem = __webpack_require__( 66 );
    185 media.view.Router = __webpack_require__( 67 );
    186 media.view.Sidebar = __webpack_require__( 68 );
    187 media.view.Attachment = __webpack_require__( 69 );
    188 media.view.Attachment.Library = __webpack_require__( 70 );
    189 media.view.Attachment.EditLibrary = __webpack_require__( 71 );
    190 media.view.Attachments = __webpack_require__( 72 );
    191 media.view.Search = __webpack_require__( 73 );
    192 media.view.AttachmentFilters = __webpack_require__( 74 );
    193 media.view.DateFilter = __webpack_require__( 75 );
    194 media.view.AttachmentFilters.Uploaded = __webpack_require__( 76 );
    195 media.view.AttachmentFilters.All = __webpack_require__( 77 );
    196 media.view.AttachmentsBrowser = __webpack_require__( 78 );
    197 media.view.Selection = __webpack_require__( 79 );
    198 media.view.Attachment.Selection = __webpack_require__( 80 );
    199 media.view.Attachments.Selection = __webpack_require__( 81 );
    200 media.view.Attachment.EditSelection = __webpack_require__( 82 );
    201 media.view.Settings = __webpack_require__( 83 );
    202 media.view.Settings.AttachmentDisplay = __webpack_require__( 84 );
    203 media.view.Settings.Gallery = __webpack_require__( 85 );
    204 media.view.Settings.Playlist = __webpack_require__( 86 );
    205 media.view.Attachment.Details = __webpack_require__( 87 );
    206 media.view.AttachmentCompat = __webpack_require__( 88 );
    207 media.view.Iframe = __webpack_require__( 89 );
    208 media.view.Embed = __webpack_require__( 90 );
    209 media.view.Label = __webpack_require__( 91 );
    210 media.view.EmbedUrl = __webpack_require__( 92 );
    211 media.view.EmbedLink = __webpack_require__( 93 );
    212 media.view.EmbedImage = __webpack_require__( 94 );
    213 media.view.ImageDetails = __webpack_require__( 95 );
    214 media.view.Cropper = __webpack_require__( 96 );
    215 media.view.SiteIconCropper = __webpack_require__( 97 );
    216 media.view.SiteIconPreview = __webpack_require__( 98 );
    217 media.view.EditImage = __webpack_require__( 99 );
    218 media.view.Spinner = __webpack_require__( 100 );
    219 
    220 
    221 /***/ }),
    222 /* 27 */
    223 /***/ (function(module, exports) {
    224 
     410});
     411
     412module.exports = CustomizeImageCropper;
     413
     414},{}],5:[function(require,module,exports){
    225415/**
    226  * wp.media.controller.Region
    227  *
    228  * A region is a persistent application layout area.
    229  *
    230  * A region assumes one mode at any time, and can be switched to another.
    231  *
    232  * When mode changes, events are triggered on the region's parent view.
    233  * The parent view will listen to specific events and fill the region with an
    234  * appropriate view depending on mode. For example, a frame listens for the
    235  * 'browse' mode t be activated on the 'content' view and then fills the region
    236  * with an AttachmentsBrowser view.
     416 * wp.media.controller.EditImage
     417 *
     418 * A state for editing (cropping, etc.) an image.
    237419 *
    238420 * @class
    239  *
    240  * @param {object}        options          Options hash for the region.
    241  * @param {string}        options.id       Unique identifier for the region.
    242  * @param {Backbone.View} options.view     A parent view the region exists within.
    243  * @param {string}        options.selector jQuery selector for the region within the parent view.
     421 * @augments wp.media.controller.State
     422 * @augments Backbone.Model
     423 *
     424 * @param {object}                    attributes                      The attributes hash passed to the state.
     425 * @param {wp.media.model.Attachment} attributes.model                The attachment.
     426 * @param {string}                    [attributes.id=edit-image]      Unique identifier.
     427 * @param {string}                    [attributes.title=Edit Image]   Title for the state. Displays in the media menu and the frame's title region.
     428 * @param {string}                    [attributes.content=edit-image] Initial mode for the content region.
     429 * @param {string}                    [attributes.toolbar=edit-image] Initial mode for the toolbar region.
     430 * @param {string}                    [attributes.menu=false]         Initial mode for the menu region.
     431 * @param {string}                    [attributes.url]                Unused. @todo Consider removal.
    244432 */
    245 var Region = function( options ) {
    246     _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
    247 };
    248 
    249 // Use Backbone's self-propagating `extend` inheritance method.
    250 Region.extend = Backbone.Model.extend;
    251 
    252 _.extend( Region.prototype, {
    253     /**
    254      * Activate a mode.
     433var l10n = wp.media.view.l10n,
     434    EditImage;
     435
     436EditImage = wp.media.controller.State.extend({
     437    defaults: {
     438        id:      'edit-image',
     439        title:   l10n.editImage,
     440        menu:    false,
     441        toolbar: 'edit-image',
     442        content: 'edit-image',
     443        url:     ''
     444    },
     445
     446    /**
     447     * @since 3.9.0
     448     */
     449    activate: function() {
     450        this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
     451    },
     452
     453    /**
     454     * @since 3.9.0
     455     */
     456    deactivate: function() {
     457        this.stopListening( this.frame );
     458    },
     459
     460    /**
     461     * @since 3.9.0
     462     */
     463    toolbar: function() {
     464        var frame = this.frame,
     465            lastState = frame.lastState(),
     466            previous = lastState && lastState.id;
     467
     468        frame.toolbar.set( new wp.media.view.Toolbar({
     469            controller: frame,
     470            items: {
     471                back: {
     472                    style: 'primary',
     473                    text:     l10n.back,
     474                    priority: 20,
     475                    click:    function() {
     476                        if ( previous ) {
     477                            frame.setState( previous );
     478                        } else {
     479                            frame.close();
     480                        }
     481                    }
     482                }
     483            }
     484        }) );
     485    }
     486});
     487
     488module.exports = EditImage;
     489
     490},{}],6:[function(require,module,exports){
     491/**
     492 * wp.media.controller.Embed
     493 *
     494 * A state for embedding media from a URL.
     495 *
     496 * @class
     497 * @augments wp.media.controller.State
     498 * @augments Backbone.Model
     499 *
     500 * @param {object} attributes                         The attributes hash passed to the state.
     501 * @param {string} [attributes.id=embed]              Unique identifier.
     502 * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region.
     503 * @param {string} [attributes.content=embed]         Initial mode for the content region.
     504 * @param {string} [attributes.menu=default]          Initial mode for the menu region.
     505 * @param {string} [attributes.toolbar=main-embed]    Initial mode for the toolbar region.
     506 * @param {string} [attributes.menu=false]            Initial mode for the menu region.
     507 * @param {int}    [attributes.priority=120]          The priority for the state link in the media menu.
     508 * @param {string} [attributes.type=link]             The type of embed. Currently only link is supported.
     509 * @param {string} [attributes.url]                   The embed URL.
     510 * @param {object} [attributes.metadata={}]           Properties of the embed, which will override attributes.url if set.
     511 */
     512var l10n = wp.media.view.l10n,
     513    $ = Backbone.$,
     514    Embed;
     515
     516Embed = wp.media.controller.State.extend({
     517    defaults: {
     518        id:       'embed',
     519        title:    l10n.insertFromUrlTitle,
     520        content:  'embed',
     521        menu:     'default',
     522        toolbar:  'main-embed',
     523        priority: 120,
     524        type:     'link',
     525        url:      '',
     526        metadata: {}
     527    },
     528
     529    // The amount of time used when debouncing the scan.
     530    sensitivity: 400,
     531
     532    initialize: function(options) {
     533        this.metadata = options.metadata;
     534        this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity );
     535        this.props = new Backbone.Model( this.metadata || { url: '' });
     536        this.props.on( 'change:url', this.debouncedScan, this );
     537        this.props.on( 'change:url', this.refresh, this );
     538        this.on( 'scan', this.scanImage, this );
     539    },
     540
     541    /**
     542     * Trigger a scan of the embedded URL's content for metadata required to embed.
    255543     *
     544     * @fires wp.media.controller.Embed#scan
     545     */
     546    scan: function() {
     547        var scanners,
     548            embed = this,
     549            attributes = {
     550                type: 'link',
     551                scanners: []
     552            };
     553
     554        // Scan is triggered with the list of `attributes` to set on the
     555        // state, useful for the 'type' attribute and 'scanners' attribute,
     556        // an array of promise objects for asynchronous scan operations.
     557        if ( this.props.get('url') ) {
     558            this.trigger( 'scan', attributes );
     559        }
     560
     561        if ( attributes.scanners.length ) {
     562            scanners = attributes.scanners = $.when.apply( $, attributes.scanners );
     563            scanners.always( function() {
     564                if ( embed.get('scanners') === scanners ) {
     565                    embed.set( 'loading', false );
     566                }
     567            });
     568        } else {
     569            attributes.scanners = null;
     570        }
     571
     572        attributes.loading = !! attributes.scanners;
     573        this.set( attributes );
     574    },
     575    /**
     576     * Try scanning the embed as an image to discover its dimensions.
     577     *
     578     * @param {Object} attributes
     579     */
     580    scanImage: function( attributes ) {
     581        var frame = this.frame,
     582            state = this,
     583            url = this.props.get('url'),
     584            image = new Image(),
     585            deferred = $.Deferred();
     586
     587        attributes.scanners.push( deferred.promise() );
     588
     589        // Try to load the image and find its width/height.
     590        image.onload = function() {
     591            deferred.resolve();
     592
     593            if ( state !== frame.state() || url !== state.props.get('url') ) {
     594                return;
     595            }
     596
     597            state.set({
     598                type: 'image'
     599            });
     600
     601            state.props.set({
     602                width:  image.width,
     603                height: image.height
     604            });
     605        };
     606
     607        image.onerror = deferred.reject;
     608        image.src = url;
     609    },
     610
     611    refresh: function() {
     612        this.frame.toolbar.get().refresh();
     613    },
     614
     615    reset: function() {
     616        this.props.clear().set({ url: '' });
     617
     618        if ( this.active ) {
     619            this.refresh();
     620        }
     621    }
     622});
     623
     624module.exports = Embed;
     625
     626},{}],7:[function(require,module,exports){
     627/**
     628 * wp.media.controller.FeaturedImage
     629 *
     630 * A state for selecting a featured image for a post.
     631 *
     632 * @class
     633 * @augments wp.media.controller.Library
     634 * @augments wp.media.controller.State
     635 * @augments Backbone.Model
     636 *
     637 * @param {object}                     [attributes]                          The attributes hash passed to the state.
     638 * @param {string}                     [attributes.id=featured-image]        Unique identifier.
     639 * @param {string}                     [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region.
     640 * @param {wp.media.model.Attachments} [attributes.library]                  The attachments collection to browse.
     641 *                                                                           If one is not supplied, a collection of all images will be created.
     642 * @param {boolean}                    [attributes.multiple=false]           Whether multi-select is enabled.
     643 * @param {string}                     [attributes.content=upload]           Initial mode for the content region.
     644 *                                                                           Overridden by persistent user setting if 'contentUserSetting' is true.
     645 * @param {string}                     [attributes.menu=default]             Initial mode for the menu region.
     646 * @param {string}                     [attributes.router=browse]            Initial mode for the router region.
     647 * @param {string}                     [attributes.toolbar=featured-image]   Initial mode for the toolbar region.
     648 * @param {int}                        [attributes.priority=60]              The priority for the state link in the media menu.
     649 * @param {boolean}                    [attributes.searchable=true]          Whether the library is searchable.
     650 * @param {boolean|string}             [attributes.filterable=false]         Whether the library is filterable, and if so what filters should be shown.
     651 *                                                                           Accepts 'all', 'uploaded', or 'unattached'.
     652 * @param {boolean}                    [attributes.sortable=true]            Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
     653 * @param {boolean}                    [attributes.autoSelect=true]          Whether an uploaded attachment should be automatically added to the selection.
     654 * @param {boolean}                    [attributes.describe=false]           Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
     655 * @param {boolean}                    [attributes.contentUserSetting=true]  Whether the content region's mode should be set and persisted per user.
     656 * @param {boolean}                    [attributes.syncSelection=true]       Whether the Attachments selection should be persisted from the last state.
     657 */
     658var Attachment = wp.media.model.Attachment,
     659    Library = wp.media.controller.Library,
     660    l10n = wp.media.view.l10n,
     661    FeaturedImage;
     662
     663FeaturedImage = Library.extend({
     664    defaults: _.defaults({
     665        id:            'featured-image',
     666        title:         l10n.setFeaturedImageTitle,
     667        multiple:      false,
     668        filterable:    'uploaded',
     669        toolbar:       'featured-image',
     670        priority:      60,
     671        syncSelection: true
     672    }, Library.prototype.defaults ),
     673
     674    /**
     675     * @since 3.5.0
     676     */
     677    initialize: function() {
     678        var library, comparator;
     679
     680        // If we haven't been provided a `library`, create a `Selection`.
     681        if ( ! this.get('library') ) {
     682            this.set( 'library', wp.media.query({ type: 'image' }) );
     683        }
     684
     685        Library.prototype.initialize.apply( this, arguments );
     686
     687        library    = this.get('library');
     688        comparator = library.comparator;
     689
     690        // Overload the library's comparator to push items that are not in
     691        // the mirrored query to the front of the aggregate collection.
     692        library.comparator = function( a, b ) {
     693            var aInQuery = !! this.mirroring.get( a.cid ),
     694                bInQuery = !! this.mirroring.get( b.cid );
     695
     696            if ( ! aInQuery && bInQuery ) {
     697                return -1;
     698            } else if ( aInQuery && ! bInQuery ) {
     699                return 1;
     700            } else {
     701                return comparator.apply( this, arguments );
     702            }
     703        };
     704
     705        // Add all items in the selection to the library, so any featured
     706        // images that are not initially loaded still appear.
     707        library.observe( this.get('selection') );
     708    },
     709
     710    /**
     711     * @since 3.5.0
     712     */
     713    activate: function() {
     714        this.updateSelection();
     715        this.frame.on( 'open', this.updateSelection, this );
     716
     717        Library.prototype.activate.apply( this, arguments );
     718    },
     719
     720    /**
     721     * @since 3.5.0
     722     */
     723    deactivate: function() {
     724        this.frame.off( 'open', this.updateSelection, this );
     725
     726        Library.prototype.deactivate.apply( this, arguments );
     727    },
     728
     729    /**
     730     * @since 3.5.0
     731     */
     732    updateSelection: function() {
     733        var selection = this.get('selection'),
     734            id = wp.media.view.settings.post.featuredImageId,
     735            attachment;
     736
     737        if ( '' !== id && -1 !== id ) {
     738            attachment = Attachment.get( id );
     739            attachment.fetch();
     740        }
     741
     742        selection.reset( attachment ? [ attachment ] : [] );
     743    }
     744});
     745
     746module.exports = FeaturedImage;
     747
     748},{}],8:[function(require,module,exports){
     749/**
     750 * wp.media.controller.GalleryAdd
     751 *
     752 * A state for selecting more images to add to a gallery.
     753 *
     754 * @class
     755 * @augments wp.media.controller.Library
     756 * @augments wp.media.controller.State
     757 * @augments Backbone.Model
     758 *
     759 * @param {object}                     [attributes]                         The attributes hash passed to the state.
     760 * @param {string}                     [attributes.id=gallery-library]      Unique identifier.
     761 * @param {string}                     [attributes.title=Add to Gallery]    Title for the state. Displays in the frame's title region.
     762 * @param {boolean}                    [attributes.multiple=add]            Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
     763 * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
     764 *                                                                          If one is not supplied, a collection of all images will be created.
     765 * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
     766 *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
     767 * @param {string}                     [attributes.menu=gallery]            Initial mode for the menu region.
     768 * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
     769 *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
     770 * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
     771 * @param {string}                     [attributes.toolbar=gallery-add]     Initial mode for the toolbar region.
     772 * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
     773 * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
     774 * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
     775 * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
     776 * @param {int}                        [attributes.priority=100]            The priority for the state link in the media menu.
     777 * @param {boolean}                    [attributes.syncSelection=false]     Whether the Attachments selection should be persisted from the last state.
     778 *                                                                          Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
     779 */
     780var Selection = wp.media.model.Selection,
     781    Library = wp.media.controller.Library,
     782    l10n = wp.media.view.l10n,
     783    GalleryAdd;
     784
     785GalleryAdd = Library.extend({
     786    defaults: _.defaults({
     787        id:            'gallery-library',
     788        title:         l10n.addToGalleryTitle,
     789        multiple:      'add',
     790        filterable:    'uploaded',
     791        menu:          'gallery',
     792        toolbar:       'gallery-add',
     793        priority:      100,
     794        syncSelection: false
     795    }, Library.prototype.defaults ),
     796
     797    /**
     798     * @since 3.5.0
     799     */
     800    initialize: function() {
     801        // If a library wasn't supplied, create a library of images.
     802        if ( ! this.get('library') ) {
     803            this.set( 'library', wp.media.query({ type: 'image' }) );
     804        }
     805
     806        Library.prototype.initialize.apply( this, arguments );
     807    },
     808
     809    /**
     810     * @since 3.5.0
     811     */
     812    activate: function() {
     813        var library = this.get('library'),
     814            edit    = this.frame.state('gallery-edit').get('library');
     815
     816        if ( this.editLibrary && this.editLibrary !== edit ) {
     817            library.unobserve( this.editLibrary );
     818        }
     819
     820        // Accepts attachments that exist in the original library and
     821        // that do not exist in gallery's library.
     822        library.validator = function( attachment ) {
     823            return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
     824        };
     825
     826        // Reset the library to ensure that all attachments are re-added
     827        // to the collection. Do so silently, as calling `observe` will
     828        // trigger the `reset` event.
     829        library.reset( library.mirroring.models, { silent: true });
     830        library.observe( edit );
     831        this.editLibrary = edit;
     832
     833        Library.prototype.activate.apply( this, arguments );
     834    }
     835});
     836
     837module.exports = GalleryAdd;
     838
     839},{}],9:[function(require,module,exports){
     840/**
     841 * wp.media.controller.GalleryEdit
     842 *
     843 * A state for editing a gallery's images and settings.
     844 *
     845 * @class
     846 * @augments wp.media.controller.Library
     847 * @augments wp.media.controller.State
     848 * @augments Backbone.Model
     849 *
     850 * @param {object}                     [attributes]                       The attributes hash passed to the state.
     851 * @param {string}                     [attributes.id=gallery-edit]       Unique identifier.
     852 * @param {string}                     [attributes.title=Edit Gallery]    Title for the state. Displays in the frame's title region.
     853 * @param {wp.media.model.Attachments} [attributes.library]               The collection of attachments in the gallery.
     854 *                                                                        If one is not supplied, an empty media.model.Selection collection is created.
     855 * @param {boolean}                    [attributes.multiple=false]        Whether multi-select is enabled.
     856 * @param {boolean}                    [attributes.searchable=false]      Whether the library is searchable.
     857 * @param {boolean}                    [attributes.sortable=true]         Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
     858 * @param {boolean}                    [attributes.date=true]             Whether to show the date filter in the browser's toolbar.
     859 * @param {string|false}               [attributes.content=browse]        Initial mode for the content region.
     860 * @param {string|false}               [attributes.toolbar=image-details] Initial mode for the toolbar region.
     861 * @param {boolean}                    [attributes.describe=true]         Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
     862 * @param {boolean}                    [attributes.displaySettings=true]  Whether to show the attachment display settings interface.
     863 * @param {boolean}                    [attributes.dragInfo=true]         Whether to show instructional text about the attachments being sortable.
     864 * @param {int}                        [attributes.idealColumnWidth=170]  The ideal column width in pixels for attachments.
     865 * @param {boolean}                    [attributes.editing=false]         Whether the gallery is being created, or editing an existing instance.
     866 * @param {int}                        [attributes.priority=60]           The priority for the state link in the media menu.
     867 * @param {boolean}                    [attributes.syncSelection=false]   Whether the Attachments selection should be persisted from the last state.
     868 *                                                                        Defaults to false for this state, because the library passed in  *is* the selection.
     869 * @param {view}                       [attributes.AttachmentView]        The single `Attachment` view to be used in the `Attachments`.
     870 *                                                                        If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
     871 */
     872var Library = wp.media.controller.Library,
     873    l10n = wp.media.view.l10n,
     874    GalleryEdit;
     875
     876GalleryEdit = Library.extend({
     877    defaults: {
     878        id:               'gallery-edit',
     879        title:            l10n.editGalleryTitle,
     880        multiple:         false,
     881        searchable:       false,
     882        sortable:         true,
     883        date:             false,
     884        display:          false,
     885        content:          'browse',
     886        toolbar:          'gallery-edit',
     887        describe:         true,
     888        displaySettings:  true,
     889        dragInfo:         true,
     890        idealColumnWidth: 170,
     891        editing:          false,
     892        priority:         60,
     893        syncSelection:    false
     894    },
     895
     896    /**
     897     * @since 3.5.0
     898     */
     899    initialize: function() {
     900        // If we haven't been provided a `library`, create a `Selection`.
     901        if ( ! this.get('library') ) {
     902            this.set( 'library', new wp.media.model.Selection() );
     903        }
     904
     905        // The single `Attachment` view to be used in the `Attachments` view.
     906        if ( ! this.get('AttachmentView') ) {
     907            this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
     908        }
     909
     910        Library.prototype.initialize.apply( this, arguments );
     911    },
     912
     913    /**
     914     * @since 3.5.0
     915     */
     916    activate: function() {
     917        var library = this.get('library');
     918
     919        // Limit the library to images only.
     920        library.props.set( 'type', 'image' );
     921
     922        // Watch for uploaded attachments.
     923        this.get('library').observe( wp.Uploader.queue );
     924
     925        this.frame.on( 'content:render:browse', this.gallerySettings, this );
     926
     927        Library.prototype.activate.apply( this, arguments );
     928    },
     929
     930    /**
     931     * @since 3.5.0
     932     */
     933    deactivate: function() {
     934        // Stop watching for uploaded attachments.
     935        this.get('library').unobserve( wp.Uploader.queue );
     936
     937        this.frame.off( 'content:render:browse', this.gallerySettings, this );
     938
     939        Library.prototype.deactivate.apply( this, arguments );
     940    },
     941
     942    /**
    256943     * @since 3.5.0
    257944     *
    258      * @param {string} mode
    259      *
    260      * @fires this.view#{this.id}:activate:{this._mode}
    261      * @fires this.view#{this.id}:activate
    262      * @fires this.view#{this.id}:deactivate:{this._mode}
    263      * @fires this.view#{this.id}:deactivate
    264      *
    265      * @returns {wp.media.controller.Region} Returns itself to allow chaining.
    266      */
    267     mode: function( mode ) {
    268         if ( ! mode ) {
    269             return this._mode;
    270         }
    271         // Bail if we're trying to change to the current mode.
    272         if ( mode === this._mode ) {
    273             return this;
    274         }
    275 
    276         /**
    277          * Region mode deactivation event.
    278          *
    279          * @event this.view#{this.id}:deactivate:{this._mode}
    280          * @event this.view#{this.id}:deactivate
    281          */
    282         this.trigger('deactivate');
    283 
    284         this._mode = mode;
    285         this.render( mode );
    286 
    287         /**
    288          * Region mode activation event.
    289          *
    290          * @event this.view#{this.id}:activate:{this._mode}
    291          * @event this.view#{this.id}:activate
    292          */
    293         this.trigger('activate');
    294         return this;
    295     },
    296     /**
    297      * Render a mode.
    298      *
    299      * @since 3.5.0
    300      *
    301      * @param {string} mode
    302      *
    303      * @fires this.view#{this.id}:create:{this._mode}
    304      * @fires this.view#{this.id}:create
    305      * @fires this.view#{this.id}:render:{this._mode}
    306      * @fires this.view#{this.id}:render
    307      *
    308      * @returns {wp.media.controller.Region} Returns itself to allow chaining
    309      */
    310     render: function( mode ) {
    311         // If the mode isn't active, activate it.
    312         if ( mode && mode !== this._mode ) {
    313             return this.mode( mode );
    314         }
    315 
    316         var set = { view: null },
    317             view;
    318 
    319         /**
    320          * Create region view event.
    321          *
    322          * Region view creation takes place in an event callback on the frame.
    323          *
    324          * @event this.view#{this.id}:create:{this._mode}
    325          * @event this.view#{this.id}:create
    326          */
    327         this.trigger( 'create', set );
    328         view = set.view;
    329 
    330         /**
    331          * Render region view event.
    332          *
    333          * Region view creation takes place in an event callback on the frame.
    334          *
    335          * @event this.view#{this.id}:create:{this._mode}
    336          * @event this.view#{this.id}:create
    337          */
    338         this.trigger( 'render', view );
    339         if ( view ) {
    340             this.set( view );
    341         }
    342         return this;
    343     },
    344 
    345     /**
    346      * Get the region's view.
    347      *
    348      * @since 3.5.0
    349      *
    350      * @returns {wp.media.View}
    351      */
    352     get: function() {
    353         return this.view.views.first( this.selector );
    354     },
    355 
    356     /**
    357      * Set the region's view as a subview of the frame.
    358      *
    359      * @since 3.5.0
    360      *
    361      * @param {Array|Object} views
    362      * @param {Object} [options={}]
    363      * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining
    364      */
    365     set: function( views, options ) {
    366         if ( options ) {
    367             options.add = false;
    368         }
    369         return this.view.views.set( this.selector, views, options );
    370     },
    371 
    372     /**
    373      * Trigger regional view events on the frame.
    374      *
    375      * @since 3.5.0
    376      *
    377      * @param {string} event
    378      * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining.
    379      */
    380     trigger: function( event ) {
    381         var base, args;
    382 
    383         if ( ! this._mode ) {
     945     * @param browser
     946     */
     947    gallerySettings: function( browser ) {
     948        if ( ! this.get('displaySettings') ) {
    384949            return;
    385950        }
    386951
    387         args = _.toArray( arguments );
    388         base = this.id + ':' + event;
    389 
    390         // Trigger `{this.id}:{event}:{this._mode}` event on the frame.
    391         args[0] = base + ':' + this._mode;
    392         this.view.trigger.apply( this.view, args );
    393 
    394         // Trigger `{this.id}:{event}` event on the frame.
    395         args[0] = base;
    396         this.view.trigger.apply( this.view, args );
    397         return this;
     952        var library = this.get('library');
     953
     954        if ( ! library || ! browser ) {
     955            return;
     956        }
     957
     958        library.gallery = library.gallery || new Backbone.Model();
     959
     960        browser.sidebar.set({
     961            gallery: new wp.media.view.Settings.Gallery({
     962                controller: this,
     963                model:      library.gallery,
     964                priority:   40
     965            })
     966        });
     967
     968        browser.toolbar.set( 'reverse', {
     969            text:     l10n.reverseOrder,
     970            priority: 80,
     971
     972            click: function() {
     973                library.reset( library.toArray().reverse() );
     974            }
     975        });
    398976    }
    399977});
    400978
    401 module.exports = Region;
    402 
    403 
    404 /***/ }),
    405 /* 28 */
    406 /***/ (function(module, exports) {
    407 
     979module.exports = GalleryEdit;
     980
     981},{}],10:[function(require,module,exports){
    408982/**
    409  * wp.media.controller.StateMachine
    410  *
    411  * A state machine keeps track of state. It is in one state at a time,
    412  * and can change from one state to another.
    413  *
    414  * States are stored as models in a Backbone collection.
    415  *
    416  * @since 3.5.0
     983 * wp.media.controller.ImageDetails
     984 *
     985 * A state for editing the attachment display settings of an image that's been
     986 * inserted into the editor.
    417987 *
    418988 * @class
     989 * @augments wp.media.controller.State
    419990 * @augments Backbone.Model
    420  * @mixin
    421  * @mixes Backbone.Events
    422  *
    423  * @param {Array} states
     991 *
     992 * @param {object}                    [attributes]                       The attributes hash passed to the state.
     993 * @param {string}                    [attributes.id=image-details]      Unique identifier.
     994 * @param {string}                    [attributes.title=Image Details]   Title for the state. Displays in the frame's title region.
     995 * @param {wp.media.model.Attachment} attributes.image                   The image's model.
     996 * @param {string|false}              [attributes.content=image-details] Initial mode for the content region.
     997 * @param {string|false}              [attributes.menu=false]            Initial mode for the menu region.
     998 * @param {string|false}              [attributes.router=false]          Initial mode for the router region.
     999 * @param {string|false}              [attributes.toolbar=image-details] Initial mode for the toolbar region.
     1000 * @param {boolean}                   [attributes.editing=false]         Unused.
     1001 * @param {int}                       [attributes.priority=60]           Unused.
     1002 *
     1003 * @todo This state inherits some defaults from media.controller.Library.prototype.defaults,
     1004 *       however this may not do anything.
    4241005 */
    425 var StateMachine = function( states ) {
    426     // @todo This is dead code. The states collection gets created in media.view.Frame._createStates.
    427     this.states = new Backbone.Collection( states );
    428 };
    429 
    430 // Use Backbone's self-propagating `extend` inheritance method.
    431 StateMachine.extend = Backbone.Model.extend;
    432 
    433 _.extend( StateMachine.prototype, Backbone.Events, {
    434     /**
    435      * Fetch a state.
     1006var State = wp.media.controller.State,
     1007    Library = wp.media.controller.Library,
     1008    l10n = wp.media.view.l10n,
     1009    ImageDetails;
     1010
     1011ImageDetails = State.extend({
     1012    defaults: _.defaults({
     1013        id:       'image-details',
     1014        title:    l10n.imageDetailsTitle,
     1015        content:  'image-details',
     1016        menu:     false,
     1017        router:   false,
     1018        toolbar:  'image-details',
     1019        editing:  false,
     1020        priority: 60
     1021    }, Library.prototype.defaults ),
     1022
     1023    /**
     1024     * @since 3.9.0
    4361025     *
    437      * If no `id` is provided, returns the active state.
    438      *
    439      * Implicitly creates states.
    440      *
    441      * Ensure that the `states` collection exists so the `StateMachine`
    442      *   can be used as a mixin.
    443      *
    444      * @since 3.5.0
    445      *
    446      * @param {string} id
    447      * @returns {wp.media.controller.State} Returns a State model
    448      *   from the StateMachine collection
    449      */
    450     state: function( id ) {
    451         this.states = this.states || new Backbone.Collection();
    452 
    453         // Default to the active state.
    454         id = id || this._state;
    455 
    456         if ( id && ! this.states.get( id ) ) {
    457             this.states.add({ id: id });
    458         }
    459         return this.states.get( id );
    460     },
    461 
    462     /**
    463      * Sets the active state.
    464      *
    465      * Bail if we're trying to select the current state, if we haven't
    466      * created the `states` collection, or are trying to select a state
    467      * that does not exist.
    468      *
    469      * @since 3.5.0
    470      *
    471      * @param {string} id
    472      *
    473      * @fires wp.media.controller.State#deactivate
    474      * @fires wp.media.controller.State#activate
    475      *
    476      * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining
    477      */
    478     setState: function( id ) {
    479         var previous = this.state();
    480 
    481         if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) {
    482             return this;
    483         }
    484 
    485         if ( previous ) {
    486             previous.trigger('deactivate');
    487             this._lastState = previous.id;
    488         }
    489 
    490         this._state = id;
    491         this.state().trigger('activate');
    492 
    493         return this;
    494     },
    495 
    496     /**
    497      * Returns the previous active state.
    498      *
    499      * Call the `state()` method with no parameters to retrieve the current
    500      * active state.
    501      *
    502      * @since 3.5.0
    503      *
    504      * @returns {wp.media.controller.State} Returns a State model
    505      *    from the StateMachine collection
    506      */
    507     lastState: function() {
    508         if ( this._lastState ) {
    509             return this.state( this._lastState );
    510         }
     1026     * @param options Attributes
     1027     */
     1028    initialize: function( options ) {
     1029        this.image = options.image;
     1030        State.prototype.initialize.apply( this, arguments );
     1031    },
     1032
     1033    /**
     1034     * @since 3.9.0
     1035     */
     1036    activate: function() {
     1037        this.frame.modal.$el.addClass('image-details');
    5111038    }
    5121039});
    5131040
    514 // Map all event binding and triggering on a StateMachine to its `states` collection.
    515 _.each([ 'on', 'off', 'trigger' ], function( method ) {
    516     /**
    517      * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining.
    518      */
    519     StateMachine.prototype[ method ] = function() {
    520         // Ensure that the `states` collection exists so the `StateMachine`
    521         // can be used as a mixin.
    522         this.states = this.states || new Backbone.Collection();
    523         // Forward the method to the `states` collection.
    524         this.states[ method ].apply( this.states, arguments );
    525         return this;
    526     };
    527 });
    528 
    529 module.exports = StateMachine;
    530 
    531 
    532 /***/ }),
    533 /* 29 */
    534 /***/ (function(module, exports) {
    535 
    536 /**
    537  * wp.media.controller.State
    538  *
    539  * A state is a step in a workflow that when set will trigger the controllers
    540  * for the regions to be updated as specified in the frame.
    541  *
    542  * A state has an event-driven lifecycle:
    543  *
    544  *     'ready'      triggers when a state is added to a state machine's collection.
    545  *     'activate'   triggers when a state is activated by a state machine.
    546  *     'deactivate' triggers when a state is deactivated by a state machine.
    547  *     'reset'      is not triggered automatically. It should be invoked by the
    548  *                  proper controller to reset the state to its default.
    549  *
    550  * @class
    551  * @augments Backbone.Model
    552  */
    553 var State = Backbone.Model.extend({
    554     /**
    555      * Constructor.
    556      *
    557      * @since 3.5.0
    558      */
    559     constructor: function() {
    560         this.on( 'activate', this._preActivate, this );
    561         this.on( 'activate', this.activate, this );
    562         this.on( 'activate', this._postActivate, this );
    563         this.on( 'deactivate', this._deactivate, this );
    564         this.on( 'deactivate', this.deactivate, this );
    565         this.on( 'reset', this.reset, this );
    566         this.on( 'ready', this._ready, this );
    567         this.on( 'ready', this.ready, this );
    568         /**
    569          * Call parent constructor with passed arguments
    570          */
    571         Backbone.Model.apply( this, arguments );
    572         this.on( 'change:menu', this._updateMenu, this );
    573     },
    574     /**
    575      * Ready event callback.
    576      *
    577      * @abstract
    578      * @since 3.5.0
    579      */
    580     ready: function() {},
    581 
    582     /**
    583      * Activate event callback.
    584      *
    585      * @abstract
    586      * @since 3.5.0
    587      */
    588     activate: function() {},
    589 
    590     /**
    591      * Deactivate event callback.
    592      *
    593      * @abstract
    594      * @since 3.5.0
    595      */
    596     deactivate: function() {},
    597 
    598     /**
    599      * Reset event callback.
    600      *
    601      * @abstract
    602      * @since 3.5.0
    603      */
    604     reset: function() {},
    605 
    606     /**
    607      * @access private
    608      * @since 3.5.0
    609      */
    610     _ready: function() {
    611         this._updateMenu();
    612     },
    613 
    614     /**
    615      * @access private
    616      * @since 3.5.0
    617     */
    618     _preActivate: function() {
    619         this.active = true;
    620     },
    621 
    622     /**
    623      * @access private
    624      * @since 3.5.0
    625      */
    626     _postActivate: function() {
    627         this.on( 'change:menu', this._menu, this );
    628         this.on( 'change:titleMode', this._title, this );
    629         this.on( 'change:content', this._content, this );
    630         this.on( 'change:toolbar', this._toolbar, this );
    631 
    632         this.frame.on( 'title:render:default', this._renderTitle, this );
    633 
    634         this._title();
    635         this._menu();
    636         this._toolbar();
    637         this._content();
    638         this._router();
    639     },
    640 
    641     /**
    642      * @access private
    643      * @since 3.5.0
    644      */
    645     _deactivate: function() {
    646         this.active = false;
    647 
    648         this.frame.off( 'title:render:default', this._renderTitle, this );
    649 
    650         this.off( 'change:menu', this._menu, this );
    651         this.off( 'change:titleMode', this._title, this );
    652         this.off( 'change:content', this._content, this );
    653         this.off( 'change:toolbar', this._toolbar, this );
    654     },
    655 
    656     /**
    657      * @access private
    658      * @since 3.5.0
    659      */
    660     _title: function() {
    661         this.frame.title.render( this.get('titleMode') || 'default' );
    662     },
    663 
    664     /**
    665      * @access private
    666      * @since 3.5.0
    667      */
    668     _renderTitle: function( view ) {
    669         view.$el.text( this.get('title') || '' );
    670     },
    671 
    672     /**
    673      * @access private
    674      * @since 3.5.0
    675      */
    676     _router: function() {
    677         var router = this.frame.router,
    678             mode = this.get('router'),
    679             view;
    680 
    681         this.frame.$el.toggleClass( 'hide-router', ! mode );
    682         if ( ! mode ) {
    683             return;
    684         }
    685 
    686         this.frame.router.render( mode );
    687 
    688         view = router.get();
    689         if ( view && view.select ) {
    690             view.select( this.frame.content.mode() );
    691         }
    692     },
    693 
    694     /**
    695      * @access private
    696      * @since 3.5.0
    697      */
    698     _menu: function() {
    699         var menu = this.frame.menu,
    700             mode = this.get('menu'),
    701             view;
    702 
    703         this.frame.$el.toggleClass( 'hide-menu', ! mode );
    704         if ( ! mode ) {
    705             return;
    706         }
    707 
    708         menu.mode( mode );
    709 
    710         view = menu.get();
    711         if ( view && view.select ) {
    712             view.select( this.id );
    713         }
    714     },
    715 
    716     /**
    717      * @access private
    718      * @since 3.5.0
    719      */
    720     _updateMenu: function() {
    721         var previous = this.previous('menu'),
    722             menu = this.get('menu');
    723 
    724         if ( previous ) {
    725             this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
    726         }
    727 
    728         if ( menu ) {
    729             this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
    730         }
    731     },
    732 
    733     /**
    734      * Create a view in the media menu for the state.
    735      *
    736      * @access private
    737      * @since 3.5.0
    738      *
    739      * @param {media.view.Menu} view The menu view.
    740      */
    741     _renderMenu: function( view ) {
    742         var menuItem = this.get('menuItem'),
    743             title = this.get('title'),
    744             priority = this.get('priority');
    745 
    746         if ( ! menuItem && title ) {
    747             menuItem = { text: title };
    748 
    749             if ( priority ) {
    750                 menuItem.priority = priority;
    751             }
    752         }
    753 
    754         if ( ! menuItem ) {
    755             return;
    756         }
    757 
    758         view.set( this.id, menuItem );
    759     }
    760 });
    761 
    762 _.each(['toolbar','content'], function( region ) {
    763     /**
    764      * @access private
    765      */
    766     State.prototype[ '_' + region ] = function() {
    767         var mode = this.get( region );
    768         if ( mode ) {
    769             this.frame[ region ].render( mode );
    770         }
    771     };
    772 });
    773 
    774 module.exports = State;
    775 
    776 
    777 /***/ }),
    778 /* 30 */
    779 /***/ (function(module, exports) {
    780 
    781 /**
    782  * wp.media.selectionSync
    783  *
    784  * Sync an attachments selection in a state with another state.
    785  *
    786  * Allows for selecting multiple images in the Insert Media workflow, and then
    787  * switching to the Insert Gallery workflow while preserving the attachments selection.
    788  *
    789  * @mixin
    790  */
    791 var selectionSync = {
    792     /**
    793      * @since 3.5.0
    794      */
    795     syncSelection: function() {
    796         var selection = this.get('selection'),
    797             manager = this.frame._selection;
    798 
    799         if ( ! this.get('syncSelection') || ! manager || ! selection ) {
    800             return;
    801         }
    802 
    803         // If the selection supports multiple items, validate the stored
    804         // attachments based on the new selection's conditions. Record
    805         // the attachments that are not included; we'll maintain a
    806         // reference to those. Other attachments are considered in flux.
    807         if ( selection.multiple ) {
    808             selection.reset( [], { silent: true });
    809             selection.validateAll( manager.attachments );
    810             manager.difference = _.difference( manager.attachments.models, selection.models );
    811         }
    812 
    813         // Sync the selection's single item with the master.
    814         selection.single( manager.single );
    815     },
    816 
    817     /**
    818      * Record the currently active attachments, which is a combination
    819      * of the selection's attachments and the set of selected
    820      * attachments that this specific selection considered invalid.
    821      * Reset the difference and record the single attachment.
    822      *
    823      * @since 3.5.0
    824      */
    825     recordSelection: function() {
    826         var selection = this.get('selection'),
    827             manager = this.frame._selection;
    828 
    829         if ( ! this.get('syncSelection') || ! manager || ! selection ) {
    830             return;
    831         }
    832 
    833         if ( selection.multiple ) {
    834             manager.attachments.reset( selection.toArray().concat( manager.difference ) );
    835             manager.difference = [];
    836         } else {
    837             manager.attachments.add( selection.toArray() );
    838         }
    839 
    840         manager.single = selection._single;
    841     }
    842 };
    843 
    844 module.exports = selectionSync;
    845 
    846 
    847 /***/ }),
    848 /* 31 */
    849 /***/ (function(module, exports) {
    850 
     1041module.exports = ImageDetails;
     1042
     1043},{}],11:[function(require,module,exports){
    8511044/**
    8521045 * wp.media.controller.Library
     
    11201313module.exports = Library;
    11211314
    1122 
    1123 /***/ }),
    1124 /* 32 */
    1125 /***/ (function(module, exports) {
    1126 
     1315},{}],12:[function(require,module,exports){
    11271316/**
    1128  * wp.media.controller.ImageDetails
    1129  *
    1130  * A state for editing the attachment display settings of an image that's been
    1131  * inserted into the editor.
    1132  *
    1133  * @class
    1134  * @augments wp.media.controller.State
    1135  * @augments Backbone.Model
    1136  *
    1137  * @param {object}                    [attributes]                       The attributes hash passed to the state.
    1138  * @param {string}                    [attributes.id=image-details]      Unique identifier.
    1139  * @param {string}                    [attributes.title=Image Details]   Title for the state. Displays in the frame's title region.
    1140  * @param {wp.media.model.Attachment} attributes.image                   The image's model.
    1141  * @param {string|false}              [attributes.content=image-details] Initial mode for the content region.
    1142  * @param {string|false}              [attributes.menu=false]            Initial mode for the menu region.
    1143  * @param {string|false}              [attributes.router=false]          Initial mode for the router region.
    1144  * @param {string|false}              [attributes.toolbar=image-details] Initial mode for the toolbar region.
    1145  * @param {boolean}                   [attributes.editing=false]         Unused.
    1146  * @param {int}                       [attributes.priority=60]           Unused.
    1147  *
    1148  * @todo This state inherits some defaults from media.controller.Library.prototype.defaults,
    1149  *       however this may not do anything.
    1150  */
    1151 var State = wp.media.controller.State,
    1152     Library = wp.media.controller.Library,
    1153     l10n = wp.media.view.l10n,
    1154     ImageDetails;
    1155 
    1156 ImageDetails = State.extend({
    1157     defaults: _.defaults({
    1158         id:       'image-details',
    1159         title:    l10n.imageDetailsTitle,
    1160         content:  'image-details',
    1161         menu:     false,
    1162         router:   false,
    1163         toolbar:  'image-details',
    1164         editing:  false,
    1165         priority: 60
    1166     }, Library.prototype.defaults ),
    1167 
    1168     /**
    1169      * @since 3.9.0
    1170      *
    1171      * @param options Attributes
    1172      */
    1173     initialize: function( options ) {
    1174         this.image = options.image;
    1175         State.prototype.initialize.apply( this, arguments );
    1176     },
    1177 
    1178     /**
    1179      * @since 3.9.0
    1180      */
    1181     activate: function() {
    1182         this.frame.modal.$el.addClass('image-details');
    1183     }
    1184 });
    1185 
    1186 module.exports = ImageDetails;
    1187 
    1188 
    1189 /***/ }),
    1190 /* 33 */
    1191 /***/ (function(module, exports) {
    1192 
    1193 /**
    1194  * wp.media.controller.GalleryEdit
    1195  *
    1196  * A state for editing a gallery's images and settings.
     1317 * wp.media.controller.MediaLibrary
    11971318 *
    11981319 * @class
     
    12001321 * @augments wp.media.controller.State
    12011322 * @augments Backbone.Model
    1202  *
    1203  * @param {object}                     [attributes]                       The attributes hash passed to the state.
    1204  * @param {string}                     [attributes.id=gallery-edit]       Unique identifier.
    1205  * @param {string}                     [attributes.title=Edit Gallery]    Title for the state. Displays in the frame's title region.
    1206  * @param {wp.media.model.Attachments} [attributes.library]               The collection of attachments in the gallery.
    1207  *                                                                        If one is not supplied, an empty media.model.Selection collection is created.
    1208  * @param {boolean}                    [attributes.multiple=false]        Whether multi-select is enabled.
    1209  * @param {boolean}                    [attributes.searchable=false]      Whether the library is searchable.
    1210  * @param {boolean}                    [attributes.sortable=true]         Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
    1211  * @param {boolean}                    [attributes.date=true]             Whether to show the date filter in the browser's toolbar.
    1212  * @param {string|false}               [attributes.content=browse]        Initial mode for the content region.
    1213  * @param {string|false}               [attributes.toolbar=image-details] Initial mode for the toolbar region.
    1214  * @param {boolean}                    [attributes.describe=true]         Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
    1215  * @param {boolean}                    [attributes.displaySettings=true]  Whether to show the attachment display settings interface.
    1216  * @param {boolean}                    [attributes.dragInfo=true]         Whether to show instructional text about the attachments being sortable.
    1217  * @param {int}                        [attributes.idealColumnWidth=170]  The ideal column width in pixels for attachments.
    1218  * @param {boolean}                    [attributes.editing=false]         Whether the gallery is being created, or editing an existing instance.
    1219  * @param {int}                        [attributes.priority=60]           The priority for the state link in the media menu.
    1220  * @param {boolean}                    [attributes.syncSelection=false]   Whether the Attachments selection should be persisted from the last state.
    1221  *                                                                        Defaults to false for this state, because the library passed in  *is* the selection.
    1222  * @param {view}                       [attributes.AttachmentView]        The single `Attachment` view to be used in the `Attachments`.
    1223  *                                                                        If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
    12241323 */
    12251324var Library = wp.media.controller.Library,
    1226     l10n = wp.media.view.l10n,
    1227     GalleryEdit;
    1228 
    1229 GalleryEdit = Library.extend({
    1230     defaults: {
    1231         id:               'gallery-edit',
    1232         title:            l10n.editGalleryTitle,
    1233         multiple:         false,
    1234         searchable:       false,
    1235         sortable:         true,
    1236         date:             false,
    1237         display:          false,
    1238         content:          'browse',
    1239         toolbar:          'gallery-edit',
    1240         describe:         true,
    1241         displaySettings:  true,
    1242         dragInfo:         true,
    1243         idealColumnWidth: 170,
    1244         editing:          false,
    1245         priority:         60,
    1246         syncSelection:    false
    1247     },
    1248 
    1249     /**
    1250      * @since 3.5.0
    1251      */
    1252     initialize: function() {
    1253         // If we haven't been provided a `library`, create a `Selection`.
    1254         if ( ! this.get('library') ) {
    1255             this.set( 'library', new wp.media.model.Selection() );
    1256         }
    1257 
    1258         // The single `Attachment` view to be used in the `Attachments` view.
    1259         if ( ! this.get('AttachmentView') ) {
    1260             this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
    1261         }
     1325    MediaLibrary;
     1326
     1327MediaLibrary = Library.extend({
     1328    defaults: _.defaults({
     1329        // Attachments browser defaults. @see media.view.AttachmentsBrowser
     1330        filterable:      'uploaded',
     1331
     1332        displaySettings: false,
     1333        priority:        80,
     1334        syncSelection:   false
     1335    }, Library.prototype.defaults ),
     1336
     1337    /**
     1338     * @since 3.9.0
     1339     *
     1340     * @param options
     1341     */
     1342    initialize: function( options ) {
     1343        this.media = options.media;
     1344        this.type = options.type;
     1345        this.set( 'library', wp.media.query({ type: this.type }) );
    12621346
    12631347        Library.prototype.initialize.apply( this, arguments );
     
    12651349
    12661350    /**
    1267      * @since 3.5.0
     1351     * @since 3.9.0
    12681352     */
    12691353    activate: function() {
    1270         var library = this.get('library');
    1271 
    1272         // Limit the library to images only.
    1273         library.props.set( 'type', 'image' );
    1274 
    1275         // Watch for uploaded attachments.
    1276         this.get('library').observe( wp.Uploader.queue );
    1277 
    1278         this.frame.on( 'content:render:browse', this.gallerySettings, this );
    1279 
    1280         Library.prototype.activate.apply( this, arguments );
    1281     },
    1282 
    1283     /**
    1284      * @since 3.5.0
    1285      */
    1286     deactivate: function() {
    1287         // Stop watching for uploaded attachments.
    1288         this.get('library').unobserve( wp.Uploader.queue );
    1289 
    1290         this.frame.off( 'content:render:browse', this.gallerySettings, this );
    1291 
    1292         Library.prototype.deactivate.apply( this, arguments );
    1293     },
    1294 
    1295     /**
    1296      * @since 3.5.0
    1297      *
    1298      * @param browser
    1299      */
    1300     gallerySettings: function( browser ) {
    1301         if ( ! this.get('displaySettings') ) {
    1302             return;
    1303         }
    1304 
    1305         var library = this.get('library');
    1306 
    1307         if ( ! library || ! browser ) {
    1308             return;
    1309         }
    1310 
    1311         library.gallery = library.gallery || new Backbone.Model();
    1312 
    1313         browser.sidebar.set({
    1314             gallery: new wp.media.view.Settings.Gallery({
    1315                 controller: this,
    1316                 model:      library.gallery,
    1317                 priority:   40
    1318             })
    1319         });
    1320 
    1321         browser.toolbar.set( 'reverse', {
    1322             text:     l10n.reverseOrder,
    1323             priority: 80,
    1324 
    1325             click: function() {
    1326                 library.reset( library.toArray().reverse() );
    1327             }
    1328         });
    1329     }
    1330 });
    1331 
    1332 module.exports = GalleryEdit;
    1333 
    1334 
    1335 /***/ }),
    1336 /* 34 */
    1337 /***/ (function(module, exports) {
    1338 
    1339 /**
    1340  * wp.media.controller.GalleryAdd
    1341  *
    1342  * A state for selecting more images to add to a gallery.
    1343  *
    1344  * @class
    1345  * @augments wp.media.controller.Library
    1346  * @augments wp.media.controller.State
    1347  * @augments Backbone.Model
    1348  *
    1349  * @param {object}                     [attributes]                         The attributes hash passed to the state.
    1350  * @param {string}                     [attributes.id=gallery-library]      Unique identifier.
    1351  * @param {string}                     [attributes.title=Add to Gallery]    Title for the state. Displays in the frame's title region.
    1352  * @param {boolean}                    [attributes.multiple=add]            Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
    1353  * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
    1354  *                                                                          If one is not supplied, a collection of all images will be created.
    1355  * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
    1356  *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
    1357  * @param {string}                     [attributes.menu=gallery]            Initial mode for the menu region.
    1358  * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
    1359  *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
    1360  * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
    1361  * @param {string}                     [attributes.toolbar=gallery-add]     Initial mode for the toolbar region.
    1362  * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
    1363  * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
    1364  * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
    1365  * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
    1366  * @param {int}                        [attributes.priority=100]            The priority for the state link in the media menu.
    1367  * @param {boolean}                    [attributes.syncSelection=false]     Whether the Attachments selection should be persisted from the last state.
    1368  *                                                                          Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
    1369  */
    1370 var Selection = wp.media.model.Selection,
    1371     Library = wp.media.controller.Library,
    1372     l10n = wp.media.view.l10n,
    1373     GalleryAdd;
    1374 
    1375 GalleryAdd = Library.extend({
    1376     defaults: _.defaults({
    1377         id:            'gallery-library',
    1378         title:         l10n.addToGalleryTitle,
    1379         multiple:      'add',
    1380         filterable:    'uploaded',
    1381         menu:          'gallery',
    1382         toolbar:       'gallery-add',
    1383         priority:      100,
    1384         syncSelection: false
    1385     }, Library.prototype.defaults ),
    1386 
    1387     /**
    1388      * @since 3.5.0
    1389      */
    1390     initialize: function() {
    1391         // If a library wasn't supplied, create a library of images.
    1392         if ( ! this.get('library') ) {
    1393             this.set( 'library', wp.media.query({ type: 'image' }) );
    1394         }
    1395 
    1396         Library.prototype.initialize.apply( this, arguments );
    1397     },
    1398 
    1399     /**
    1400      * @since 3.5.0
    1401      */
    1402     activate: function() {
    1403         var library = this.get('library'),
    1404             edit    = this.frame.state('gallery-edit').get('library');
    1405 
    1406         if ( this.editLibrary && this.editLibrary !== edit ) {
    1407             library.unobserve( this.editLibrary );
    1408         }
    1409 
    1410         // Accepts attachments that exist in the original library and
    1411         // that do not exist in gallery's library.
    1412         library.validator = function( attachment ) {
    1413             return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
    1414         };
    1415 
    1416         // Reset the library to ensure that all attachments are re-added
    1417         // to the collection. Do so silently, as calling `observe` will
    1418         // trigger the `reset` event.
    1419         library.reset( library.mirroring.models, { silent: true });
    1420         library.observe( edit );
    1421         this.editLibrary = edit;
    1422 
     1354        // @todo this should use this.frame.
     1355        if ( wp.media.frame.lastMime ) {
     1356            this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) );
     1357            delete wp.media.frame.lastMime;
     1358        }
    14231359        Library.prototype.activate.apply( this, arguments );
    14241360    }
    14251361});
    14261362
    1427 module.exports = GalleryAdd;
    1428 
    1429 
    1430 /***/ }),
    1431 /* 35 */
    1432 /***/ (function(module, exports) {
    1433 
     1363module.exports = MediaLibrary;
     1364
     1365},{}],13:[function(require,module,exports){
    14341366/**
    1435  * wp.media.controller.CollectionEdit
    1436  *
    1437  * A state for editing a collection, which is used by audio and video playlists,
    1438  * and can be used for other collections.
     1367 * wp.media.controller.Region
     1368 *
     1369 * A region is a persistent application layout area.
     1370 *
     1371 * A region assumes one mode at any time, and can be switched to another.
     1372 *
     1373 * When mode changes, events are triggered on the region's parent view.
     1374 * The parent view will listen to specific events and fill the region with an
     1375 * appropriate view depending on mode. For example, a frame listens for the
     1376 * 'browse' mode t be activated on the 'content' view and then fills the region
     1377 * with an AttachmentsBrowser view.
    14391378 *
    14401379 * @class
    1441  * @augments wp.media.controller.Library
    1442  * @augments wp.media.controller.State
    1443  * @augments Backbone.Model
    1444  *
    1445  * @param {object}                     [attributes]                      The attributes hash passed to the state.
    1446  * @param {string}                     attributes.title                  Title for the state. Displays in the media menu and the frame's title region.
    1447  * @param {wp.media.model.Attachments} [attributes.library]              The attachments collection to edit.
    1448  *                                                                       If one is not supplied, an empty media.model.Selection collection is created.
    1449  * @param {boolean}                    [attributes.multiple=false]       Whether multi-select is enabled.
    1450  * @param {string}                     [attributes.content=browse]       Initial mode for the content region.
    1451  * @param {string}                     attributes.menu                   Initial mode for the menu region. @todo this needs a better explanation.
    1452  * @param {boolean}                    [attributes.searchable=false]     Whether the library is searchable.
    1453  * @param {boolean}                    [attributes.sortable=true]        Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
    1454  * @param {boolean}                    [attributes.date=true]            Whether to show the date filter in the browser's toolbar.
    1455  * @param {boolean}                    [attributes.describe=true]        Whether to offer UI to describe the attachments - e.g. captioning images in a gallery.
    1456  * @param {boolean}                    [attributes.dragInfo=true]        Whether to show instructional text about the attachments being sortable.
    1457  * @param {boolean}                    [attributes.dragInfoText]         Instructional text about the attachments being sortable.
    1458  * @param {int}                        [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
    1459  * @param {boolean}                    [attributes.editing=false]        Whether the gallery is being created, or editing an existing instance.
    1460  * @param {int}                        [attributes.priority=60]          The priority for the state link in the media menu.
    1461  * @param {boolean}                    [attributes.syncSelection=false]  Whether the Attachments selection should be persisted from the last state.
    1462  *                                                                       Defaults to false for this state, because the library passed in  *is* the selection.
    1463  * @param {view}                       [attributes.SettingsView]         The view to edit the collection instance settings (e.g. Playlist settings with "Show tracklist" checkbox).
    1464  * @param {view}                       [attributes.AttachmentView]       The single `Attachment` view to be used in the `Attachments`.
    1465  *                                                                       If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
    1466  * @param {string}                     attributes.type                   The collection's media type. (e.g. 'video').
    1467  * @param {string}                     attributes.collectionType         The collection type. (e.g. 'playlist').
     1380 *
     1381 * @param {object}        options          Options hash for the region.
     1382 * @param {string}        options.id       Unique identifier for the region.
     1383 * @param {Backbone.View} options.view     A parent view the region exists within.
     1384 * @param {string}        options.selector jQuery selector for the region within the parent view.
    14681385 */
    1469 var Library = wp.media.controller.Library,
    1470     l10n = wp.media.view.l10n,
    1471     $ = jQuery,
    1472     CollectionEdit;
    1473 
    1474 CollectionEdit = Library.extend({
    1475     defaults: {
    1476         multiple:         false,
    1477         sortable:         true,
    1478         date:             false,
    1479         searchable:       false,
    1480         content:          'browse',
    1481         describe:         true,
    1482         dragInfo:         true,
    1483         idealColumnWidth: 170,
    1484         editing:          false,
    1485         priority:         60,
    1486         SettingsView:     false,
    1487         syncSelection:    false
    1488     },
    1489 
    1490     /**
    1491      * @since 3.9.0
    1492      */
    1493     initialize: function() {
    1494         var collectionType = this.get('collectionType');
    1495 
    1496         if ( 'video' === this.get( 'type' ) ) {
    1497             collectionType = 'video-' + collectionType;
    1498         }
    1499 
    1500         this.set( 'id', collectionType + '-edit' );
    1501         this.set( 'toolbar', collectionType + '-edit' );
    1502 
    1503         // If we haven't been provided a `library`, create a `Selection`.
    1504         if ( ! this.get('library') ) {
    1505             this.set( 'library', new wp.media.model.Selection() );
    1506         }
    1507         // The single `Attachment` view to be used in the `Attachments` view.
    1508         if ( ! this.get('AttachmentView') ) {
    1509             this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
    1510         }
    1511         Library.prototype.initialize.apply( this, arguments );
    1512     },
    1513 
    1514     /**
    1515      * @since 3.9.0
    1516      */
    1517     activate: function() {
    1518         var library = this.get('library');
    1519 
    1520         // Limit the library to images only.
    1521         library.props.set( 'type', this.get( 'type' ) );
    1522 
    1523         // Watch for uploaded attachments.
    1524         this.get('library').observe( wp.Uploader.queue );
    1525 
    1526         this.frame.on( 'content:render:browse', this.renderSettings, this );
    1527 
    1528         Library.prototype.activate.apply( this, arguments );
    1529     },
    1530 
    1531     /**
    1532      * @since 3.9.0
    1533      */
    1534     deactivate: function() {
    1535         // Stop watching for uploaded attachments.
    1536         this.get('library').unobserve( wp.Uploader.queue );
    1537 
    1538         this.frame.off( 'content:render:browse', this.renderSettings, this );
    1539 
    1540         Library.prototype.deactivate.apply( this, arguments );
    1541     },
    1542 
    1543     /**
    1544      * Render the collection embed settings view in the browser sidebar.
     1386var Region = function( options ) {
     1387    _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
     1388};
     1389
     1390// Use Backbone's self-propagating `extend` inheritance method.
     1391Region.extend = Backbone.Model.extend;
     1392
     1393_.extend( Region.prototype, {
     1394    /**
     1395     * Activate a mode.
    15451396     *
    1546      * @todo This is against the pattern elsewhere in media. Typically the frame
    1547      *       is responsible for adding region mode callbacks. Explain.
     1397     * @since 3.5.0
    15481398     *
    1549      * @since 3.9.0
     1399     * @param {string} mode
    15501400     *
    1551      * @param {wp.media.view.attachmentsBrowser} The attachments browser view.
    1552      */
    1553     renderSettings: function( attachmentsBrowserView ) {
    1554         var library = this.get('library'),
    1555             collectionType = this.get('collectionType'),
    1556             dragInfoText = this.get('dragInfoText'),
    1557             SettingsView = this.get('SettingsView'),
    1558             obj = {};
    1559 
    1560         if ( ! library || ! attachmentsBrowserView ) {
     1401     * @fires this.view#{this.id}:activate:{this._mode}
     1402     * @fires this.view#{this.id}:activate
     1403     * @fires this.view#{this.id}:deactivate:{this._mode}
     1404     * @fires this.view#{this.id}:deactivate
     1405     *
     1406     * @returns {wp.media.controller.Region} Returns itself to allow chaining.
     1407     */
     1408    mode: function( mode ) {
     1409        if ( ! mode ) {
     1410            return this._mode;
     1411        }
     1412        // Bail if we're trying to change to the current mode.
     1413        if ( mode === this._mode ) {
     1414            return this;
     1415        }
     1416
     1417        /**
     1418         * Region mode deactivation event.
     1419         *
     1420         * @event this.view#{this.id}:deactivate:{this._mode}
     1421         * @event this.view#{this.id}:deactivate
     1422         */
     1423        this.trigger('deactivate');
     1424
     1425        this._mode = mode;
     1426        this.render( mode );
     1427
     1428        /**
     1429         * Region mode activation event.
     1430         *
     1431         * @event this.view#{this.id}:activate:{this._mode}
     1432         * @event this.view#{this.id}:activate
     1433         */
     1434        this.trigger('activate');
     1435        return this;
     1436    },
     1437    /**
     1438     * Render a mode.
     1439     *
     1440     * @since 3.5.0
     1441     *
     1442     * @param {string} mode
     1443     *
     1444     * @fires this.view#{this.id}:create:{this._mode}
     1445     * @fires this.view#{this.id}:create
     1446     * @fires this.view#{this.id}:render:{this._mode}
     1447     * @fires this.view#{this.id}:render
     1448     *
     1449     * @returns {wp.media.controller.Region} Returns itself to allow chaining
     1450     */
     1451    render: function( mode ) {
     1452        // If the mode isn't active, activate it.
     1453        if ( mode && mode !== this._mode ) {
     1454            return this.mode( mode );
     1455        }
     1456
     1457        var set = { view: null },
     1458            view;
     1459
     1460        /**
     1461         * Create region view event.
     1462         *
     1463         * Region view creation takes place in an event callback on the frame.
     1464         *
     1465         * @event this.view#{this.id}:create:{this._mode}
     1466         * @event this.view#{this.id}:create
     1467         */
     1468        this.trigger( 'create', set );
     1469        view = set.view;
     1470
     1471        /**
     1472         * Render region view event.
     1473         *
     1474         * Region view creation takes place in an event callback on the frame.
     1475         *
     1476         * @event this.view#{this.id}:create:{this._mode}
     1477         * @event this.view#{this.id}:create
     1478         */
     1479        this.trigger( 'render', view );
     1480        if ( view ) {
     1481            this.set( view );
     1482        }
     1483        return this;
     1484    },
     1485
     1486    /**
     1487     * Get the region's view.
     1488     *
     1489     * @since 3.5.0
     1490     *
     1491     * @returns {wp.media.View}
     1492     */
     1493    get: function() {
     1494        return this.view.views.first( this.selector );
     1495    },
     1496
     1497    /**
     1498     * Set the region's view as a subview of the frame.
     1499     *
     1500     * @since 3.5.0
     1501     *
     1502     * @param {Array|Object} views
     1503     * @param {Object} [options={}]
     1504     * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining
     1505     */
     1506    set: function( views, options ) {
     1507        if ( options ) {
     1508            options.add = false;
     1509        }
     1510        return this.view.views.set( this.selector, views, options );
     1511    },
     1512
     1513    /**
     1514     * Trigger regional view events on the frame.
     1515     *
     1516     * @since 3.5.0
     1517     *
     1518     * @param {string} event
     1519     * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining.
     1520     */
     1521    trigger: function( event ) {
     1522        var base, args;
     1523
     1524        if ( ! this._mode ) {
    15611525            return;
    15621526        }
    15631527
    1564         library[ collectionType ] = library[ collectionType ] || new Backbone.Model();
    1565 
    1566         obj[ collectionType ] = new SettingsView({
    1567             controller: this,
    1568             model:      library[ collectionType ],
    1569             priority:   40
    1570         });
    1571 
    1572         attachmentsBrowserView.sidebar.set( obj );
    1573 
    1574         if ( dragInfoText ) {
    1575             attachmentsBrowserView.toolbar.set( 'dragInfo', new wp.media.View({
    1576                 el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0],
    1577                 priority: -40
    1578             }) );
    1579         }
    1580 
    1581         // Add the 'Reverse order' button to the toolbar.
    1582         attachmentsBrowserView.toolbar.set( 'reverse', {
    1583             text:     l10n.reverseOrder,
    1584             priority: 80,
    1585 
    1586             click: function() {
    1587                 library.reset( library.toArray().reverse() );
    1588             }
    1589         });
     1528        args = _.toArray( arguments );
     1529        base = this.id + ':' + event;
     1530
     1531        // Trigger `{this.id}:{event}:{this._mode}` event on the frame.
     1532        args[0] = base + ':' + this._mode;
     1533        this.view.trigger.apply( this.view, args );
     1534
     1535        // Trigger `{this.id}:{event}` event on the frame.
     1536        args[0] = base;
     1537        this.view.trigger.apply( this.view, args );
     1538        return this;
    15901539    }
    15911540});
    15921541
    1593 module.exports = CollectionEdit;
    1594 
    1595 
    1596 /***/ }),
    1597 /* 36 */
    1598 /***/ (function(module, exports) {
    1599 
    1600 /**
    1601  * wp.media.controller.CollectionAdd
    1602  *
    1603  * A state for adding attachments to a collection (e.g. video playlist).
    1604  *
    1605  * @class
    1606  * @augments wp.media.controller.Library
    1607  * @augments wp.media.controller.State
    1608  * @augments Backbone.Model
    1609  *
    1610  * @param {object}                     [attributes]                         The attributes hash passed to the state.
    1611  * @param {string}                     [attributes.id=library]      Unique identifier.
    1612  * @param {string}                     attributes.title                    Title for the state. Displays in the frame's title region.
    1613  * @param {boolean}                    [attributes.multiple=add]            Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
    1614  * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
    1615  *                                                                          If one is not supplied, a collection of attachments of the specified type will be created.
    1616  * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
    1617  *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
    1618  * @param {string}                     [attributes.menu=gallery]            Initial mode for the menu region.
    1619  * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
    1620  *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
    1621  * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
    1622  * @param {string}                     [attributes.toolbar=gallery-add]     Initial mode for the toolbar region.
    1623  * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
    1624  * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
    1625  * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
    1626  * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
    1627  * @param {int}                        [attributes.priority=100]            The priority for the state link in the media menu.
    1628  * @param {boolean}                    [attributes.syncSelection=false]     Whether the Attachments selection should be persisted from the last state.
    1629  *                                                                          Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
    1630  * @param {string}                     attributes.type                   The collection's media type. (e.g. 'video').
    1631  * @param {string}                     attributes.collectionType         The collection type. (e.g. 'playlist').
    1632  */
    1633 var Selection = wp.media.model.Selection,
    1634     Library = wp.media.controller.Library,
    1635     CollectionAdd;
    1636 
    1637 CollectionAdd = Library.extend({
    1638     defaults: _.defaults( {
    1639         // Selection defaults. @see media.model.Selection
    1640         multiple:      'add',
    1641         // Attachments browser defaults. @see media.view.AttachmentsBrowser
    1642         filterable:    'uploaded',
    1643 
    1644         priority:      100,
    1645         syncSelection: false
    1646     }, Library.prototype.defaults ),
    1647 
    1648     /**
    1649      * @since 3.9.0
    1650      */
    1651     initialize: function() {
    1652         var collectionType = this.get('collectionType');
    1653 
    1654         if ( 'video' === this.get( 'type' ) ) {
    1655             collectionType = 'video-' + collectionType;
    1656         }
    1657 
    1658         this.set( 'id', collectionType + '-library' );
    1659         this.set( 'toolbar', collectionType + '-add' );
    1660         this.set( 'menu', collectionType );
    1661 
    1662         // If we haven't been provided a `library`, create a `Selection`.
    1663         if ( ! this.get('library') ) {
    1664             this.set( 'library', wp.media.query({ type: this.get('type') }) );
    1665         }
    1666         Library.prototype.initialize.apply( this, arguments );
    1667     },
    1668 
    1669     /**
    1670      * @since 3.9.0
    1671      */
    1672     activate: function() {
    1673         var library = this.get('library'),
    1674             editLibrary = this.get('editLibrary'),
    1675             edit = this.frame.state( this.get('collectionType') + '-edit' ).get('library');
    1676 
    1677         if ( editLibrary && editLibrary !== edit ) {
    1678             library.unobserve( editLibrary );
    1679         }
    1680 
    1681         // Accepts attachments that exist in the original library and
    1682         // that do not exist in gallery's library.
    1683         library.validator = function( attachment ) {
    1684             return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
    1685         };
    1686 
    1687         // Reset the library to ensure that all attachments are re-added
    1688         // to the collection. Do so silently, as calling `observe` will
    1689         // trigger the `reset` event.
    1690         library.reset( library.mirroring.models, { silent: true });
    1691         library.observe( edit );
    1692         this.set('editLibrary', edit);
    1693 
    1694         Library.prototype.activate.apply( this, arguments );
    1695     }
    1696 });
    1697 
    1698 module.exports = CollectionAdd;
    1699 
    1700 
    1701 /***/ }),
    1702 /* 37 */
    1703 /***/ (function(module, exports) {
    1704 
    1705 /**
    1706  * wp.media.controller.FeaturedImage
    1707  *
    1708  * A state for selecting a featured image for a post.
    1709  *
    1710  * @class
    1711  * @augments wp.media.controller.Library
    1712  * @augments wp.media.controller.State
    1713  * @augments Backbone.Model
    1714  *
    1715  * @param {object}                     [attributes]                          The attributes hash passed to the state.
    1716  * @param {string}                     [attributes.id=featured-image]        Unique identifier.
    1717  * @param {string}                     [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region.
    1718  * @param {wp.media.model.Attachments} [attributes.library]                  The attachments collection to browse.
    1719  *                                                                           If one is not supplied, a collection of all images will be created.
    1720  * @param {boolean}                    [attributes.multiple=false]           Whether multi-select is enabled.
    1721  * @param {string}                     [attributes.content=upload]           Initial mode for the content region.
    1722  *                                                                           Overridden by persistent user setting if 'contentUserSetting' is true.
    1723  * @param {string}                     [attributes.menu=default]             Initial mode for the menu region.
    1724  * @param {string}                     [attributes.router=browse]            Initial mode for the router region.
    1725  * @param {string}                     [attributes.toolbar=featured-image]   Initial mode for the toolbar region.
    1726  * @param {int}                        [attributes.priority=60]              The priority for the state link in the media menu.
    1727  * @param {boolean}                    [attributes.searchable=true]          Whether the library is searchable.
    1728  * @param {boolean|string}             [attributes.filterable=false]         Whether the library is filterable, and if so what filters should be shown.
    1729  *                                                                           Accepts 'all', 'uploaded', or 'unattached'.
    1730  * @param {boolean}                    [attributes.sortable=true]            Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
    1731  * @param {boolean}                    [attributes.autoSelect=true]          Whether an uploaded attachment should be automatically added to the selection.
    1732  * @param {boolean}                    [attributes.describe=false]           Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
    1733  * @param {boolean}                    [attributes.contentUserSetting=true]  Whether the content region's mode should be set and persisted per user.
    1734  * @param {boolean}                    [attributes.syncSelection=true]       Whether the Attachments selection should be persisted from the last state.
    1735  */
    1736 var Attachment = wp.media.model.Attachment,
    1737     Library = wp.media.controller.Library,
    1738     l10n = wp.media.view.l10n,
    1739     FeaturedImage;
    1740 
    1741 FeaturedImage = Library.extend({
    1742     defaults: _.defaults({
    1743         id:            'featured-image',
    1744         title:         l10n.setFeaturedImageTitle,
    1745         multiple:      false,
    1746         filterable:    'uploaded',
    1747         toolbar:       'featured-image',
    1748         priority:      60,
    1749         syncSelection: true
    1750     }, Library.prototype.defaults ),
    1751 
    1752     /**
    1753      * @since 3.5.0
    1754      */
    1755     initialize: function() {
    1756         var library, comparator;
    1757 
    1758         // If we haven't been provided a `library`, create a `Selection`.
    1759         if ( ! this.get('library') ) {
    1760             this.set( 'library', wp.media.query({ type: 'image' }) );
    1761         }
    1762 
    1763         Library.prototype.initialize.apply( this, arguments );
    1764 
    1765         library    = this.get('library');
    1766         comparator = library.comparator;
    1767 
    1768         // Overload the library's comparator to push items that are not in
    1769         // the mirrored query to the front of the aggregate collection.
    1770         library.comparator = function( a, b ) {
    1771             var aInQuery = !! this.mirroring.get( a.cid ),
    1772                 bInQuery = !! this.mirroring.get( b.cid );
    1773 
    1774             if ( ! aInQuery && bInQuery ) {
    1775                 return -1;
    1776             } else if ( aInQuery && ! bInQuery ) {
    1777                 return 1;
    1778             } else {
    1779                 return comparator.apply( this, arguments );
    1780             }
    1781         };
    1782 
    1783         // Add all items in the selection to the library, so any featured
    1784         // images that are not initially loaded still appear.
    1785         library.observe( this.get('selection') );
    1786     },
    1787 
    1788     /**
    1789      * @since 3.5.0
    1790      */
    1791     activate: function() {
    1792         this.updateSelection();
    1793         this.frame.on( 'open', this.updateSelection, this );
    1794 
    1795         Library.prototype.activate.apply( this, arguments );
    1796     },
    1797 
    1798     /**
    1799      * @since 3.5.0
    1800      */
    1801     deactivate: function() {
    1802         this.frame.off( 'open', this.updateSelection, this );
    1803 
    1804         Library.prototype.deactivate.apply( this, arguments );
    1805     },
    1806 
    1807     /**
    1808      * @since 3.5.0
    1809      */
    1810     updateSelection: function() {
    1811         var selection = this.get('selection'),
    1812             id = wp.media.view.settings.post.featuredImageId,
    1813             attachment;
    1814 
    1815         if ( '' !== id && -1 !== id ) {
    1816             attachment = Attachment.get( id );
    1817             attachment.fetch();
    1818         }
    1819 
    1820         selection.reset( attachment ? [ attachment ] : [] );
    1821     }
    1822 });
    1823 
    1824 module.exports = FeaturedImage;
    1825 
    1826 
    1827 /***/ }),
    1828 /* 38 */
    1829 /***/ (function(module, exports) {
    1830 
     1542module.exports = Region;
     1543
     1544},{}],14:[function(require,module,exports){
    18311545/**
    18321546 * wp.media.controller.ReplaceImage
     
    19361650module.exports = ReplaceImage;
    19371651
    1938 
    1939 /***/ }),
    1940 /* 39 */
    1941 /***/ (function(module, exports) {
    1942 
    1943 /**
    1944  * wp.media.controller.EditImage
    1945  *
    1946  * A state for editing (cropping, etc.) an image.
    1947  *
    1948  * @class
    1949  * @augments wp.media.controller.State
    1950  * @augments Backbone.Model
    1951  *
    1952  * @param {object}                    attributes                      The attributes hash passed to the state.
    1953  * @param {wp.media.model.Attachment} attributes.model                The attachment.
    1954  * @param {string}                    [attributes.id=edit-image]      Unique identifier.
    1955  * @param {string}                    [attributes.title=Edit Image]   Title for the state. Displays in the media menu and the frame's title region.
    1956  * @param {string}                    [attributes.content=edit-image] Initial mode for the content region.
    1957  * @param {string}                    [attributes.toolbar=edit-image] Initial mode for the toolbar region.
    1958  * @param {string}                    [attributes.menu=false]         Initial mode for the menu region.
    1959  * @param {string}                    [attributes.url]                Unused. @todo Consider removal.
    1960  */
    1961 var l10n = wp.media.view.l10n,
    1962     EditImage;
    1963 
    1964 EditImage = wp.media.controller.State.extend({
    1965     defaults: {
    1966         id:      'edit-image',
    1967         title:   l10n.editImage,
    1968         menu:    false,
    1969         toolbar: 'edit-image',
    1970         content: 'edit-image',
    1971         url:     ''
    1972     },
    1973 
    1974     /**
    1975      * @since 3.9.0
    1976      */
    1977     activate: function() {
    1978         this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
    1979     },
    1980 
    1981     /**
    1982      * @since 3.9.0
    1983      */
    1984     deactivate: function() {
    1985         this.stopListening( this.frame );
    1986     },
    1987 
    1988     /**
    1989      * @since 3.9.0
    1990      */
    1991     toolbar: function() {
    1992         var frame = this.frame,
    1993             lastState = frame.lastState(),
    1994             previous = lastState && lastState.id;
    1995 
    1996         frame.toolbar.set( new wp.media.view.Toolbar({
    1997             controller: frame,
    1998             items: {
    1999                 back: {
    2000                     style: 'primary',
    2001                     text:     l10n.back,
    2002                     priority: 20,
    2003                     click:    function() {
    2004                         if ( previous ) {
    2005                             frame.setState( previous );
    2006                         } else {
    2007                             frame.close();
    2008                         }
    2009                     }
    2010                 }
    2011             }
    2012         }) );
    2013     }
    2014 });
    2015 
    2016 module.exports = EditImage;
    2017 
    2018 
    2019 /***/ }),
    2020 /* 40 */
    2021 /***/ (function(module, exports) {
    2022 
    2023 /**
    2024  * wp.media.controller.MediaLibrary
    2025  *
    2026  * @class
    2027  * @augments wp.media.controller.Library
    2028  * @augments wp.media.controller.State
    2029  * @augments Backbone.Model
    2030  */
    2031 var Library = wp.media.controller.Library,
    2032     MediaLibrary;
    2033 
    2034 MediaLibrary = Library.extend({
    2035     defaults: _.defaults({
    2036         // Attachments browser defaults. @see media.view.AttachmentsBrowser
    2037         filterable:      'uploaded',
    2038 
    2039         displaySettings: false,
    2040         priority:        80,
    2041         syncSelection:   false
    2042     }, Library.prototype.defaults ),
    2043 
    2044     /**
    2045      * @since 3.9.0
    2046      *
    2047      * @param options
    2048      */
    2049     initialize: function( options ) {
    2050         this.media = options.media;
    2051         this.type = options.type;
    2052         this.set( 'library', wp.media.query({ type: this.type }) );
    2053 
    2054         Library.prototype.initialize.apply( this, arguments );
    2055     },
    2056 
    2057     /**
    2058      * @since 3.9.0
    2059      */
    2060     activate: function() {
    2061         // @todo this should use this.frame.
    2062         if ( wp.media.frame.lastMime ) {
    2063             this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) );
    2064             delete wp.media.frame.lastMime;
    2065         }
    2066         Library.prototype.activate.apply( this, arguments );
    2067     }
    2068 });
    2069 
    2070 module.exports = MediaLibrary;
    2071 
    2072 
    2073 /***/ }),
    2074 /* 41 */
    2075 /***/ (function(module, exports) {
    2076 
    2077 /**
    2078  * wp.media.controller.Embed
    2079  *
    2080  * A state for embedding media from a URL.
    2081  *
    2082  * @class
    2083  * @augments wp.media.controller.State
    2084  * @augments Backbone.Model
    2085  *
    2086  * @param {object} attributes                         The attributes hash passed to the state.
    2087  * @param {string} [attributes.id=embed]              Unique identifier.
    2088  * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region.
    2089  * @param {string} [attributes.content=embed]         Initial mode for the content region.
    2090  * @param {string} [attributes.menu=default]          Initial mode for the menu region.
    2091  * @param {string} [attributes.toolbar=main-embed]    Initial mode for the toolbar region.
    2092  * @param {string} [attributes.menu=false]            Initial mode for the menu region.
    2093  * @param {int}    [attributes.priority=120]          The priority for the state link in the media menu.
    2094  * @param {string} [attributes.type=link]             The type of embed. Currently only link is supported.
    2095  * @param {string} [attributes.url]                   The embed URL.
    2096  * @param {object} [attributes.metadata={}]           Properties of the embed, which will override attributes.url if set.
    2097  */
    2098 var l10n = wp.media.view.l10n,
    2099     $ = Backbone.$,
    2100     Embed;
    2101 
    2102 Embed = wp.media.controller.State.extend({
    2103     defaults: {
    2104         id:       'embed',
    2105         title:    l10n.insertFromUrlTitle,
    2106         content:  'embed',
    2107         menu:     'default',
    2108         toolbar:  'main-embed',
    2109         priority: 120,
    2110         type:     'link',
    2111         url:      '',
    2112         metadata: {}
    2113     },
    2114 
    2115     // The amount of time used when debouncing the scan.
    2116     sensitivity: 400,
    2117 
    2118     initialize: function(options) {
    2119         this.metadata = options.metadata;
    2120         this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity );
    2121         this.props = new Backbone.Model( this.metadata || { url: '' });
    2122         this.props.on( 'change:url', this.debouncedScan, this );
    2123         this.props.on( 'change:url', this.refresh, this );
    2124         this.on( 'scan', this.scanImage, this );
    2125     },
    2126 
    2127     /**
    2128      * Trigger a scan of the embedded URL's content for metadata required to embed.
    2129      *
    2130      * @fires wp.media.controller.Embed#scan
    2131      */
    2132     scan: function() {
    2133         var scanners,
    2134             embed = this,
    2135             attributes = {
    2136                 type: 'link',
    2137                 scanners: []
    2138             };
    2139 
    2140         // Scan is triggered with the list of `attributes` to set on the
    2141         // state, useful for the 'type' attribute and 'scanners' attribute,
    2142         // an array of promise objects for asynchronous scan operations.
    2143         if ( this.props.get('url') ) {
    2144             this.trigger( 'scan', attributes );
    2145         }
    2146 
    2147         if ( attributes.scanners.length ) {
    2148             scanners = attributes.scanners = $.when.apply( $, attributes.scanners );
    2149             scanners.always( function() {
    2150                 if ( embed.get('scanners') === scanners ) {
    2151                     embed.set( 'loading', false );
    2152                 }
    2153             });
    2154         } else {
    2155             attributes.scanners = null;
    2156         }
    2157 
    2158         attributes.loading = !! attributes.scanners;
    2159         this.set( attributes );
    2160     },
    2161     /**
    2162      * Try scanning the embed as an image to discover its dimensions.
    2163      *
    2164      * @param {Object} attributes
    2165      */
    2166     scanImage: function( attributes ) {
    2167         var frame = this.frame,
    2168             state = this,
    2169             url = this.props.get('url'),
    2170             image = new Image(),
    2171             deferred = $.Deferred();
    2172 
    2173         attributes.scanners.push( deferred.promise() );
    2174 
    2175         // Try to load the image and find its width/height.
    2176         image.onload = function() {
    2177             deferred.resolve();
    2178 
    2179             if ( state !== frame.state() || url !== state.props.get('url') ) {
    2180                 return;
    2181             }
    2182 
    2183             state.set({
    2184                 type: 'image'
    2185             });
    2186 
    2187             state.props.set({
    2188                 width:  image.width,
    2189                 height: image.height
    2190             });
    2191         };
    2192 
    2193         image.onerror = deferred.reject;
    2194         image.src = url;
    2195     },
    2196 
    2197     refresh: function() {
    2198         this.frame.toolbar.get().refresh();
    2199     },
    2200 
    2201     reset: function() {
    2202         this.props.clear().set({ url: '' });
    2203 
    2204         if ( this.active ) {
    2205             this.refresh();
    2206         }
    2207     }
    2208 });
    2209 
    2210 module.exports = Embed;
    2211 
    2212 
    2213 /***/ }),
    2214 /* 42 */
    2215 /***/ (function(module, exports) {
    2216 
    2217 /**
    2218  * wp.media.controller.Cropper
    2219  *
    2220  * A state for cropping an image.
    2221  *
    2222  * @class
    2223  * @augments wp.media.controller.State
    2224  * @augments Backbone.Model
    2225  */
    2226 var l10n = wp.media.view.l10n,
    2227     Cropper;
    2228 
    2229 Cropper = wp.media.controller.State.extend({
    2230     defaults: {
    2231         id:          'cropper',
    2232         title:       l10n.cropImage,
    2233         // Region mode defaults.
    2234         toolbar:     'crop',
    2235         content:     'crop',
    2236         router:      false,
    2237 
    2238         canSkipCrop: false
    2239     },
    2240 
    2241     activate: function() {
    2242         this.frame.on( 'content:create:crop', this.createCropContent, this );
    2243         this.frame.on( 'close', this.removeCropper, this );
    2244         this.set('selection', new Backbone.Collection(this.frame._selection.single));
    2245     },
    2246 
    2247     deactivate: function() {
    2248         this.frame.toolbar.mode('browse');
    2249     },
    2250 
    2251     createCropContent: function() {
    2252         this.cropperView = new wp.media.view.Cropper({
    2253             controller: this,
    2254             attachment: this.get('selection').first()
    2255         });
    2256         this.cropperView.on('image-loaded', this.createCropToolbar, this);
    2257         this.frame.content.set(this.cropperView);
    2258 
    2259     },
    2260     removeCropper: function() {
    2261         this.imgSelect.cancelSelection();
    2262         this.imgSelect.setOptions({remove: true});
    2263         this.imgSelect.update();
    2264         this.cropperView.remove();
    2265     },
    2266     createCropToolbar: function() {
    2267         var canSkipCrop, toolbarOptions;
    2268 
    2269         canSkipCrop = this.get('canSkipCrop') || false;
    2270 
    2271         toolbarOptions = {
    2272             controller: this.frame,
    2273             items: {
    2274                 insert: {
    2275                     style:    'primary',
    2276                     text:     l10n.cropImage,
    2277                     priority: 80,
    2278                     requires: { library: false, selection: false },
    2279 
    2280                     click: function() {
    2281                         var controller = this.controller,
    2282                             selection;
    2283 
    2284                         selection = controller.state().get('selection').first();
    2285                         selection.set({cropDetails: controller.state().imgSelect.getSelection()});
    2286 
    2287                         this.$el.text(l10n.cropping);
    2288                         this.$el.attr('disabled', true);
    2289 
    2290                         controller.state().doCrop( selection ).done( function( croppedImage ) {
    2291                             controller.trigger('cropped', croppedImage );
    2292                             controller.close();
    2293                         }).fail( function() {
    2294                             controller.trigger('content:error:crop');
    2295                         });
    2296                     }
    2297                 }
    2298             }
    2299         };
    2300 
    2301         if ( canSkipCrop ) {
    2302             _.extend( toolbarOptions.items, {
    2303                 skip: {
    2304                     style:      'secondary',
    2305                     text:       l10n.skipCropping,
    2306                     priority:   70,
    2307                     requires:   { library: false, selection: false },
    2308                     click:      function() {
    2309                         var selection = this.controller.state().get('selection').first();
    2310                         this.controller.state().cropperView.remove();
    2311                         this.controller.trigger('skippedcrop', selection);
    2312                         this.controller.close();
    2313                     }
    2314                 }
    2315             });
    2316         }
    2317 
    2318         this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) );
    2319     },
    2320 
    2321     doCrop: function( attachment ) {
    2322         return wp.ajax.post( 'custom-header-crop', {
    2323             nonce: attachment.get('nonces').edit,
    2324             id: attachment.get('id'),
    2325             cropDetails: attachment.get('cropDetails')
    2326         } );
    2327     }
    2328 });
    2329 
    2330 module.exports = Cropper;
    2331 
    2332 
    2333 /***/ }),
    2334 /* 43 */
    2335 /***/ (function(module, exports) {
    2336 
    2337 /**
    2338  * wp.media.controller.CustomizeImageCropper
    2339  *
    2340  * A state for cropping an image.
    2341  *
    2342  * @class
    2343  * @augments wp.media.controller.Cropper
    2344  * @augments wp.media.controller.State
    2345  * @augments Backbone.Model
    2346  */
    2347 var Controller = wp.media.controller,
    2348     CustomizeImageCropper;
    2349 
    2350 CustomizeImageCropper = Controller.Cropper.extend({
    2351     doCrop: function( attachment ) {
    2352         var cropDetails = attachment.get( 'cropDetails' ),
    2353             control = this.get( 'control' );
    2354 
    2355         cropDetails.dst_width  = control.params.width;
    2356         cropDetails.dst_height = control.params.height;
    2357 
    2358         return wp.ajax.post( 'crop-image', {
    2359             wp_customize: 'on',
    2360             nonce: attachment.get( 'nonces' ).edit,
    2361             id: attachment.get( 'id' ),
    2362             context: control.id,
    2363             cropDetails: cropDetails
    2364         } );
    2365     }
    2366 });
    2367 
    2368 module.exports = CustomizeImageCropper;
    2369 
    2370 
    2371 /***/ }),
    2372 /* 44 */
    2373 /***/ (function(module, exports) {
    2374 
     1652},{}],15:[function(require,module,exports){
    23751653/**
    23761654 * wp.media.controller.SiteIconCropper
     
    24211699module.exports = SiteIconCropper;
    24221700
    2423 
    2424 /***/ }),
    2425 /* 45 */
    2426 /***/ (function(module, exports) {
    2427 
     1701},{}],16:[function(require,module,exports){
    24281702/**
    2429  * wp.media.View
    2430  *
    2431  * The base view class for media.
    2432  *
    2433  * Undelegating events, removing events from the model, and
    2434  * removing events from the controller mirror the code for
    2435  * `Backbone.View.dispose` in Backbone 0.9.8 development.
    2436  *
    2437  * This behavior has since been removed, and should not be used
    2438  * outside of the media manager.
     1703 * wp.media.controller.StateMachine
     1704 *
     1705 * A state machine keeps track of state. It is in one state at a time,
     1706 * and can change from one state to another.
     1707 *
     1708 * States are stored as models in a Backbone collection.
     1709 *
     1710 * @since 3.5.0
    24391711 *
    24401712 * @class
    2441  * @augments wp.Backbone.View
    2442  * @augments Backbone.View
     1713 * @augments Backbone.Model
     1714 * @mixin
     1715 * @mixes Backbone.Events
     1716 *
     1717 * @param {Array} states
    24431718 */
    2444 var View = wp.Backbone.View.extend({
    2445     constructor: function( options ) {
    2446         if ( options && options.controller ) {
    2447             this.controller = options.controller;
    2448         }
    2449         wp.Backbone.View.apply( this, arguments );
    2450     },
    2451     /**
    2452      * @todo The internal comment mentions this might have been a stop-gap
    2453      *       before Backbone 0.9.8 came out. Figure out if Backbone core takes
    2454      *       care of this in Backbone.View now.
     1719var StateMachine = function( states ) {
     1720    // @todo This is dead code. The states collection gets created in media.view.Frame._createStates.
     1721    this.states = new Backbone.Collection( states );
     1722};
     1723
     1724// Use Backbone's self-propagating `extend` inheritance method.
     1725StateMachine.extend = Backbone.Model.extend;
     1726
     1727_.extend( StateMachine.prototype, Backbone.Events, {
     1728    /**
     1729     * Fetch a state.
    24551730     *
    2456      * @returns {wp.media.View} Returns itself to allow chaining
    2457      */
    2458     dispose: function() {
    2459         // Undelegating events, removing events from the model, and
    2460         // removing events from the controller mirror the code for
    2461         // `Backbone.View.dispose` in Backbone 0.9.8 development.
    2462         this.undelegateEvents();
    2463 
    2464         if ( this.model && this.model.off ) {
    2465             this.model.off( null, null, this );
    2466         }
    2467 
    2468         if ( this.collection && this.collection.off ) {
    2469             this.collection.off( null, null, this );
    2470         }
    2471 
    2472         // Unbind controller events.
    2473         if ( this.controller && this.controller.off ) {
    2474             this.controller.off( null, null, this );
    2475         }
     1731     * If no `id` is provided, returns the active state.
     1732     *
     1733     * Implicitly creates states.
     1734     *
     1735     * Ensure that the `states` collection exists so the `StateMachine`
     1736     *   can be used as a mixin.
     1737     *
     1738     * @since 3.5.0
     1739     *
     1740     * @param {string} id
     1741     * @returns {wp.media.controller.State} Returns a State model
     1742     *   from the StateMachine collection
     1743     */
     1744    state: function( id ) {
     1745        this.states = this.states || new Backbone.Collection();
     1746
     1747        // Default to the active state.
     1748        id = id || this._state;
     1749
     1750        if ( id && ! this.states.get( id ) ) {
     1751            this.states.add({ id: id });
     1752        }
     1753        return this.states.get( id );
     1754    },
     1755
     1756    /**
     1757     * Sets the active state.
     1758     *
     1759     * Bail if we're trying to select the current state, if we haven't
     1760     * created the `states` collection, or are trying to select a state
     1761     * that does not exist.
     1762     *
     1763     * @since 3.5.0
     1764     *
     1765     * @param {string} id
     1766     *
     1767     * @fires wp.media.controller.State#deactivate
     1768     * @fires wp.media.controller.State#activate
     1769     *
     1770     * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining
     1771     */
     1772    setState: function( id ) {
     1773        var previous = this.state();
     1774
     1775        if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) {
     1776            return this;
     1777        }
     1778
     1779        if ( previous ) {
     1780            previous.trigger('deactivate');
     1781            this._lastState = previous.id;
     1782        }
     1783
     1784        this._state = id;
     1785        this.state().trigger('activate');
    24761786
    24771787        return this;
    24781788    },
    2479     /**
    2480      * @returns {wp.media.View} Returns itself to allow chaining
    2481      */
    2482     remove: function() {
    2483         this.dispose();
    2484         /**
    2485          * call 'remove' directly on the parent class
    2486          */
    2487         return wp.Backbone.View.prototype.remove.apply( this, arguments );
     1789
     1790    /**
     1791     * Returns the previous active state.
     1792     *
     1793     * Call the `state()` method with no parameters to retrieve the current
     1794     * active state.
     1795     *
     1796     * @since 3.5.0
     1797     *
     1798     * @returns {wp.media.controller.State} Returns a State model
     1799     *    from the StateMachine collection
     1800     */
     1801    lastState: function() {
     1802        if ( this._lastState ) {
     1803            return this.state( this._lastState );
     1804        }
    24881805    }
    24891806});
    24901807
    2491 module.exports = View;
    2492 
    2493 
    2494 /***/ }),
    2495 /* 46 */
    2496 /***/ (function(module, exports) {
    2497 
    2498 /**
    2499  * wp.media.view.Frame
    2500  *
    2501  * A frame is a composite view consisting of one or more regions and one or more
    2502  * states.
    2503  *
    2504  * @see wp.media.controller.State
    2505  * @see wp.media.controller.Region
    2506  *
    2507  * @class
    2508  * @augments wp.media.View
    2509  * @augments wp.Backbone.View
    2510  * @augments Backbone.View
    2511  * @mixes wp.media.controller.StateMachine
    2512  */
    2513 var Frame = wp.media.View.extend({
    2514     initialize: function() {
    2515         _.defaults( this.options, {
    2516             mode: [ 'select' ]
    2517         });
    2518         this._createRegions();
    2519         this._createStates();
    2520         this._createModes();
    2521     },
    2522 
    2523     _createRegions: function() {
    2524         // Clone the regions array.
    2525         this.regions = this.regions ? this.regions.slice() : [];
    2526 
    2527         // Initialize regions.
    2528         _.each( this.regions, function( region ) {
    2529             this[ region ] = new wp.media.controller.Region({
    2530                 view:     this,
    2531                 id:       region,
    2532                 selector: '.media-frame-' + region
    2533             });
    2534         }, this );
    2535     },
    2536     /**
    2537      * Create the frame's states.
    2538      *
    2539      * @see wp.media.controller.State
    2540      * @see wp.media.controller.StateMachine
    2541      *
    2542      * @fires wp.media.controller.State#ready
    2543      */
    2544     _createStates: function() {
    2545         // Create the default `states` collection.
    2546         this.states = new Backbone.Collection( null, {
    2547             model: wp.media.controller.State
    2548         });
    2549 
    2550         // Ensure states have a reference to the frame.
    2551         this.states.on( 'add', function( model ) {
    2552             model.frame = this;
    2553             model.trigger('ready');
    2554         }, this );
    2555 
    2556         if ( this.options.states ) {
    2557             this.states.add( this.options.states );
    2558         }
    2559     },
    2560 
    2561     /**
    2562      * A frame can be in a mode or multiple modes at one time.
    2563      *
    2564      * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode.
    2565      */
    2566     _createModes: function() {
    2567         // Store active "modes" that the frame is in. Unrelated to region modes.
    2568         this.activeModes = new Backbone.Collection();
    2569         this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) );
    2570 
    2571         _.each( this.options.mode, function( mode ) {
    2572             this.activateMode( mode );
    2573         }, this );
    2574     },
    2575     /**
    2576      * Reset all states on the frame to their defaults.
    2577      *
    2578      * @returns {wp.media.view.Frame} Returns itself to allow chaining
    2579      */
    2580     reset: function() {
    2581         this.states.invoke( 'trigger', 'reset' );
    2582         return this;
    2583     },
    2584     /**
    2585      * Map activeMode collection events to the frame.
    2586      */
    2587     triggerModeEvents: function( model, collection, options ) {
    2588         var collectionEvent,
    2589             modeEventMap = {
    2590                 add: 'activate',
    2591                 remove: 'deactivate'
    2592             },
    2593             eventToTrigger;
    2594         // Probably a better way to do this.
    2595         _.each( options, function( value, key ) {
    2596             if ( value ) {
    2597                 collectionEvent = key;
    2598             }
    2599         } );
    2600 
    2601         if ( ! _.has( modeEventMap, collectionEvent ) ) {
    2602             return;
    2603         }
    2604 
    2605         eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent];
    2606         this.trigger( eventToTrigger );
    2607     },
    2608     /**
    2609      * Activate a mode on the frame.
    2610      *
    2611      * @param string mode Mode ID.
    2612      * @returns {this} Returns itself to allow chaining.
    2613      */
    2614     activateMode: function( mode ) {
    2615         // Bail if the mode is already active.
    2616         if ( this.isModeActive( mode ) ) {
    2617             return;
    2618         }
    2619         this.activeModes.add( [ { id: mode } ] );
    2620         // Add a CSS class to the frame so elements can be styled for the mode.
    2621         this.$el.addClass( 'mode-' + mode );
    2622 
    2623         return this;
    2624     },
    2625     /**
    2626      * Deactivate a mode on the frame.
    2627      *
    2628      * @param string mode Mode ID.
    2629      * @returns {this} Returns itself to allow chaining.
    2630      */
    2631     deactivateMode: function( mode ) {
    2632         // Bail if the mode isn't active.
    2633         if ( ! this.isModeActive( mode ) ) {
    2634             return this;
    2635         }
    2636         this.activeModes.remove( this.activeModes.where( { id: mode } ) );
    2637         this.$el.removeClass( 'mode-' + mode );
    2638         /**
    2639          * Frame mode deactivation event.
    2640          *
    2641          * @event this#{mode}:deactivate
    2642          */
    2643         this.trigger( mode + ':deactivate' );
    2644 
    2645         return this;
    2646     },
    2647     /**
    2648      * Check if a mode is enabled on the frame.
    2649      *
    2650      * @param  string mode Mode ID.
    2651      * @return bool
    2652      */
    2653     isModeActive: function( mode ) {
    2654         return Boolean( this.activeModes.where( { id: mode } ).length );
    2655     }
    2656 });
    2657 
    2658 // Make the `Frame` a `StateMachine`.
    2659 _.extend( Frame.prototype, wp.media.controller.StateMachine.prototype );
    2660 
    2661 module.exports = Frame;
    2662 
    2663 
    2664 /***/ }),
    2665 /* 47 */
    2666 /***/ (function(module, exports) {
    2667 
    2668 /**
    2669  * wp.media.view.MediaFrame
    2670  *
    2671  * The frame used to create the media modal.
    2672  *
    2673  * @class
    2674  * @augments wp.media.view.Frame
    2675  * @augments wp.media.View
    2676  * @augments wp.Backbone.View
    2677  * @augments Backbone.View
    2678  * @mixes wp.media.controller.StateMachine
    2679  */
    2680 var Frame = wp.media.view.Frame,
    2681     $ = jQuery,
    2682     MediaFrame;
    2683 
    2684 MediaFrame = Frame.extend({
    2685     className: 'media-frame',
    2686     template:  wp.template('media-frame'),
    2687     regions:   ['menu','title','content','toolbar','router'],
    2688 
    2689     events: {
    2690         'click div.media-frame-title h1': 'toggleMenu'
    2691     },
    2692 
    2693     /**
    2694      * @global wp.Uploader
    2695      */
    2696     initialize: function() {
    2697         Frame.prototype.initialize.apply( this, arguments );
    2698 
    2699         _.defaults( this.options, {
    2700             title:    '',
    2701             modal:    true,
    2702             uploader: true
    2703         });
    2704 
    2705         // Ensure core UI is enabled.
    2706         this.$el.addClass('wp-core-ui');
    2707 
    2708         // Initialize modal container view.
    2709         if ( this.options.modal ) {
    2710             this.modal = new wp.media.view.Modal({
    2711                 controller: this,
    2712                 title:      this.options.title
    2713             });
    2714 
    2715             this.modal.content( this );
    2716         }
    2717 
    2718         // Force the uploader off if the upload limit has been exceeded or
    2719         // if the browser isn't supported.
    2720         if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
    2721             this.options.uploader = false;
    2722         }
    2723 
    2724         // Initialize window-wide uploader.
    2725         if ( this.options.uploader ) {
    2726             this.uploader = new wp.media.view.UploaderWindow({
    2727                 controller: this,
    2728                 uploader: {
    2729                     dropzone:  this.modal ? this.modal.$el : this.$el,
    2730                     container: this.$el
    2731                 }
    2732             });
    2733             this.views.set( '.media-frame-uploader', this.uploader );
    2734         }
    2735 
    2736         this.on( 'attach', _.bind( this.views.ready, this.views ), this );
    2737 
    2738         // Bind default title creation.
    2739         this.on( 'title:create:default', this.createTitle, this );
    2740         this.title.mode('default');
    2741 
    2742         this.on( 'title:render', function( view ) {
    2743             view.$el.append( '<span class="dashicons dashicons-arrow-down"></span>' );
    2744         });
    2745 
    2746         // Bind default menu.
    2747         this.on( 'menu:create:default', this.createMenu, this );
    2748     },
    2749     /**
    2750      * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
    2751      */
    2752     render: function() {
    2753         // Activate the default state if no active state exists.
    2754         if ( ! this.state() && this.options.state ) {
    2755             this.setState( this.options.state );
    2756         }
    2757         /**
    2758          * call 'render' directly on the parent class
    2759          */
    2760         return Frame.prototype.render.apply( this, arguments );
    2761     },
    2762     /**
    2763      * @param {Object} title
    2764      * @this wp.media.controller.Region
    2765      */
    2766     createTitle: function( title ) {
    2767         title.view = new wp.media.View({
    2768             controller: this,
    2769             tagName: 'h1'
    2770         });
    2771     },
    2772     /**
    2773      * @param {Object} menu
    2774      * @this wp.media.controller.Region
    2775      */
    2776     createMenu: function( menu ) {
    2777         menu.view = new wp.media.view.Menu({
    2778             controller: this
    2779         });
    2780     },
    2781 
    2782     toggleMenu: function() {
    2783         this.$el.find( '.media-menu' ).toggleClass( 'visible' );
    2784     },
    2785 
    2786     /**
    2787      * @param {Object} toolbar
    2788      * @this wp.media.controller.Region
    2789      */
    2790     createToolbar: function( toolbar ) {
    2791         toolbar.view = new wp.media.view.Toolbar({
    2792             controller: this
    2793         });
    2794     },
    2795     /**
    2796      * @param {Object} router
    2797      * @this wp.media.controller.Region
    2798      */
    2799     createRouter: function( router ) {
    2800         router.view = new wp.media.view.Router({
    2801             controller: this
    2802         });
    2803     },
    2804     /**
    2805      * @param {Object} options
    2806      */
    2807     createIframeStates: function( options ) {
    2808         var settings = wp.media.view.settings,
    2809             tabs = settings.tabs,
    2810             tabUrl = settings.tabUrl,
    2811             $postId;
    2812 
    2813         if ( ! tabs || ! tabUrl ) {
    2814             return;
    2815         }
    2816 
    2817         // Add the post ID to the tab URL if it exists.
    2818         $postId = $('#post_ID');
    2819         if ( $postId.length ) {
    2820             tabUrl += '&post_id=' + $postId.val();
    2821         }
    2822 
    2823         // Generate the tab states.
    2824         _.each( tabs, function( title, id ) {
    2825             this.state( 'iframe:' + id ).set( _.defaults({
    2826                 tab:     id,
    2827                 src:     tabUrl + '&tab=' + id,
    2828                 title:   title,
    2829                 content: 'iframe',
    2830                 menu:    'default'
    2831             }, options ) );
    2832         }, this );
    2833 
    2834         this.on( 'content:create:iframe', this.iframeContent, this );
    2835         this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
    2836         this.on( 'menu:render:default', this.iframeMenu, this );
    2837         this.on( 'open', this.hijackThickbox, this );
    2838         this.on( 'close', this.restoreThickbox, this );
    2839     },
    2840 
    2841     /**
    2842      * @param {Object} content
    2843      * @this wp.media.controller.Region
    2844      */
    2845     iframeContent: function( content ) {
    2846         this.$el.addClass('hide-toolbar');
    2847         content.view = new wp.media.view.Iframe({
    2848             controller: this
    2849         });
    2850     },
    2851 
    2852     iframeContentCleanup: function() {
    2853         this.$el.removeClass('hide-toolbar');
    2854     },
    2855 
    2856     iframeMenu: function( view ) {
    2857         var views = {};
    2858 
    2859         if ( ! view ) {
    2860             return;
    2861         }
    2862 
    2863         _.each( wp.media.view.settings.tabs, function( title, id ) {
    2864             views[ 'iframe:' + id ] = {
    2865                 text: this.state( 'iframe:' + id ).get('title'),
    2866                 priority: 200
    2867             };
    2868         }, this );
    2869 
    2870         view.set( views );
    2871     },
    2872 
    2873     hijackThickbox: function() {
    2874         var frame = this;
    2875 
    2876         if ( ! window.tb_remove || this._tb_remove ) {
    2877             return;
    2878         }
    2879 
    2880         this._tb_remove = window.tb_remove;
    2881         window.tb_remove = function() {
    2882             frame.close();
    2883             frame.reset();
    2884             frame.setState( frame.options.state );
    2885             frame._tb_remove.call( window );
    2886         };
    2887     },
    2888 
    2889     restoreThickbox: function() {
    2890         if ( ! this._tb_remove ) {
    2891             return;
    2892         }
    2893 
    2894         window.tb_remove = this._tb_remove;
    2895         delete this._tb_remove;
    2896     }
    2897 });
    2898 
    2899 // Map some of the modal's methods to the frame.
    2900 _.each(['open','close','attach','detach','escape'], function( method ) {
    2901     /**
    2902      * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
    2903      */
    2904     MediaFrame.prototype[ method ] = function() {
    2905         if ( this.modal ) {
    2906             this.modal[ method ].apply( this.modal, arguments );
    2907         }
     1808// Map all event binding and triggering on a StateMachine to its `states` collection.
     1809_.each([ 'on', 'off', 'trigger' ], function( method ) {
     1810    /**
     1811     * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining.
     1812     */
     1813    StateMachine.prototype[ method ] = function() {
     1814        // Ensure that the `states` collection exists so the `StateMachine`
     1815        // can be used as a mixin.
     1816        this.states = this.states || new Backbone.Collection();
     1817        // Forward the method to the `states` collection.
     1818        this.states[ method ].apply( this.states, arguments );
    29081819        return this;
    29091820    };
    29101821});
    29111822
    2912 module.exports = MediaFrame;
    2913 
    2914 
    2915 /***/ }),
    2916 /* 48 */
    2917 /***/ (function(module, exports) {
    2918 
     1823module.exports = StateMachine;
     1824
     1825},{}],17:[function(require,module,exports){
    29191826/**
    2920  * wp.media.view.MediaFrame.Select
    2921  *
    2922  * A frame for selecting an item or items from the media library.
     1827 * wp.media.controller.State
     1828 *
     1829 * A state is a step in a workflow that when set will trigger the controllers
     1830 * for the regions to be updated as specified in the frame.
     1831 *
     1832 * A state has an event-driven lifecycle:
     1833 *
     1834 *     'ready'      triggers when a state is added to a state machine's collection.
     1835 *     'activate'   triggers when a state is activated by a state machine.
     1836 *     'deactivate' triggers when a state is deactivated by a state machine.
     1837 *     'reset'      is not triggered automatically. It should be invoked by the
     1838 *                  proper controller to reset the state to its default.
    29231839 *
    29241840 * @class
    2925  * @augments wp.media.view.MediaFrame
    2926  * @augments wp.media.view.Frame
    2927  * @augments wp.media.View
    2928  * @augments wp.Backbone.View
    2929  * @augments Backbone.View
    2930  * @mixes wp.media.controller.StateMachine
     1841 * @augments Backbone.Model
    29311842 */
    2932 
    2933 var MediaFrame = wp.media.view.MediaFrame,
    2934     l10n = wp.media.view.l10n,
    2935     Select;
    2936 
    2937 Select = MediaFrame.extend({
    2938     initialize: function() {
    2939         // Call 'initialize' directly on the parent class.
    2940         MediaFrame.prototype.initialize.apply( this, arguments );
    2941 
    2942         _.defaults( this.options, {
    2943             selection: [],
    2944             library:   {},
    2945             multiple:  false,
    2946             state:    'library'
    2947         });
    2948 
    2949         this.createSelection();
    2950         this.createStates();
    2951         this.bindHandlers();
    2952     },
    2953 
    2954     /**
    2955      * Attach a selection collection to the frame.
     1843var State = Backbone.Model.extend({
     1844    /**
     1845     * Constructor.
    29561846     *
    2957      * A selection is a collection of attachments used for a specific purpose
    2958      * by a media frame. e.g. Selecting an attachment (or many) to insert into
    2959      * post content.
     1847     * @since 3.5.0
     1848     */
     1849    constructor: function() {
     1850        this.on( 'activate', this._preActivate, this );
     1851        this.on( 'activate', this.activate, this );
     1852        this.on( 'activate', this._postActivate, this );
     1853        this.on( 'deactivate', this._deactivate, this );
     1854        this.on( 'deactivate', this.deactivate, this );
     1855        this.on( 'reset', this.reset, this );
     1856        this.on( 'ready', this._ready, this );
     1857        this.on( 'ready', this.ready, this );
     1858        /**
     1859         * Call parent constructor with passed arguments
     1860         */
     1861        Backbone.Model.apply( this, arguments );
     1862        this.on( 'change:menu', this._updateMenu, this );
     1863    },
     1864    /**
     1865     * Ready event callback.
    29601866     *
    2961      * @see media.model.Selection
    2962      */
    2963     createSelection: function() {
    2964         var selection = this.options.selection;
    2965 
    2966         if ( ! (selection instanceof wp.media.model.Selection) ) {
    2967             this.options.selection = new wp.media.model.Selection( selection, {
    2968                 multiple: this.options.multiple
    2969             });
    2970         }
    2971 
    2972         this._selection = {
    2973             attachments: new wp.media.model.Attachments(),
    2974             difference: []
    2975         };
    2976     },
    2977 
    2978     /**
    2979      * Create the default states on the frame.
    2980      */
    2981     createStates: function() {
    2982         var options = this.options;
    2983 
    2984         if ( this.options.states ) {
     1867     * @abstract
     1868     * @since 3.5.0
     1869     */
     1870    ready: function() {},
     1871
     1872    /**
     1873     * Activate event callback.
     1874     *
     1875     * @abstract
     1876     * @since 3.5.0
     1877     */
     1878    activate: function() {},
     1879
     1880    /**
     1881     * Deactivate event callback.
     1882     *
     1883     * @abstract
     1884     * @since 3.5.0
     1885     */
     1886    deactivate: function() {},
     1887
     1888    /**
     1889     * Reset event callback.
     1890     *
     1891     * @abstract
     1892     * @since 3.5.0
     1893     */
     1894    reset: function() {},
     1895
     1896    /**
     1897     * @access private
     1898     * @since 3.5.0
     1899     */
     1900    _ready: function() {
     1901        this._updateMenu();
     1902    },
     1903
     1904    /**
     1905     * @access private
     1906     * @since 3.5.0
     1907    */
     1908    _preActivate: function() {
     1909        this.active = true;
     1910    },
     1911
     1912    /**
     1913     * @access private
     1914     * @since 3.5.0
     1915     */
     1916    _postActivate: function() {
     1917        this.on( 'change:menu', this._menu, this );
     1918        this.on( 'change:titleMode', this._title, this );
     1919        this.on( 'change:content', this._content, this );
     1920        this.on( 'change:toolbar', this._toolbar, this );
     1921
     1922        this.frame.on( 'title:render:default', this._renderTitle, this );
     1923
     1924        this._title();
     1925        this._menu();
     1926        this._toolbar();
     1927        this._content();
     1928        this._router();
     1929    },
     1930
     1931    /**
     1932     * @access private
     1933     * @since 3.5.0
     1934     */
     1935    _deactivate: function() {
     1936        this.active = false;
     1937
     1938        this.frame.off( 'title:render:default', this._renderTitle, this );
     1939
     1940        this.off( 'change:menu', this._menu, this );
     1941        this.off( 'change:titleMode', this._title, this );
     1942        this.off( 'change:content', this._content, this );
     1943        this.off( 'change:toolbar', this._toolbar, this );
     1944    },
     1945
     1946    /**
     1947     * @access private
     1948     * @since 3.5.0
     1949     */
     1950    _title: function() {
     1951        this.frame.title.render( this.get('titleMode') || 'default' );
     1952    },
     1953
     1954    /**
     1955     * @access private
     1956     * @since 3.5.0
     1957     */
     1958    _renderTitle: function( view ) {
     1959        view.$el.text( this.get('title') || '' );
     1960    },
     1961
     1962    /**
     1963     * @access private
     1964     * @since 3.5.0
     1965     */
     1966    _router: function() {
     1967        var router = this.frame.router,
     1968            mode = this.get('router'),
     1969            view;
     1970
     1971        this.frame.$el.toggleClass( 'hide-router', ! mode );
     1972        if ( ! mode ) {
    29851973            return;
    29861974        }
    29871975
    2988         // Add the default states.
    2989         this.states.add([
    2990             // Main states.
    2991             new wp.media.controller.Library({
    2992                 library:   wp.media.query( options.library ),
    2993                 multiple:  options.multiple,
    2994                 title:     options.title,
    2995                 priority:  20
    2996             })
    2997         ]);
    2998     },
    2999 
    3000     /**
    3001      * Bind region mode event callbacks.
     1976        this.frame.router.render( mode );
     1977
     1978        view = router.get();
     1979        if ( view && view.select ) {
     1980            view.select( this.frame.content.mode() );
     1981        }
     1982    },
     1983
     1984    /**
     1985     * @access private
     1986     * @since 3.5.0
     1987     */
     1988    _menu: function() {
     1989        var menu = this.frame.menu,
     1990            mode = this.get('menu'),
     1991            view;
     1992
     1993        this.frame.$el.toggleClass( 'hide-menu', ! mode );
     1994        if ( ! mode ) {
     1995            return;
     1996        }
     1997
     1998        menu.mode( mode );
     1999
     2000        view = menu.get();
     2001        if ( view && view.select ) {
     2002            view.select( this.id );
     2003        }
     2004    },
     2005
     2006    /**
     2007     * @access private
     2008     * @since 3.5.0
     2009     */
     2010    _updateMenu: function() {
     2011        var previous = this.previous('menu'),
     2012            menu = this.get('menu');
     2013
     2014        if ( previous ) {
     2015            this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
     2016        }
     2017
     2018        if ( menu ) {
     2019            this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
     2020        }
     2021    },
     2022
     2023    /**
     2024     * Create a view in the media menu for the state.
    30022025     *
    3003      * @see media.controller.Region.render
    3004      */
    3005     bindHandlers: function() {
    3006         this.on( 'router:create:browse', this.createRouter, this );
    3007         this.on( 'router:render:browse', this.browseRouter, this );
    3008         this.on( 'content:create:browse', this.browseContent, this );
    3009         this.on( 'content:render:upload', this.uploadContent, this );
    3010         this.on( 'toolbar:create:select', this.createSelectToolbar, this );
    3011     },
    3012 
    3013     /**
    3014      * Render callback for the router region in the `browse` mode.
     2026     * @access private
     2027     * @since 3.5.0
    30152028     *
    3016      * @param {wp.media.view.Router} routerView
    3017      */
    3018     browseRouter: function( routerView ) {
    3019         routerView.set({
    3020             upload: {
    3021                 text:     l10n.uploadFilesTitle,
    3022                 priority: 20
    3023             },
    3024             browse: {
    3025                 text:     l10n.mediaLibraryTitle,
    3026                 priority: 40
     2029     * @param {media.view.Menu} view The menu view.
     2030     */
     2031    _renderMenu: function( view ) {
     2032        var menuItem = this.get('menuItem'),
     2033            title = this.get('title'),
     2034            priority = this.get('priority');
     2035
     2036        if ( ! menuItem && title ) {
     2037            menuItem = { text: title };
     2038
     2039            if ( priority ) {
     2040                menuItem.priority = priority;
    30272041            }
    3028         });
    3029     },
    3030 
    3031     /**
    3032      * Render callback for the content region in the `browse` mode.
    3033      *
    3034      * @param {wp.media.controller.Region} contentRegion
    3035      */
    3036     browseContent: function( contentRegion ) {
    3037         var state = this.state();
    3038 
    3039         this.$el.removeClass('hide-toolbar');
    3040 
    3041         // Browse our library of attachments.
    3042         contentRegion.view = new wp.media.view.AttachmentsBrowser({
    3043             controller: this,
    3044             collection: state.get('library'),
    3045             selection:  state.get('selection'),
    3046             model:      state,
    3047             sortable:   state.get('sortable'),
    3048             search:     state.get('searchable'),
    3049             filters:    state.get('filterable'),
    3050             date:       state.get('date'),
    3051             display:    state.has('display') ? state.get('display') : state.get('displaySettings'),
    3052             dragInfo:   state.get('dragInfo'),
    3053 
    3054             idealColumnWidth: state.get('idealColumnWidth'),
    3055             suggestedWidth:   state.get('suggestedWidth'),
    3056             suggestedHeight:  state.get('suggestedHeight'),
    3057 
    3058             AttachmentView: state.get('AttachmentView')
    3059         });
    3060     },
    3061 
    3062     /**
    3063      * Render callback for the content region in the `upload` mode.
    3064      */
    3065     uploadContent: function() {
    3066         this.$el.removeClass( 'hide-toolbar' );
    3067         this.content.set( new wp.media.view.UploaderInline({
    3068             controller: this
    3069         }) );
    3070     },
    3071 
    3072     /**
    3073      * Toolbars
    3074      *
    3075      * @param {Object} toolbar
    3076      * @param {Object} [options={}]
    3077      * @this wp.media.controller.Region
    3078      */
    3079     createSelectToolbar: function( toolbar, options ) {
    3080         options = options || this.options.button || {};
    3081         options.controller = this;
    3082 
    3083         toolbar.view = new wp.media.view.Toolbar.Select( options );
     2042        }
     2043
     2044        if ( ! menuItem ) {
     2045            return;
     2046        }
     2047
     2048        view.set( this.id, menuItem );
    30842049    }
    30852050});
    30862051
    3087 module.exports = Select;
    3088 
    3089 
    3090 /***/ }),
    3091 /* 49 */
    3092 /***/ (function(module, exports) {
    3093 
     2052_.each(['toolbar','content'], function( region ) {
     2053    /**
     2054     * @access private
     2055     */
     2056    State.prototype[ '_' + region ] = function() {
     2057        var mode = this.get( region );
     2058        if ( mode ) {
     2059            this.frame[ region ].render( mode );
     2060        }
     2061    };
     2062});
     2063
     2064module.exports = State;
     2065
     2066},{}],18:[function(require,module,exports){
    30942067/**
    3095  * wp.media.view.MediaFrame.Post
    3096  *
    3097  * The frame for manipulating media on the Edit Post page.
    3098  *
    3099  * @class
    3100  * @augments wp.media.view.MediaFrame.Select
    3101  * @augments wp.media.view.MediaFrame
    3102  * @augments wp.media.view.Frame
    3103  * @augments wp.media.View
    3104  * @augments wp.Backbone.View
    3105  * @augments Backbone.View
    3106  * @mixes wp.media.controller.StateMachine
     2068 * wp.media.selectionSync
     2069 *
     2070 * Sync an attachments selection in a state with another state.
     2071 *
     2072 * Allows for selecting multiple images in the Insert Media workflow, and then
     2073 * switching to the Insert Gallery workflow while preserving the attachments selection.
     2074 *
     2075 * @mixin
    31072076 */
    3108 var Select = wp.media.view.MediaFrame.Select,
    3109     Library = wp.media.controller.Library,
    3110     l10n = wp.media.view.l10n,
    3111     Post;
    3112 
    3113 Post = Select.extend({
    3114     initialize: function() {
    3115         this.counts = {
    3116             audio: {
    3117                 count: wp.media.view.settings.attachmentCounts.audio,
    3118                 state: 'playlist'
    3119             },
    3120             video: {
    3121                 count: wp.media.view.settings.attachmentCounts.video,
    3122                 state: 'video-playlist'
    3123             }
    3124         };
    3125 
    3126         _.defaults( this.options, {
    3127             multiple:  true,
    3128             editing:   false,
    3129             state:    'insert',
    3130             metadata:  {}
    3131         });
    3132 
    3133         // Call 'initialize' directly on the parent class.
    3134         Select.prototype.initialize.apply( this, arguments );
    3135         this.createIframeStates();
    3136 
    3137     },
    3138 
    3139     /**
    3140      * Create the default states.
    3141      */
    3142     createStates: function() {
    3143         var options = this.options;
    3144 
    3145         this.states.add([
    3146             // Main states.
    3147             new Library({
    3148                 id:         'insert',
    3149                 title:      l10n.insertMediaTitle,
    3150                 priority:   20,
    3151                 toolbar:    'main-insert',
    3152                 filterable: 'all',
    3153                 library:    wp.media.query( options.library ),
    3154                 multiple:   options.multiple ? 'reset' : false,
    3155                 editable:   true,
    3156 
    3157                 // If the user isn't allowed to edit fields,
    3158                 // can they still edit it locally?
    3159                 allowLocalEdits: true,
    3160 
    3161                 // Show the attachment display settings.
    3162                 displaySettings: true,
    3163                 // Update user settings when users adjust the
    3164                 // attachment display settings.
    3165                 displayUserSettings: true
    3166             }),
    3167 
    3168             new Library({
    3169                 id:         'gallery',
    3170                 title:      l10n.createGalleryTitle,
    3171                 priority:   40,
    3172                 toolbar:    'main-gallery',
    3173                 filterable: 'uploaded',
    3174                 multiple:   'add',
    3175                 editable:   false,
    3176 
    3177                 library:  wp.media.query( _.defaults({
    3178                     type: 'image'
    3179                 }, options.library ) )
    3180             }),
    3181 
    3182             // Embed states.
    3183             new wp.media.controller.Embed( { metadata: options.metadata } ),
    3184 
    3185             new wp.media.controller.EditImage( { model: options.editImage } ),
    3186 
    3187             // Gallery states.
    3188             new wp.media.controller.GalleryEdit({
    3189                 library: options.selection,
    3190                 editing: options.editing,
    3191                 menu:    'gallery'
    3192             }),
    3193 
    3194             new wp.media.controller.GalleryAdd(),
    3195 
    3196             new Library({
    3197                 id:         'playlist',
    3198                 title:      l10n.createPlaylistTitle,
    3199                 priority:   60,
    3200                 toolbar:    'main-playlist',
    3201                 filterable: 'uploaded',
    3202                 multiple:   'add',
    3203                 editable:   false,
    3204 
    3205                 library:  wp.media.query( _.defaults({
    3206                     type: 'audio'
    3207                 }, options.library ) )
    3208             }),
    3209 
    3210             // Playlist states.
    3211             new wp.media.controller.CollectionEdit({
    3212                 type: 'audio',
    3213                 collectionType: 'playlist',
    3214                 title:          l10n.editPlaylistTitle,
    3215                 SettingsView:   wp.media.view.Settings.Playlist,
    3216                 library:        options.selection,
    3217                 editing:        options.editing,
    3218                 menu:           'playlist',
    3219                 dragInfoText:   l10n.playlistDragInfo,
    3220                 dragInfo:       false
    3221             }),
    3222 
    3223             new wp.media.controller.CollectionAdd({
    3224                 type: 'audio',
    3225                 collectionType: 'playlist',
    3226                 title: l10n.addToPlaylistTitle
    3227             }),
    3228 
    3229             new Library({
    3230                 id:         'video-playlist',
    3231                 title:      l10n.createVideoPlaylistTitle,
    3232                 priority:   60,
    3233                 toolbar:    'main-video-playlist',
    3234                 filterable: 'uploaded',
    3235                 multiple:   'add',
    3236                 editable:   false,
    3237 
    3238                 library:  wp.media.query( _.defaults({
    3239                     type: 'video'
    3240                 }, options.library ) )
    3241             }),
    3242 
    3243             new wp.media.controller.CollectionEdit({
    3244                 type: 'video',
    3245                 collectionType: 'playlist',
    3246                 title:          l10n.editVideoPlaylistTitle,
    3247                 SettingsView:   wp.media.view.Settings.Playlist,
    3248                 library:        options.selection,
    3249                 editing:        options.editing,
    3250                 menu:           'video-playlist',
    3251                 dragInfoText:   l10n.videoPlaylistDragInfo,
    3252                 dragInfo:       false
    3253             }),
    3254 
    3255             new wp.media.controller.CollectionAdd({
    3256                 type: 'video',
    3257                 collectionType: 'playlist',
    3258                 title: l10n.addToVideoPlaylistTitle
    3259             })
    3260         ]);
    3261 
    3262         if ( wp.media.view.settings.post.featuredImageId ) {
    3263             this.states.add( new wp.media.controller.FeaturedImage() );
    3264         }
    3265     },
    3266 
    3267     bindHandlers: function() {
    3268         var handlers, checkCounts;
    3269 
    3270         Select.prototype.bindHandlers.apply( this, arguments );
    3271 
    3272         this.on( 'activate', this.activate, this );
    3273 
    3274         // Only bother checking media type counts if one of the counts is zero
    3275         checkCounts = _.find( this.counts, function( type ) {
    3276             return type.count === 0;
    3277         } );
    3278 
    3279         if ( typeof checkCounts !== 'undefined' ) {
    3280             this.listenTo( wp.media.model.Attachments.all, 'change:type', this.mediaTypeCounts );
    3281         }
    3282 
    3283         this.on( 'menu:create:gallery', this.createMenu, this );
    3284         this.on( 'menu:create:playlist', this.createMenu, this );
    3285         this.on( 'menu:create:video-playlist', this.createMenu, this );
    3286         this.on( 'toolbar:create:main-insert', this.createToolbar, this );
    3287         this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
    3288         this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
    3289         this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
    3290         this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
    3291         this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
    3292 
    3293         handlers = {
    3294             menu: {
    3295                 'default': 'mainMenu',
    3296                 'gallery': 'galleryMenu',
    3297                 'playlist': 'playlistMenu',
    3298                 'video-playlist': 'videoPlaylistMenu'
    3299             },
    3300 
    3301             content: {
    3302                 'embed':          'embedContent',
    3303                 'edit-image':     'editImageContent',
    3304                 'edit-selection': 'editSelectionContent'
    3305             },
    3306 
    3307             toolbar: {
    3308                 'main-insert':      'mainInsertToolbar',
    3309                 'main-gallery':     'mainGalleryToolbar',
    3310                 'gallery-edit':     'galleryEditToolbar',
    3311                 'gallery-add':      'galleryAddToolbar',
    3312                 'main-playlist':    'mainPlaylistToolbar',
    3313                 'playlist-edit':    'playlistEditToolbar',
    3314                 'playlist-add':     'playlistAddToolbar',
    3315                 'main-video-playlist': 'mainVideoPlaylistToolbar',
    3316                 'video-playlist-edit': 'videoPlaylistEditToolbar',
    3317                 'video-playlist-add': 'videoPlaylistAddToolbar'
    3318             }
    3319         };
    3320 
    3321         _.each( handlers, function( regionHandlers, region ) {
    3322             _.each( regionHandlers, function( callback, handler ) {
    3323                 this.on( region + ':render:' + handler, this[ callback ], this );
    3324             }, this );
    3325         }, this );
    3326     },
    3327 
    3328     activate: function() {
    3329         // Hide menu items for states tied to particular media types if there are no items
    3330         _.each( this.counts, function( type ) {
    3331             if ( type.count < 1 ) {
    3332                 this.menuItemVisibility( type.state, 'hide' );
    3333             }
    3334         }, this );
    3335     },
    3336 
    3337     mediaTypeCounts: function( model, attr ) {
    3338         if ( typeof this.counts[ attr ] !== 'undefined' && this.counts[ attr ].count < 1 ) {
    3339             this.counts[ attr ].count++;
    3340             this.menuItemVisibility( this.counts[ attr ].state, 'show' );
    3341         }
    3342     },
    3343 
    3344     // Menus
    3345     /**
    3346      * @param {wp.Backbone.View} view
    3347      */
    3348     mainMenu: function( view ) {
    3349         view.set({
    3350             'library-separator': new wp.media.View({
    3351                 className: 'separator',
    3352                 priority: 100
    3353             })
    3354         });
    3355     },
    3356 
    3357     menuItemVisibility: function( state, visibility ) {
    3358         var menu = this.menu.get();
    3359         if ( visibility === 'hide' ) {
    3360             menu.hide( state );
    3361         } else if ( visibility === 'show' ) {
    3362             menu.show( state );
    3363         }
    3364     },
    3365     /**
    3366      * @param {wp.Backbone.View} view
    3367      */
    3368     galleryMenu: function( view ) {
    3369         var lastState = this.lastState(),
    3370             previous = lastState && lastState.id,
    3371             frame = this;
    3372 
    3373         view.set({
    3374             cancel: {
    3375                 text:     l10n.cancelGalleryTitle,
    3376                 priority: 20,
    3377                 click:    function() {
    3378                     if ( previous ) {
    3379                         frame.setState( previous );
    3380                     } else {
    3381                         frame.close();
    3382                     }
    3383 
    3384                     // Keep focus inside media modal
    3385                     // after canceling a gallery
    3386                     this.controller.modal.focusManager.focus();
    3387                 }
    3388             },
    3389             separateCancel: new wp.media.View({
    3390                 className: 'separator',
    3391                 priority: 40
    3392             })
    3393         });
    3394     },
    3395 
    3396     playlistMenu: function( view ) {
    3397         var lastState = this.lastState(),
    3398             previous = lastState && lastState.id,
    3399             frame = this;
    3400 
    3401         view.set({
    3402             cancel: {
    3403                 text:     l10n.cancelPlaylistTitle,
    3404                 priority: 20,
    3405                 click:    function() {
    3406                     if ( previous ) {
    3407                         frame.setState( previous );
    3408                     } else {
    3409                         frame.close();
    3410                     }
    3411                 }
    3412             },
    3413             separateCancel: new wp.media.View({
    3414                 className: 'separator',
    3415                 priority: 40
    3416             })
    3417         });
    3418     },
    3419 
    3420     videoPlaylistMenu: function( view ) {
    3421         var lastState = this.lastState(),
    3422             previous = lastState && lastState.id,
    3423             frame = this;
    3424 
    3425         view.set({
    3426             cancel: {
    3427                 text:     l10n.cancelVideoPlaylistTitle,
    3428                 priority: 20,
    3429                 click:    function() {
    3430                     if ( previous ) {
    3431                         frame.setState( previous );
    3432                     } else {
    3433                         frame.close();
    3434                     }
    3435                 }
    3436             },
    3437             separateCancel: new wp.media.View({
    3438                 className: 'separator',
    3439                 priority: 40
    3440             })
    3441         });
    3442     },
    3443 
    3444     // Content
    3445     embedContent: function() {
    3446         var view = new wp.media.view.Embed({
    3447             controller: this,
    3448             model:      this.state()
    3449         }).render();
    3450 
    3451         this.content.set( view );
    3452 
    3453         if ( ! wp.media.isTouchDevice ) {
    3454             view.url.focus();
    3455         }
    3456     },
    3457 
    3458     editSelectionContent: function() {
    3459         var state = this.state(),
    3460             selection = state.get('selection'),
    3461             view;
    3462 
    3463         view = new wp.media.view.AttachmentsBrowser({
    3464             controller: this,
    3465             collection: selection,
    3466             selection:  selection,
    3467             model:      state,
    3468             sortable:   true,
    3469             search:     false,
    3470             date:       false,
    3471             dragInfo:   true,
    3472 
    3473             AttachmentView: wp.media.view.Attachments.EditSelection
    3474         }).render();
    3475 
    3476         view.toolbar.set( 'backToLibrary', {
    3477             text:     l10n.returnToLibrary,
    3478             priority: -100,
    3479 
    3480             click: function() {
    3481                 this.controller.content.mode('browse');
    3482             }
    3483         });
    3484 
    3485         // Browse our library of attachments.
    3486         this.content.set( view );
    3487 
    3488         // Trigger the controller to set focus
    3489         this.trigger( 'edit:selection', this );
    3490     },
    3491 
    3492     editImageContent: function() {
    3493         var image = this.state().get('image'),
    3494             view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
    3495 
    3496         this.content.set( view );
    3497 
    3498         // after creating the wrapper view, load the actual editor via an ajax call
    3499         view.loadEditor();
    3500 
    3501     },
    3502 
    3503     // Toolbars
    3504 
    3505     /**
    3506      * @param {wp.Backbone.View} view
    3507      */
    3508     selectionStatusToolbar: function( view ) {
    3509         var editable = this.state().get('editable');
    3510 
    3511         view.set( 'selection', new wp.media.view.Selection({
    3512             controller: this,
    3513             collection: this.state().get('selection'),
    3514             priority:   -40,
    3515 
    3516             // If the selection is editable, pass the callback to
    3517             // switch the content mode.
    3518             editable: editable && function() {
    3519                 this.controller.content.mode('edit-selection');
    3520             }
    3521         }).render() );
    3522     },
    3523 
    3524     /**
    3525      * @param {wp.Backbone.View} view
    3526      */
    3527     mainInsertToolbar: function( view ) {
    3528         var controller = this;
    3529 
    3530         this.selectionStatusToolbar( view );
    3531 
    3532         view.set( 'insert', {
    3533             style:    'primary',
    3534             priority: 80,
    3535             text:     l10n.insertIntoPost,
    3536             requires: { selection: true },
    3537 
    3538             /**
    3539              * @fires wp.media.controller.State#insert
    3540              */
    3541             click: function() {
    3542                 var state = controller.state(),
    3543                     selection = state.get('selection');
    3544 
    3545                 controller.close();
    3546                 state.trigger( 'insert', selection ).reset();
    3547             }
    3548         });
    3549     },
    3550 
    3551     /**
    3552      * @param {wp.Backbone.View} view
    3553      */
    3554     mainGalleryToolbar: function( view ) {
    3555         var controller = this;
    3556 
    3557         this.selectionStatusToolbar( view );
    3558 
    3559         view.set( 'gallery', {
    3560             style:    'primary',
    3561             text:     l10n.createNewGallery,
    3562             priority: 60,
    3563             requires: { selection: true },
    3564 
    3565             click: function() {
    3566                 var selection = controller.state().get('selection'),
    3567                     edit = controller.state('gallery-edit'),
    3568                     models = selection.where({ type: 'image' });
    3569 
    3570                 edit.set( 'library', new wp.media.model.Selection( models, {
    3571                     props:    selection.props.toJSON(),
    3572                     multiple: true
    3573                 }) );
    3574 
    3575                 this.controller.setState('gallery-edit');
    3576 
    3577                 // Keep focus inside media modal
    3578                 // after jumping to gallery view
    3579                 this.controller.modal.focusManager.focus();
    3580             }
    3581         });
    3582     },
    3583 
    3584     mainPlaylistToolbar: function( view ) {
    3585         var controller = this;
    3586 
    3587         this.selectionStatusToolbar( view );
    3588 
    3589         view.set( 'playlist', {
    3590             style:    'primary',
    3591             text:     l10n.createNewPlaylist,
    3592             priority: 100,
    3593             requires: { selection: true },
    3594 
    3595             click: function() {
    3596                 var selection = controller.state().get('selection'),
    3597                     edit = controller.state('playlist-edit'),
    3598                     models = selection.where({ type: 'audio' });
    3599 
    3600                 edit.set( 'library', new wp.media.model.Selection( models, {
    3601                     props:    selection.props.toJSON(),
    3602                     multiple: true
    3603                 }) );
    3604 
    3605                 this.controller.setState('playlist-edit');
    3606 
    3607                 // Keep focus inside media modal
    3608                 // after jumping to playlist view
    3609                 this.controller.modal.focusManager.focus();
    3610             }
    3611         });
    3612     },
    3613 
    3614     mainVideoPlaylistToolbar: function( view ) {
    3615         var controller = this;
    3616 
    3617         this.selectionStatusToolbar( view );
    3618 
    3619         view.set( 'video-playlist', {
    3620             style:    'primary',
    3621             text:     l10n.createNewVideoPlaylist,
    3622             priority: 100,
    3623             requires: { selection: true },
    3624 
    3625             click: function() {
    3626                 var selection = controller.state().get('selection'),
    3627                     edit = controller.state('video-playlist-edit'),
    3628                     models = selection.where({ type: 'video' });
    3629 
    3630                 edit.set( 'library', new wp.media.model.Selection( models, {
    3631                     props:    selection.props.toJSON(),
    3632                     multiple: true
    3633                 }) );
    3634 
    3635                 this.controller.setState('video-playlist-edit');
    3636 
    3637                 // Keep focus inside media modal
    3638                 // after jumping to video playlist view
    3639                 this.controller.modal.focusManager.focus();
    3640             }
    3641         });
    3642     },
    3643 
    3644     featuredImageToolbar: function( toolbar ) {
    3645         this.createSelectToolbar( toolbar, {
    3646             text:  l10n.setFeaturedImage,
    3647             state: this.options.state
    3648         });
    3649     },
    3650 
    3651     mainEmbedToolbar: function( toolbar ) {
    3652         toolbar.view = new wp.media.view.Toolbar.Embed({
    3653             controller: this
    3654         });
    3655     },
    3656 
    3657     galleryEditToolbar: function() {
    3658         var editing = this.state().get('editing');
    3659         this.toolbar.set( new wp.media.view.Toolbar({
    3660             controller: this,
    3661             items: {
    3662                 insert: {
    3663                     style:    'primary',
    3664                     text:     editing ? l10n.updateGallery : l10n.insertGallery,
    3665                     priority: 80,
    3666                     requires: { library: true },
    3667 
    3668                     /**
    3669                      * @fires wp.media.controller.State#update
    3670                      */
    3671                     click: function() {
    3672                         var controller = this.controller,
    3673                             state = controller.state();
    3674 
    3675                         controller.close();
    3676                         state.trigger( 'update', state.get('library') );
    3677 
    3678                         // Restore and reset the default state.
    3679                         controller.setState( controller.options.state );
    3680                         controller.reset();
    3681                     }
    3682                 }
    3683             }
    3684         }) );
    3685     },
    3686 
    3687     galleryAddToolbar: function() {
    3688         this.toolbar.set( new wp.media.view.Toolbar({
    3689             controller: this,
    3690             items: {
    3691                 insert: {
    3692                     style:    'primary',
    3693                     text:     l10n.addToGallery,
    3694                     priority: 80,
    3695                     requires: { selection: true },
    3696 
    3697                     /**
    3698                      * @fires wp.media.controller.State#reset
    3699                      */
    3700                     click: function() {
    3701                         var controller = this.controller,
    3702                             state = controller.state(),
    3703                             edit = controller.state('gallery-edit');
    3704 
    3705                         edit.get('library').add( state.get('selection').models );
    3706                         state.trigger('reset');
    3707                         controller.setState('gallery-edit');
    3708                     }
    3709                 }
    3710             }
    3711         }) );
    3712     },
    3713 
    3714     playlistEditToolbar: function() {
    3715         var editing = this.state().get('editing');
    3716         this.toolbar.set( new wp.media.view.Toolbar({
    3717             controller: this,
    3718             items: {
    3719                 insert: {
    3720                     style:    'primary',
    3721                     text:     editing ? l10n.updatePlaylist : l10n.insertPlaylist,
    3722                     priority: 80,
    3723                     requires: { library: true },
    3724 
    3725                     /**
    3726                      * @fires wp.media.controller.State#update
    3727                      */
    3728                     click: function() {
    3729                         var controller = this.controller,
    3730                             state = controller.state();
    3731 
    3732                         controller.close();
    3733                         state.trigger( 'update', state.get('library') );
    3734 
    3735                         // Restore and reset the default state.
    3736                         controller.setState( controller.options.state );
    3737                         controller.reset();
    3738                     }
    3739                 }
    3740             }
    3741         }) );
    3742     },
    3743 
    3744     playlistAddToolbar: function() {
    3745         this.toolbar.set( new wp.media.view.Toolbar({
    3746             controller: this,
    3747             items: {
    3748                 insert: {
    3749                     style:    'primary',
    3750                     text:     l10n.addToPlaylist,
    3751                     priority: 80,
    3752                     requires: { selection: true },
    3753 
    3754                     /**
    3755                      * @fires wp.media.controller.State#reset
    3756                      */
    3757                     click: function() {
    3758                         var controller = this.controller,
    3759                             state = controller.state(),
    3760                             edit = controller.state('playlist-edit');
    3761 
    3762                         edit.get('library').add( state.get('selection').models );
    3763                         state.trigger('reset');
    3764                         controller.setState('playlist-edit');
    3765                     }
    3766                 }
    3767             }
    3768         }) );
    3769     },
    3770 
    3771     videoPlaylistEditToolbar: function() {
    3772         var editing = this.state().get('editing');
    3773         this.toolbar.set( new wp.media.view.Toolbar({
    3774             controller: this,
    3775             items: {
    3776                 insert: {
    3777                     style:    'primary',
    3778                     text:     editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
    3779                     priority: 140,
    3780                     requires: { library: true },
    3781 
    3782                     click: function() {
    3783                         var controller = this.controller,
    3784                             state = controller.state(),
    3785                             library = state.get('library');
    3786 
    3787                         library.type = 'video';
    3788 
    3789                         controller.close();
    3790                         state.trigger( 'update', library );
    3791 
    3792                         // Restore and reset the default state.
    3793                         controller.setState( controller.options.state );
    3794                         controller.reset();
    3795                     }
    3796                 }
    3797             }
    3798         }) );
    3799     },
    3800 
    3801     videoPlaylistAddToolbar: function() {
    3802         this.toolbar.set( new wp.media.view.Toolbar({
    3803             controller: this,
    3804             items: {
    3805                 insert: {
    3806                     style:    'primary',
    3807                     text:     l10n.addToVideoPlaylist,
    3808                     priority: 140,
    3809                     requires: { selection: true },
    3810 
    3811                     click: function() {
    3812                         var controller = this.controller,
    3813                             state = controller.state(),
    3814                             edit = controller.state('video-playlist-edit');
    3815 
    3816                         edit.get('library').add( state.get('selection').models );
    3817                         state.trigger('reset');
    3818                         controller.setState('video-playlist-edit');
    3819                     }
    3820                 }
    3821             }
    3822         }) );
     2077var selectionSync = {
     2078    /**
     2079     * @since 3.5.0
     2080     */
     2081    syncSelection: function() {
     2082        var selection = this.get('selection'),
     2083            manager = this.frame._selection;
     2084
     2085        if ( ! this.get('syncSelection') || ! manager || ! selection ) {
     2086            return;
     2087        }
     2088
     2089        // If the selection supports multiple items, validate the stored
     2090        // attachments based on the new selection's conditions. Record
     2091        // the attachments that are not included; we'll maintain a
     2092        // reference to those. Other attachments are considered in flux.
     2093        if ( selection.multiple ) {
     2094            selection.reset( [], { silent: true });
     2095            selection.validateAll( manager.attachments );
     2096            manager.difference = _.difference( manager.attachments.models, selection.models );
     2097        }
     2098
     2099        // Sync the selection's single item with the master.
     2100        selection.single( manager.single );
     2101    },
     2102
     2103    /**
     2104     * Record the currently active attachments, which is a combination
     2105     * of the selection's attachments and the set of selected
     2106     * attachments that this specific selection considered invalid.
     2107     * Reset the difference and record the single attachment.
     2108     *
     2109     * @since 3.5.0
     2110     */
     2111    recordSelection: function() {
     2112        var selection = this.get('selection'),
     2113            manager = this.frame._selection;
     2114
     2115        if ( ! this.get('syncSelection') || ! manager || ! selection ) {
     2116            return;
     2117        }
     2118
     2119        if ( selection.multiple ) {
     2120            manager.attachments.reset( selection.toArray().concat( manager.difference ) );
     2121            manager.difference = [];
     2122        } else {
     2123            manager.attachments.add( selection.toArray() );
     2124        }
     2125
     2126        manager.single = selection._single;
    38232127    }
    3824 });
    3825 
    3826 module.exports = Post;
    3827 
    3828 
    3829 /***/ }),
    3830 /* 50 */
    3831 /***/ (function(module, exports) {
     2128};
     2129
     2130module.exports = selectionSync;
     2131
     2132},{}],19:[function(require,module,exports){
     2133var media = wp.media,
     2134    $ = jQuery,
     2135    l10n;
     2136
     2137media.isTouchDevice = ( 'ontouchend' in document );
     2138
     2139// Link any localized strings.
     2140l10n = media.view.l10n = window._wpMediaViewsL10n || {};
     2141
     2142// Link any settings.
     2143media.view.settings = l10n.settings || {};
     2144delete l10n.settings;
     2145
     2146// Copy the `post` setting over to the model settings.
     2147media.model.settings.post = media.view.settings.post;
     2148
     2149// Check if the browser supports CSS 3.0 transitions
     2150$.support.transition = (function(){
     2151    var style = document.documentElement.style,
     2152        transitions = {
     2153            WebkitTransition: 'webkitTransitionEnd',
     2154            MozTransition:    'transitionend',
     2155            OTransition:      'oTransitionEnd otransitionend',
     2156            transition:       'transitionend'
     2157        }, transition;
     2158
     2159    transition = _.find( _.keys( transitions ), function( transition ) {
     2160        return ! _.isUndefined( style[ transition ] );
     2161    });
     2162
     2163    return transition && {
     2164        end: transitions[ transition ]
     2165    };
     2166}());
    38322167
    38332168/**
    3834  * wp.media.view.MediaFrame.ImageDetails
    3835  *
    3836  * A media frame for manipulating an image that's already been inserted
    3837  * into a post.
    3838  *
    3839  * @class
    3840  * @augments wp.media.view.MediaFrame.Select
    3841  * @augments wp.media.view.MediaFrame
    3842  * @augments wp.media.view.Frame
    3843  * @augments wp.media.View
    3844  * @augments wp.Backbone.View
    3845  * @augments Backbone.View
    3846  * @mixes wp.media.controller.StateMachine
     2169 * A shared event bus used to provide events into
     2170 * the media workflows that 3rd-party devs can use to hook
     2171 * in.
    38472172 */
    3848 var Select = wp.media.view.MediaFrame.Select,
    3849     l10n = wp.media.view.l10n,
    3850     ImageDetails;
    3851 
    3852 ImageDetails = Select.extend({
    3853     defaults: {
    3854         id:      'image',
    3855         url:     '',
    3856         menu:    'image-details',
    3857         content: 'image-details',
    3858         toolbar: 'image-details',
    3859         type:    'link',
    3860         title:    l10n.imageDetailsTitle,
    3861         priority: 120
    3862     },
    3863 
    3864     initialize: function( options ) {
    3865         this.image = new wp.media.model.PostImage( options.metadata );
    3866         this.options.selection = new wp.media.model.Selection( this.image.attachment, { multiple: false } );
    3867         Select.prototype.initialize.apply( this, arguments );
    3868     },
    3869 
    3870     bindHandlers: function() {
    3871         Select.prototype.bindHandlers.apply( this, arguments );
    3872         this.on( 'menu:create:image-details', this.createMenu, this );
    3873         this.on( 'content:create:image-details', this.imageDetailsContent, this );
    3874         this.on( 'content:render:edit-image', this.editImageContent, this );
    3875         this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this );
    3876         // override the select toolbar
    3877         this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this );
    3878     },
    3879 
    3880     createStates: function() {
    3881         this.states.add([
    3882             new wp.media.controller.ImageDetails({
    3883                 image: this.image,
    3884                 editable: false
    3885             }),
    3886             new wp.media.controller.ReplaceImage({
    3887                 id: 'replace-image',
    3888                 library: wp.media.query( { type: 'image' } ),
    3889                 image: this.image,
    3890                 multiple:  false,
    3891                 title:     l10n.imageReplaceTitle,
    3892                 toolbar: 'replace',
    3893                 priority:  80,
    3894                 displaySettings: true
    3895             }),
    3896             new wp.media.controller.EditImage( {
    3897                 image: this.image,
    3898                 selection: this.options.selection
    3899             } )
    3900         ]);
    3901     },
    3902 
    3903     imageDetailsContent: function( options ) {
    3904         options.view = new wp.media.view.ImageDetails({
    3905             controller: this,
    3906             model: this.state().image,
    3907             attachment: this.state().image.attachment
    3908         });
    3909     },
    3910 
    3911     editImageContent: function() {
    3912         var state = this.state(),
    3913             model = state.get('image'),
    3914             view;
    3915 
    3916         if ( ! model ) {
    3917             return;
    3918         }
    3919 
    3920         view = new wp.media.view.EditImage( { model: model, controller: this } ).render();
    3921 
    3922         this.content.set( view );
    3923 
    3924         // after bringing in the frame, load the actual editor via an ajax call
    3925         view.loadEditor();
    3926 
    3927     },
    3928 
    3929     renderImageDetailsToolbar: function() {
    3930         this.toolbar.set( new wp.media.view.Toolbar({
    3931             controller: this,
    3932             items: {
    3933                 select: {
    3934                     style:    'primary',
    3935                     text:     l10n.update,
    3936                     priority: 80,
    3937 
    3938                     click: function() {
    3939                         var controller = this.controller,
    3940                             state = controller.state();
    3941 
    3942                         controller.close();
    3943 
    3944                         // not sure if we want to use wp.media.string.image which will create a shortcode or
    3945                         // perhaps wp.html.string to at least to build the <img />
    3946                         state.trigger( 'update', controller.image.toJSON() );
    3947 
    3948                         // Restore and reset the default state.
    3949                         controller.setState( controller.options.state );
    3950                         controller.reset();
    3951                     }
    3952                 }
    3953             }
    3954         }) );
    3955     },
    3956 
    3957     renderReplaceImageToolbar: function() {
    3958         var frame = this,
    3959             lastState = frame.lastState(),
    3960             previous = lastState && lastState.id;
    3961 
    3962         this.toolbar.set( new wp.media.view.Toolbar({
    3963             controller: this,
    3964             items: {
    3965                 back: {
    3966                     text:     l10n.back,
    3967                     priority: 20,
    3968                     click:    function() {
    3969                         if ( previous ) {
    3970                             frame.setState( previous );
    3971                         } else {
    3972                             frame.close();
    3973                         }
    3974                     }
    3975                 },
    3976 
    3977                 replace: {
    3978                     style:    'primary',
    3979                     text:     l10n.replace,
    3980                     priority: 80,
    3981 
    3982                     click: function() {
    3983                         var controller = this.controller,
    3984                             state = controller.state(),
    3985                             selection = state.get( 'selection' ),
    3986                             attachment = selection.single();
    3987 
    3988                         controller.close();
    3989 
    3990                         controller.image.changeAttachment( attachment, state.display( attachment ) );
    3991 
    3992                         // not sure if we want to use wp.media.string.image which will create a shortcode or
    3993                         // perhaps wp.html.string to at least to build the <img />
    3994                         state.trigger( 'replace', controller.image.toJSON() );
    3995 
    3996                         // Restore and reset the default state.
    3997                         controller.setState( controller.options.state );
    3998                         controller.reset();
    3999                     }
    4000                 }
    4001             }
    4002         }) );
     2173media.events = _.extend( {}, Backbone.Events );
     2174
     2175/**
     2176 * Makes it easier to bind events using transitions.
     2177 *
     2178 * @param {string} selector
     2179 * @param {Number} sensitivity
     2180 * @returns {Promise}
     2181 */
     2182media.transition = function( selector, sensitivity ) {
     2183    var deferred = $.Deferred();
     2184
     2185    sensitivity = sensitivity || 2000;
     2186
     2187    if ( $.support.transition ) {
     2188        if ( ! (selector instanceof $) ) {
     2189            selector = $( selector );
     2190        }
     2191
     2192        // Resolve the deferred when the first element finishes animating.
     2193        selector.first().one( $.support.transition.end, deferred.resolve );
     2194
     2195        // Just in case the event doesn't trigger, fire a callback.
     2196        _.delay( deferred.resolve, sensitivity );
     2197
     2198    // Otherwise, execute on the spot.
     2199    } else {
     2200        deferred.resolve();
    40032201    }
    40042202
    4005 });
    4006 
    4007 module.exports = ImageDetails;
    4008 
    4009 
    4010 /***/ }),
    4011 /* 51 */
    4012 /***/ (function(module, exports) {
    4013 
     2203    return deferred.promise();
     2204};
     2205
     2206media.controller.Region = require( './controllers/region.js' );
     2207media.controller.StateMachine = require( './controllers/state-machine.js' );
     2208media.controller.State = require( './controllers/state.js' );
     2209
     2210media.selectionSync = require( './utils/selection-sync.js' );
     2211media.controller.Library = require( './controllers/library.js' );
     2212media.controller.ImageDetails = require( './controllers/image-details.js' );
     2213media.controller.GalleryEdit = require( './controllers/gallery-edit.js' );
     2214media.controller.GalleryAdd = require( './controllers/gallery-add.js' );
     2215media.controller.CollectionEdit = require( './controllers/collection-edit.js' );
     2216media.controller.CollectionAdd = require( './controllers/collection-add.js' );
     2217media.controller.FeaturedImage = require( './controllers/featured-image.js' );
     2218media.controller.ReplaceImage = require( './controllers/replace-image.js' );
     2219media.controller.EditImage = require( './controllers/edit-image.js' );
     2220media.controller.MediaLibrary = require( './controllers/media-library.js' );
     2221media.controller.Embed = require( './controllers/embed.js' );
     2222media.controller.Cropper = require( './controllers/cropper.js' );
     2223media.controller.CustomizeImageCropper = require( './controllers/customize-image-cropper.js' );
     2224media.controller.SiteIconCropper = require( './controllers/site-icon-cropper.js' );
     2225
     2226media.View = require( './views/view.js' );
     2227media.view.Frame = require( './views/frame.js' );
     2228media.view.MediaFrame = require( './views/media-frame.js' );
     2229media.view.MediaFrame.Select = require( './views/frame/select.js' );
     2230media.view.MediaFrame.Post = require( './views/frame/post.js' );
     2231media.view.MediaFrame.ImageDetails = require( './views/frame/image-details.js' );
     2232media.view.Modal = require( './views/modal.js' );
     2233media.view.FocusManager = require( './views/focus-manager.js' );
     2234media.view.UploaderWindow = require( './views/uploader/window.js' );
     2235media.view.EditorUploader = require( './views/uploader/editor.js' );
     2236media.view.UploaderInline = require( './views/uploader/inline.js' );
     2237media.view.UploaderStatus = require( './views/uploader/status.js' );
     2238media.view.UploaderStatusError = require( './views/uploader/status-error.js' );
     2239media.view.Toolbar = require( './views/toolbar.js' );
     2240media.view.Toolbar.Select = require( './views/toolbar/select.js' );
     2241media.view.Toolbar.Embed = require( './views/toolbar/embed.js' );
     2242media.view.Button = require( './views/button.js' );
     2243media.view.ButtonGroup = require( './views/button-group.js' );
     2244media.view.PriorityList = require( './views/priority-list.js' );
     2245media.view.MenuItem = require( './views/menu-item.js' );
     2246media.view.Menu = require( './views/menu.js' );
     2247media.view.RouterItem = require( './views/router-item.js' );
     2248media.view.Router = require( './views/router.js' );
     2249media.view.Sidebar = require( './views/sidebar.js' );
     2250media.view.Attachment = require( './views/attachment.js' );
     2251media.view.Attachment.Library = require( './views/attachment/library.js' );
     2252media.view.Attachment.EditLibrary = require( './views/attachment/edit-library.js' );
     2253media.view.Attachments = require( './views/attachments.js' );
     2254media.view.Search = require( './views/search.js' );
     2255media.view.AttachmentFilters = require( './views/attachment-filters.js' );
     2256media.view.DateFilter = require( './views/attachment-filters/date.js' );
     2257media.view.AttachmentFilters.Uploaded = require( './views/attachment-filters/uploaded.js' );
     2258media.view.AttachmentFilters.All = require( './views/attachment-filters/all.js' );
     2259media.view.AttachmentsBrowser = require( './views/attachments/browser.js' );
     2260media.view.Selection = require( './views/selection.js' );
     2261media.view.Attachment.Selection = require( './views/attachment/selection.js' );
     2262media.view.Attachments.Selection = require( './views/attachments/selection.js' );
     2263media.view.Attachment.EditSelection = require( './views/attachment/edit-selection.js' );
     2264media.view.Settings = require( './views/settings.js' );
     2265media.view.Settings.AttachmentDisplay = require( './views/settings/attachment-display.js' );
     2266media.view.Settings.Gallery = require( './views/settings/gallery.js' );
     2267media.view.Settings.Playlist = require( './views/settings/playlist.js' );
     2268media.view.Attachment.Details = require( './views/attachment/details.js' );
     2269media.view.AttachmentCompat = require( './views/attachment-compat.js' );
     2270media.view.Iframe = require( './views/iframe.js' );
     2271media.view.Embed = require( './views/embed.js' );
     2272media.view.Label = require( './views/label.js' );
     2273media.view.EmbedUrl = require( './views/embed/url.js' );
     2274media.view.EmbedLink = require( './views/embed/link.js' );
     2275media.view.EmbedImage = require( './views/embed/image.js' );
     2276media.view.ImageDetails = require( './views/image-details.js' );
     2277media.view.Cropper = require( './views/cropper.js' );
     2278media.view.SiteIconCropper = require( './views/site-icon-cropper.js' );
     2279media.view.SiteIconPreview = require( './views/site-icon-preview.js' );
     2280media.view.EditImage = require( './views/edit-image.js' );
     2281media.view.Spinner = require( './views/spinner.js' );
     2282
     2283},{"./controllers/collection-add.js":1,"./controllers/collection-edit.js":2,"./controllers/cropper.js":3,"./controllers/customize-image-cropper.js":4,"./controllers/edit-image.js":5,"./controllers/embed.js":6,"./controllers/featured-image.js":7,"./controllers/gallery-add.js":8,"./controllers/gallery-edit.js":9,"./controllers/image-details.js":10,"./controllers/library.js":11,"./controllers/media-library.js":12,"./controllers/region.js":13,"./controllers/replace-image.js":14,"./controllers/site-icon-cropper.js":15,"./controllers/state-machine.js":16,"./controllers/state.js":17,"./utils/selection-sync.js":18,"./views/attachment-compat.js":20,"./views/attachment-filters.js":21,"./views/attachment-filters/all.js":22,"./views/attachment-filters/date.js":23,"./views/attachment-filters/uploaded.js":24,"./views/attachment.js":25,"./views/attachment/details.js":26,"./views/attachment/edit-library.js":27,"./views/attachment/edit-selection.js":28,"./views/attachment/library.js":29,"./views/attachment/selection.js":30,"./views/attachments.js":31,"./views/attachments/browser.js":32,"./views/attachments/selection.js":33,"./views/button-group.js":34,"./views/button.js":35,"./views/cropper.js":36,"./views/edit-image.js":37,"./views/embed.js":38,"./views/embed/image.js":39,"./views/embed/link.js":40,"./views/embed/url.js":41,"./views/focus-manager.js":42,"./views/frame.js":43,"./views/frame/image-details.js":44,"./views/frame/post.js":45,"./views/frame/select.js":46,"./views/iframe.js":47,"./views/image-details.js":48,"./views/label.js":49,"./views/media-frame.js":50,"./views/menu-item.js":51,"./views/menu.js":52,"./views/modal.js":53,"./views/priority-list.js":54,"./views/router-item.js":55,"./views/router.js":56,"./views/search.js":57,"./views/selection.js":58,"./views/settings.js":59,"./views/settings/attachment-display.js":60,"./views/settings/gallery.js":61,"./views/settings/playlist.js":62,"./views/sidebar.js":63,"./views/site-icon-cropper.js":64,"./views/site-icon-preview.js":65,"./views/spinner.js":66,"./views/toolbar.js":67,"./views/toolbar/embed.js":68,"./views/toolbar/select.js":69,"./views/uploader/editor.js":70,"./views/uploader/inline.js":71,"./views/uploader/status-error.js":72,"./views/uploader/status.js":73,"./views/uploader/window.js":74,"./views/view.js":75}],20:[function(require,module,exports){
    40142284/**
    4015  * wp.media.view.Modal
    4016  *
    4017  * A modal view, which the media modal uses as its default container.
    4018  *
    4019  * @class
    4020  * @augments wp.media.View
    4021  * @augments wp.Backbone.View
    4022  * @augments Backbone.View
    4023  */
    4024 var $ = jQuery,
    4025     Modal;
    4026 
    4027 Modal = wp.media.View.extend({
    4028     tagName:  'div',
    4029     template: wp.template('media-modal'),
    4030 
    4031     attributes: {
    4032         tabindex: 0
    4033     },
    4034 
    4035     events: {
    4036         'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
    4037         'keydown': 'keydown'
    4038     },
    4039 
    4040     initialize: function() {
    4041         _.defaults( this.options, {
    4042             container: document.body,
    4043             title:     '',
    4044             propagate: true,
    4045             freeze:    true
    4046         });
    4047 
    4048         this.focusManager = new wp.media.view.FocusManager({
    4049             el: this.el
    4050         });
    4051     },
    4052     /**
    4053      * @returns {Object}
    4054      */
    4055     prepare: function() {
    4056         return {
    4057             title: this.options.title
    4058         };
    4059     },
    4060 
    4061     /**
    4062      * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4063      */
    4064     attach: function() {
    4065         if ( this.views.attached ) {
    4066             return this;
    4067         }
    4068 
    4069         if ( ! this.views.rendered ) {
    4070             this.render();
    4071         }
    4072 
    4073         this.$el.appendTo( this.options.container );
    4074 
    4075         // Manually mark the view as attached and trigger ready.
    4076         this.views.attached = true;
    4077         this.views.ready();
    4078 
    4079         return this.propagate('attach');
    4080     },
    4081 
    4082     /**
    4083      * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4084      */
    4085     detach: function() {
    4086         if ( this.$el.is(':visible') ) {
    4087             this.close();
    4088         }
    4089 
    4090         this.$el.detach();
    4091         this.views.attached = false;
    4092         return this.propagate('detach');
    4093     },
    4094 
    4095     /**
    4096      * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4097      */
    4098     open: function() {
    4099         var $el = this.$el,
    4100             options = this.options,
    4101             mceEditor;
    4102 
    4103         if ( $el.is(':visible') ) {
    4104             return this;
    4105         }
    4106 
    4107         if ( ! this.views.attached ) {
    4108             this.attach();
    4109         }
    4110 
    4111         // If the `freeze` option is set, record the window's scroll position.
    4112         if ( options.freeze ) {
    4113             this._freeze = {
    4114                 scrollTop: $( window ).scrollTop()
    4115             };
    4116         }
    4117 
    4118         // Disable page scrolling.
    4119         $( 'body' ).addClass( 'modal-open' );
    4120 
    4121         $el.show();
    4122 
    4123         // Try to close the onscreen keyboard
    4124         if ( 'ontouchend' in document ) {
    4125             if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor )  && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
    4126                 mceEditor.iframeElement.focus();
    4127                 mceEditor.iframeElement.blur();
    4128 
    4129                 setTimeout( function() {
    4130                     mceEditor.iframeElement.blur();
    4131                 }, 100 );
    4132             }
    4133         }
    4134 
    4135         this.$el.focus();
    4136 
    4137         return this.propagate('open');
    4138     },
    4139 
    4140     /**
    4141      * @param {Object} options
    4142      * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4143      */
    4144     close: function( options ) {
    4145         var freeze = this._freeze;
    4146 
    4147         if ( ! this.views.attached || ! this.$el.is(':visible') ) {
    4148             return this;
    4149         }
    4150 
    4151         // Enable page scrolling.
    4152         $( 'body' ).removeClass( 'modal-open' );
    4153 
    4154         // Hide modal and remove restricted media modal tab focus once it's closed
    4155         this.$el.hide().undelegate( 'keydown' );
    4156 
    4157         // Put focus back in useful location once modal is closed
    4158         $('#wpbody-content').focus();
    4159 
    4160         this.propagate('close');
    4161 
    4162         // If the `freeze` option is set, restore the container's scroll position.
    4163         if ( freeze ) {
    4164             $( window ).scrollTop( freeze.scrollTop );
    4165         }
    4166 
    4167         if ( options && options.escape ) {
    4168             this.propagate('escape');
    4169         }
    4170 
    4171         return this;
    4172     },
    4173     /**
    4174      * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4175      */
    4176     escape: function() {
    4177         return this.close({ escape: true });
    4178     },
    4179     /**
    4180      * @param {Object} event
    4181      */
    4182     escapeHandler: function( event ) {
    4183         event.preventDefault();
    4184         this.escape();
    4185     },
    4186 
    4187     /**
    4188      * @param {Array|Object} content Views to register to '.media-modal-content'
    4189      * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4190      */
    4191     content: function( content ) {
    4192         this.views.set( '.media-modal-content', content );
    4193         return this;
    4194     },
    4195 
    4196     /**
    4197      * Triggers a modal event and if the `propagate` option is set,
    4198      * forwards events to the modal's controller.
    4199      *
    4200      * @param {string} id
    4201      * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4202      */
    4203     propagate: function( id ) {
    4204         this.trigger( id );
    4205 
    4206         if ( this.options.propagate ) {
    4207             this.controller.trigger( id );
    4208         }
    4209 
    4210         return this;
    4211     },
    4212     /**
    4213      * @param {Object} event
    4214      */
    4215     keydown: function( event ) {
    4216         // Close the modal when escape is pressed.
    4217         if ( 27 === event.which && this.$el.is(':visible') ) {
    4218             this.escape();
    4219             event.stopImmediatePropagation();
    4220         }
    4221     }
    4222 });
    4223 
    4224 module.exports = Modal;
    4225 
    4226 
    4227 /***/ }),
    4228 /* 52 */
    4229 /***/ (function(module, exports) {
    4230 
    4231 /**
    4232  * wp.media.view.FocusManager
    4233  *
    4234  * @class
    4235  * @augments wp.media.View
    4236  * @augments wp.Backbone.View
    4237  * @augments Backbone.View
    4238  */
    4239 var FocusManager = wp.media.View.extend({
    4240 
    4241     events: {
    4242         'keydown': 'constrainTabbing'
    4243     },
    4244 
    4245     focus: function() { // Reset focus on first left menu item
    4246         this.$('.media-menu-item').first().focus();
    4247     },
    4248     /**
    4249      * @param {Object} event
    4250      */
    4251     constrainTabbing: function( event ) {
    4252         var tabbables;
    4253 
    4254         // Look for the tab key.
    4255         if ( 9 !== event.keyCode ) {
    4256             return;
    4257         }
    4258 
    4259         // Skip the file input added by Plupload.
    4260         tabbables = this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' );
    4261 
    4262         // Keep tab focus within media modal while it's open
    4263         if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
    4264             tabbables.first().focus();
    4265             return false;
    4266         } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
    4267             tabbables.last().focus();
    4268             return false;
    4269         }
    4270     }
    4271 
    4272 });
    4273 
    4274 module.exports = FocusManager;
    4275 
    4276 
    4277 /***/ }),
    4278 /* 53 */
    4279 /***/ (function(module, exports) {
    4280 
    4281 /**
    4282  * wp.media.view.UploaderWindow
    4283  *
    4284  * An uploader window that allows for dragging and dropping media.
    4285  *
    4286  * @class
    4287  * @augments wp.media.View
    4288  * @augments wp.Backbone.View
    4289  * @augments Backbone.View
    4290  *
    4291  * @param {object} [options]                   Options hash passed to the view.
    4292  * @param {object} [options.uploader]          Uploader properties.
    4293  * @param {jQuery} [options.uploader.browser]
    4294 </