Make WordPress Core

Changeset 50019


Ignore:
Timestamp:
01/25/2021 08:15:27 PM (6 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  * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.
    4295  * @param {object} [options.uploader.params]
    4296  */
    4297 var $ = jQuery,
    4298         UploaderWindow;
    4299 
    4300 UploaderWindow = wp.media.View.extend({
    4301         tagName:   'div',
    4302         className: 'uploader-window',
    4303         template:  wp.template('uploader-window'),
    4304 
    4305         initialize: function() {
    4306                 var uploader;
    4307 
    4308                 this.$browser = $('<a href="#" class="browser" />').hide().appendTo('body');
    4309 
    4310                 uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
    4311                         dropzone:  this.$el,
    4312                         browser:   this.$browser,
    4313                         params:    {}
    4314                 });
    4315 
    4316                 // Ensure the dropzone is a jQuery collection.
    4317                 if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) {
    4318                         uploader.dropzone = $( uploader.dropzone );
    4319                 }
    4320 
    4321                 this.controller.on( 'activate', this.refresh, this );
    4322 
    4323                 this.controller.on( 'detach', function() {
    4324                         this.$browser.remove();
    4325                 }, this );
    4326         },
    4327 
    4328         refresh: function() {
    4329                 if ( this.uploader ) {
    4330                         this.uploader.refresh();
    4331                 }
    4332         },
    4333 
    4334         ready: function() {
    4335                 var postId = wp.media.view.settings.post.id,
    4336                         dropzone;
    4337 
    4338                 // If the uploader already exists, bail.
    4339                 if ( this.uploader ) {
    4340                         return;
    4341                 }
    4342 
    4343                 if ( postId ) {
    4344                         this.options.uploader.params.post_id = postId;
    4345                 }
    4346                 this.uploader = new wp.Uploader( this.options.uploader );
    4347 
    4348                 dropzone = this.uploader.dropzone;
    4349                 dropzone.on( 'dropzone:enter', _.bind( this.show, this ) );
    4350                 dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) );
    4351 
    4352                 $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) );
    4353         },
    4354 
    4355         _ready: function() {
    4356                 this.controller.trigger( 'uploader:ready' );
    4357         },
    4358 
    4359         show: function() {
    4360                 var $el = this.$el.show();
    4361 
    4362                 // Ensure that the animation is triggered by waiting until
    4363                 // the transparent element is painted into the DOM.
    4364                 _.defer( function() {
    4365                         $el.css({ opacity: 1 });
    4366                 });
    4367         },
    4368 
    4369         hide: function() {
    4370                 var $el = this.$el.css({ opacity: 0 });
    4371 
    4372                 wp.media.transition( $el ).done( function() {
    4373                         // Transition end events are subject to race conditions.
    4374                         // Make sure that the value is set as intended.
    4375                         if ( '0' === $el.css('opacity') ) {
    4376                                 $el.hide();
    4377                         }
    4378                 });
    4379 
    4380                 // https://core.trac.wordpress.org/ticket/27341
    4381                 _.delay( function() {
    4382                         if ( '0' === $el.css('opacity') && $el.is(':visible') ) {
    4383                                 $el.hide();
    4384                         }
    4385                 }, 500 );
    4386         }
    4387 });
    4388 
    4389 module.exports = UploaderWindow;
    4390 
    4391 
    4392 /***/ }),
    4393 /* 54 */
    4394 /***/ (function(module, exports) {
    4395 
    4396 /**
    4397  * Creates a dropzone on WP editor instances (elements with .wp-editor-wrap)
    4398  * and relays drag'n'dropped files to a media workflow.
    4399  *
    4400  * wp.media.view.EditorUploader
     2285 * wp.media.view.AttachmentCompat
     2286 *
     2287 * A view to display fields added via the `attachment_fields_to_edit` filter.
    44012288 *
    44022289 * @class
    … …  
    44062293 */
    44072294var View = wp.media.View,
    4408         l10n = wp.media.view.l10n,
    4409         $ = jQuery,
    4410         EditorUploader;
    4411 
    4412 EditorUploader = View.extend({
    4413         tagName:   'div',
    4414         className: 'uploader-editor',
    4415         template:  wp.template( 'uploader-editor' ),
    4416 
    4417         localDrag: false,
    4418         overContainer: false,
    4419         overDropzone: false,
    4420         draggingFile: null,
    4421 
    4422         /**
    4423          * Bind drag'n'drop events to callbacks.
    4424          */
     2295        AttachmentCompat;
     2296
     2297AttachmentCompat = View.extend({
     2298        tagName:   'form',
     2299        className: 'compat-item',
     2300
     2301        events: {
     2302                'submit':          'preventDefault',
     2303                'change input':    'save',
     2304                'change select':   'save',
     2305                'change textarea': 'save'
     2306        },
     2307
    44252308        initialize: function() {
    4426                 this.initialized = false;
    4427 
    4428                 // Bail if not enabled or UA does not support drag'n'drop or File API.
    4429                 if ( ! window.tinyMCEPreInit || ! window.tinyMCEPreInit.dragDropUpload || ! this.browserSupport() ) {
    4430                         return this;
    4431                 }
    4432 
    4433                 this.$document = $(document);
    4434                 this.dropzones = [];
    4435                 this.files = [];
    4436 
    4437                 this.$document.on( 'drop', '.uploader-editor', _.bind( this.drop, this ) );
    4438                 this.$document.on( 'dragover', '.uploader-editor', _.bind( this.dropzoneDragover, this ) );
    4439                 this.$document.on( 'dragleave', '.uploader-editor', _.bind( this.dropzoneDragleave, this ) );
    4440                 this.$document.on( 'click', '.uploader-editor', _.bind( this.click, this ) );
    4441 
    4442                 this.$document.on( 'dragover', _.bind( this.containerDragover, this ) );
    4443                 this.$document.on( 'dragleave', _.bind( this.containerDragleave, this ) );
    4444 
    4445                 this.$document.on( 'dragstart dragend drop', _.bind( function( event ) {
    4446                         this.localDrag = event.type === 'dragstart';
    4447                 }, this ) );
    4448 
    4449                 this.initialized = true;
    4450                 return this;
    4451         },
    4452 
    4453         /**
    4454          * Check browser support for drag'n'drop.
    4455          *
    4456          * @return Boolean
    4457          */
    4458         browserSupport: function() {
    4459                 var supports = false, div = document.createElement('div');
    4460 
    4461                 supports = ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div );
    4462                 supports = supports && !! ( window.File && window.FileList && window.FileReader );
    4463                 return supports;
    4464         },
    4465 
    4466         isDraggingFile: function( event ) {
    4467                 if ( this.draggingFile !== null ) {
    4468                         return this.draggingFile;
    4469                 }
    4470 
    4471                 if ( _.isUndefined( event.originalEvent ) || _.isUndefined( event.originalEvent.dataTransfer ) ) {
    4472                         return false;
    4473                 }
    4474 
    4475                 this.draggingFile = _.indexOf( event.originalEvent.dataTransfer.types, 'Files' ) > -1 &&
    4476                         _.indexOf( event.originalEvent.dataTransfer.types, 'text/plain' ) === -1;
    4477 
    4478                 return this.draggingFile;
    4479         },
    4480 
    4481         refresh: function( e ) {
    4482                 var dropzone_id;
    4483                 for ( dropzone_id in this.dropzones ) {
    4484                         // Hide the dropzones only if dragging has left the screen.
    4485                         this.dropzones[ dropzone_id ].toggle( this.overContainer || this.overDropzone );
    4486                 }
    4487 
    4488                 if ( ! _.isUndefined( e ) ) {
    4489                         $( e.target ).closest( '.uploader-editor' ).toggleClass( 'droppable', this.overDropzone );
    4490                 }
    4491 
    4492                 if ( ! this.overContainer && ! this.overDropzone ) {
    4493                         this.draggingFile = null;
    4494                 }
    4495 
    4496                 return this;
    4497         },
    4498 
    4499         render: function() {
    4500                 if ( ! this.initialized ) {
    4501                         return this;
    4502                 }
    4503 
    4504                 View.prototype.render.apply( this, arguments );
    4505                 $( '.wp-editor-wrap' ).each( _.bind( this.attach, this ) );
    4506                 return this;
    4507         },
    4508 
    4509         attach: function( index, editor ) {
    4510                 // Attach a dropzone to an editor.
    4511                 var dropzone = this.$el.clone();
    4512                 this.dropzones.push( dropzone );
    4513                 $( editor ).append( dropzone );
    4514                 return this;
    4515         },
    4516 
    4517         /**
    4518          * When a file is dropped on the editor uploader, open up an editor media workflow
    4519          * and upload the file immediately.
    4520          *
    4521          * @param  {jQuery.Event} event The 'drop' event.
    4522          */
    4523         drop: function( event ) {
    4524                 var $wrap, uploadView;
    4525 
    4526                 this.containerDragleave( event );
    4527                 this.dropzoneDragleave( event );
    4528 
    4529                 this.files = event.originalEvent.dataTransfer.files;
    4530                 if ( this.files.length < 1 ) {
    4531                         return;
    4532                 }
    4533 
    4534                 // Set the active editor to the drop target.
    4535                 $wrap = $( event.target ).parents( '.wp-editor-wrap' );
    4536                 if ( $wrap.length > 0 && $wrap[0].id ) {
    4537                         window.wpActiveEditor = $wrap[0].id.slice( 3, -5 );
    4538                 }
    4539 
    4540                 if ( ! this.workflow ) {
    4541                         this.workflow = wp.media.editor.open( window.wpActiveEditor, {
    4542                                 frame:    'post',
    4543                                 state:    'insert',
    4544                                 title:    l10n.addMedia,
    4545                                 multiple: true
    4546                         });
    4547 
    4548                         uploadView = this.workflow.uploader;
    4549 
    4550                         if ( uploadView.uploader && uploadView.uploader.ready ) {
    4551                                 this.addFiles.apply( this );
    4552                         } else {
    4553                                 this.workflow.on( 'uploader:ready', this.addFiles, this );
    4554                         }
    4555                 } else {
    4556                         this.workflow.state().reset();
    4557                         this.addFiles.apply( this );
    4558                         this.workflow.open();
    4559                 }
    4560 
    4561                 return false;
    4562         },
    4563 
    4564         /**
    4565          * Add the files to the uploader.
    4566          */
    4567         addFiles: function() {
    4568                 if ( this.files.length ) {
    4569                         this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) );
    4570                         this.files = [];
    4571                 }
    4572                 return this;
    4573         },
    4574 
    4575         containerDragover: function( event ) {
    4576                 if ( this.localDrag || ! this.isDraggingFile( event ) ) {
    4577                         return;
    4578                 }
    4579 
    4580                 this.overContainer = true;
    4581                 this.refresh();
    4582         },
    4583 
    4584         containerDragleave: function() {
    4585                 this.overContainer = false;
    4586 
    4587                 // Throttle dragleave because it's called when bouncing from some elements to others.
    4588                 _.delay( _.bind( this.refresh, this ), 50 );
    4589         },
    4590 
    4591         dropzoneDragover: function( event ) {
    4592                 if ( this.localDrag || ! this.isDraggingFile( event ) ) {
    4593                         return;
    4594                 }
    4595 
    4596                 this.overDropzone = true;
    4597                 this.refresh( event );
    4598                 return false;
    4599         },
    4600 
    4601         dropzoneDragleave: function( e ) {
    4602                 this.overDropzone = false;
    4603                 _.delay( _.bind( this.refresh, this, e ), 50 );
    4604         },
    4605 
    4606         click: function( e ) {
    4607                 // In the rare case where the dropzone gets stuck, hide it on click.
    4608                 this.containerDragleave( e );
    4609                 this.dropzoneDragleave( e );
    4610                 this.localDrag = false;
    4611         }
    4612 });
    4613 
    4614 module.exports = EditorUploader;
    4615 
    4616 
    4617 /***/ }),
    4618 /* 55 */
    4619 /***/ (function(module, exports) {
    4620 
    4621 /**
    4622  * wp.media.view.UploaderInline
    4623  *
    4624  * The inline uploader that shows up in the 'Upload Files' tab.
    4625  *
    4626  * @class
    4627  * @augments wp.media.View
    4628  * @augments wp.Backbone.View
    4629  * @augments Backbone.View
    4630  */
    4631 var View = wp.media.View,
    4632         UploaderInline;
    4633 
    4634 UploaderInline = View.extend({
    4635         tagName:   'div',
    4636         className: 'uploader-inline',
    4637         template:  wp.template('uploader-inline'),
    4638 
    4639         events: {
    4640                 'click .close': 'hide'
    4641         },
    4642 
    4643         initialize: function() {
    4644                 _.defaults( this.options, {
    4645                         message: '',
    4646                         status:  true,
    4647                         canClose: false
    4648                 });
    4649 
    4650                 if ( ! this.options.$browser && this.controller.uploader ) {
    4651                         this.options.$browser = this.controller.uploader.$browser;
    4652                 }
    4653 
    4654                 if ( _.isUndefined( this.options.postId ) ) {
    4655                         this.options.postId = wp.media.view.settings.post.id;
    4656                 }
    4657 
    4658                 if ( this.options.status ) {
    4659                         this.views.set( '.upload-inline-status', new wp.media.view.UploaderStatus({
    4660                                 controller: this.controller
    4661                         }) );
    4662                 }
    4663         },
    4664 
    4665         prepare: function() {
    4666                 var suggestedWidth = this.controller.state().get('suggestedWidth'),
    4667                         suggestedHeight = this.controller.state().get('suggestedHeight'),
    4668                         data = {};
    4669 
    4670                 data.message = this.options.message;
    4671                 data.canClose = this.options.canClose;
    4672 
    4673                 if ( suggestedWidth && suggestedHeight ) {
    4674                         data.suggestedWidth = suggestedWidth;
    4675                         data.suggestedHeight = suggestedHeight;
    4676                 }
    4677 
    4678                 return data;
    4679         },
    4680         /**
    4681          * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
     2309                this.listenTo( this.model, 'change:compat', this.render );
     2310        },
     2311        /**
     2312         * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
    46822313         */
    46832314        dispose: function() {
    4684                 if ( this.disposing ) {
    4685                         /**
    4686                          * call 'dispose' directly on the parent class
    4687                          */
    4688                         return View.prototype.dispose.apply( this, arguments );
    4689                 }
    4690 
    4691                 // Run remove on `dispose`, so we can be sure to refresh the
    4692                 // uploader with a view-less DOM. Track whether we're disposing
    4693                 // so we don't trigger an infinite loop.
    4694                 this.disposing = true;
    4695                 return this.remove();
    4696         },
    4697         /**
    4698          * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
    4699          */
    4700         remove: function() {
    4701                 /**
    4702                  * call 'remove' directly on the parent class
    4703                  */
    4704                 var result = View.prototype.remove.apply( this, arguments );
    4705 
    4706                 _.defer( _.bind( this.refresh, this ) );
    4707                 return result;
    4708         },
    4709 
    4710         refresh: function() {
    4711                 var uploader = this.controller.uploader;
    4712 
    4713                 if ( uploader ) {
    4714                         uploader.refresh();
    4715                 }
    4716         },
    4717         /**
    4718          * @returns {wp.media.view.UploaderInline}
    4719          */
    4720         ready: function() {
    4721                 var $browser = this.options.$browser,
    4722                         $placeholder;
    4723 
    4724                 if ( this.controller.uploader ) {
    4725                         $placeholder = this.$('.browser');
    4726 
    4727                         // Check if we've already replaced the placeholder.
    4728                         if ( $placeholder[0] === $browser[0] ) {
    4729                                 return;
    4730                         }
    4731 
    4732                         $browser.detach().text( $placeholder.text() );
    4733                         $browser[0].className = $placeholder[0].className;
    4734                         $placeholder.replaceWith( $browser.show() );
    4735                 }
    4736 
    4737                 this.refresh();
    4738                 return this;
    4739         },
    4740         show: function() {
    4741                 this.$el.removeClass( 'hidden' );
    4742         },
    4743         hide: function() {
    4744                 this.$el.addClass( 'hidden' );
    4745         }
    4746 
    4747 });
    4748 
    4749 module.exports = UploaderInline;
    4750 
    4751 
    4752 /***/ }),
    4753 /* 56 */
    4754 /***/ (function(module, exports) {
    4755 
    4756 /**
    4757  * wp.media.view.UploaderStatus
    4758  *
    4759  * An uploader status for on-going uploads.
    4760  *
    4761  * @class
    4762  * @augments wp.media.View
    4763  * @augments wp.Backbone.View
    4764  * @augments Backbone.View
    4765  */
    4766 var View = wp.media.View,
    4767         UploaderStatus;
    4768 
    4769 UploaderStatus = View.extend({
    4770         className: 'media-uploader-status',
    4771         template:  wp.template('uploader-status'),
    4772 
    4773         events: {
    4774                 'click .upload-dismiss-errors': 'dismiss'
    4775         },
    4776 
    4777         initialize: function() {
    4778                 this.queue = wp.Uploader.queue;
    4779                 this.queue.on( 'add remove reset', this.visibility, this );
    4780                 this.queue.on( 'add remove reset change:percent', this.progress, this );
    4781                 this.queue.on( 'add remove reset change:uploading', this.info, this );
    4782 
    4783                 this.errors = wp.Uploader.errors;
    4784                 this.errors.reset();
    4785                 this.errors.on( 'add remove reset', this.visibility, this );
    4786                 this.errors.on( 'add', this.error, this );
    4787         },
    4788         /**
    4789          * @global wp.Uploader
    4790          * @returns {wp.media.view.UploaderStatus}
    4791          */
    4792         dispose: function() {
    4793                 wp.Uploader.queue.off( null, null, this );
    4794                 /**
    4795                  * call 'dispose' directly on the parent class
    4796                  */
    4797                 View.prototype.dispose.apply( this, arguments );
    4798                 return this;
    4799         },
    4800 
    4801         visibility: function() {
    4802                 this.$el.toggleClass( 'uploading', !! this.queue.length );
    4803                 this.$el.toggleClass( 'errors', !! this.errors.length );
    4804                 this.$el.toggle( !! this.queue.length || !! this.errors.length );
    4805         },
    4806 
    4807         ready: function() {
    4808                 _.each({
    4809                         '$bar':      '.media-progress-bar div',
    4810                         '$index':    '.upload-index',
    4811                         '$total':    '.upload-total',
    4812                         '$filename': '.upload-filename'
    4813                 }, function( selector, key ) {
    4814                         this[ key ] = this.$( selector );
    4815                 }, this );
    4816 
    4817                 this.visibility();
    4818                 this.progress();
    4819                 this.info();
    4820         },
    4821 
    4822         progress: function() {
    4823                 var queue = this.queue,
    4824                         $bar = this.$bar;
    4825 
    4826                 if ( ! $bar || ! queue.length ) {
    4827                         return;
    4828                 }
    4829 
    4830                 $bar.width( ( queue.reduce( function( memo, attachment ) {
    4831                         if ( ! attachment.get('uploading') ) {
    4832                                 return memo + 100;
    4833                         }
    4834 
    4835                         var percent = attachment.get('percent');
    4836                         return memo + ( _.isNumber( percent ) ? percent : 100 );
    4837                 }, 0 ) / queue.length ) + '%' );
    4838         },
    4839 
    4840         info: function() {
    4841                 var queue = this.queue,
    4842                         index = 0, active;
    4843 
    4844                 if ( ! queue.length ) {
    4845                         return;
    4846                 }
    4847 
    4848                 active = this.queue.find( function( attachment, i ) {
    4849                         index = i;
    4850                         return attachment.get('uploading');
    4851                 });
    4852 
    4853                 this.$index.text( index + 1 );
    4854                 this.$total.text( queue.length );
    4855                 this.$filename.html( active ? this.filename( active.get('filename') ) : '' );
    4856         },
    4857         /**
    4858          * @param {string} filename
    4859          * @returns {string}
    4860          */
    4861         filename: function( filename ) {
    4862                 return _.escape( filename );
    4863         },
    4864         /**
    4865          * @param {Backbone.Model} error
    4866          */
    4867         error: function( error ) {
    4868                 this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({
    4869                         filename: this.filename( error.get('file').name ),
    4870                         message:  error.get('message')
    4871                 }), { at: 0 });
    4872         },
    4873 
    4874         /**
    4875          * @global wp.Uploader
    4876          *
    4877          * @param {Object} event
    4878          */
    4879         dismiss: function( event ) {
    4880                 var errors = this.views.get('.upload-errors');
    4881 
    4882                 event.preventDefault();
    4883 
    4884                 if ( errors ) {
    4885                         _.invoke( errors, 'remove' );
    4886                 }
    4887                 wp.Uploader.errors.reset();
    4888         }
    4889 });
    4890 
    4891 module.exports = UploaderStatus;
    4892 
    4893 
    4894 /***/ }),
    4895 /* 57 */
    4896 /***/ (function(module, exports) {
    4897 
    4898 /**
    4899  * wp.media.view.UploaderStatusError
    4900  *
    4901  * @class
    4902  * @augments wp.media.View
    4903  * @augments wp.Backbone.View
    4904  * @augments Backbone.View
    4905  */
    4906 var UploaderStatusError = wp.media.View.extend({
    4907         className: 'upload-error',
    4908         template:  wp.template('uploader-status-error')
    4909 });
    4910 
    4911 module.exports = UploaderStatusError;
    4912 
    4913 
    4914 /***/ }),
    4915 /* 58 */
    4916 /***/ (function(module, exports) {
    4917 
    4918 /**
    4919  * wp.media.view.Toolbar
    4920  *
    4921  * A toolbar which consists of a primary and a secondary section. Each sections
    4922  * can be filled with views.
    4923  *
    4924  * @class
    4925  * @augments wp.media.View
    4926  * @augments wp.Backbone.View
    4927  * @augments Backbone.View
    4928  */
    4929 var View = wp.media.View,
    4930         Toolbar;
    4931 
    4932 Toolbar = View.extend({
    4933         tagName:   'div',
    4934         className: 'media-toolbar',
    4935 
    4936         initialize: function() {
    4937                 var state = this.controller.state(),
    4938                         selection = this.selection = state.get('selection'),
    4939                         library = this.library = state.get('library');
    4940 
    4941                 this._views = {};
    4942 
    4943                 // The toolbar is composed of two `PriorityList` views.
    4944                 this.primary   = new wp.media.view.PriorityList();
    4945                 this.secondary = new wp.media.view.PriorityList();
    4946                 this.primary.$el.addClass('media-toolbar-primary search-form');
    4947                 this.secondary.$el.addClass('media-toolbar-secondary');
    4948 
    4949                 this.views.set([ this.secondary, this.primary ]);
    4950 
    4951                 if ( this.options.items ) {
    4952                         this.set( this.options.items, { silent: true });
    4953                 }
    4954 
    4955                 if ( ! this.options.silent ) {
    4956                         this.render();
    4957                 }
    4958 
    4959                 if ( selection ) {
    4960                         selection.on( 'add remove reset', this.refresh, this );
    4961                 }
    4962 
    4963                 if ( library ) {
    4964                         library.on( 'add remove reset', this.refresh, this );
    4965                 }
    4966         },
    4967         /**
    4968          * @returns {wp.media.view.Toolbar} Returns itsef to allow chaining
    4969          */
    4970         dispose: function() {
    4971                 if ( this.selection ) {
    4972                         this.selection.off( null, null, this );
    4973                 }
    4974 
    4975                 if ( this.library ) {
    4976                         this.library.off( null, null, this );
     2315                if ( this.$(':focus').length ) {
     2316                        this.save();
    49772317                }
    49782318                /**
    … …  
    49812321                return View.prototype.dispose.apply( this, arguments );
    49822322        },
    4983 
    4984         ready: function() {
    4985                 this.refresh();
    4986         },
    4987 
    4988         /**
    4989          * @param {string} id
    4990          * @param {Backbone.View|Object} view
    4991          * @param {Object} [options={}]
    4992          * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
    4993          */
    4994         set: function( id, view, options ) {
    4995                 var list;
    4996                 options = options || {};
    4997 
    4998                 // Accept an object with an `id` : `view` mapping.
    4999                 if ( _.isObject( id ) ) {
    5000                         _.each( id, function( view, id ) {
    5001                                 this.set( id, view, { silent: true });
    5002                         }, this );
    5003 
    5004                 } else {
    5005                         if ( ! ( view instanceof Backbone.View ) ) {
    5006                                 view.classes = [ 'media-button-' + id ].concat( view.classes || [] );
    5007                                 view = new wp.media.view.Button( view ).render();
    5008                         }
    5009 
    5010                         view.controller = view.controller || this.controller;
    5011 
    5012                         this._views[ id ] = view;
    5013 
    5014                         list = view.options.priority < 0 ? 'secondary' : 'primary';
    5015                         this[ list ].set( id, view, options );
    5016                 }
    5017 
    5018                 if ( ! options.silent ) {
    5019                         this.refresh();
    5020                 }
    5021 
     2323        /**
     2324         * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
     2325         */
     2326        render: function() {
     2327                var compat = this.model.get('compat');
     2328                if ( ! compat || ! compat.item ) {
     2329                        return;
     2330                }
     2331
     2332                this.views.detach();
     2333                this.$el.html( compat.item );
     2334                this.views.render();
    50222335                return this;
    50232336        },
    50242337        /**
    5025          * @param {string} id
    5026          * @returns {wp.media.view.Button}
    5027          */
    5028         get: function( id ) {
    5029                 return this._views[ id ];
    5030         },
    5031         /**
    5032          * @param {string} id
    5033          * @param {Object} options
    5034          * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
    5035          */
    5036         unset: function( id, options ) {
    5037                 delete this._views[ id ];
    5038                 this.primary.unset( id, options );
    5039                 this.secondary.unset( id, options );
    5040 
    5041                 if ( ! options || ! options.silent ) {
    5042                         this.refresh();
    5043                 }
    5044                 return this;
    5045         },
    5046 
    5047         refresh: function() {
    5048                 var state = this.controller.state(),
    5049                         library = state.get('library'),
    5050                         selection = state.get('selection');
    5051 
    5052                 _.each( this._views, function( button ) {
    5053                         if ( ! button.model || ! button.options || ! button.options.requires ) {
    5054                                 return;
    5055                         }
    5056 
    5057                         var requires = button.options.requires,
    5058                                 disabled = false;
    5059 
    5060                         // Prevent insertion of attachments if any of them are still uploading
    5061                         disabled = _.some( selection.models, function( attachment ) {
    5062                                 return attachment.get('uploading') === true;
    5063                         });
    5064 
    5065                         if ( requires.selection && selection && ! selection.length ) {
    5066                                 disabled = true;
    5067                         } else if ( requires.library && library && ! library.length ) {
    5068                                 disabled = true;
    5069                         }
    5070                         button.model.set( 'disabled', disabled );
     2338         * @param {Object} event
     2339         */
     2340        preventDefault: function( event ) {
     2341                event.preventDefault();
     2342        },
     2343        /**
     2344         * @param {Object} event
     2345         */
     2346        save: function( event ) {
     2347                var data = {};
     2348
     2349                if ( event ) {
     2350                        event.preventDefault();
     2351                }
     2352
     2353                _.each( this.$el.serializeArray(), function( pair ) {
     2354                        data[ pair.name ] = pair.value;
    50712355                });
     2356
     2357                this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
     2358                this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
     2359        },
     2360
     2361        postSave: function() {
     2362                this.controller.trigger( 'attachment:compat:ready', ['ready'] );
    50722363        }
    50732364});
    50742365
    5075 module.exports = Toolbar;
    5076 
    5077 
    5078 /***/ }),
    5079 /* 59 */
    5080 /***/ (function(module, exports) {
    5081 
    5082 /**
    5083  * wp.media.view.Toolbar.Select
    5084  *
    5085  * @class
    5086  * @augments wp.media.view.Toolbar
    5087  * @augments wp.media.View
    5088  * @augments wp.Backbone.View
    5089  * @augments Backbone.View
    5090  */
    5091 var Toolbar = wp.media.view.Toolbar,
    5092         l10n = wp.media.view.l10n,
    5093         Select;
    5094 
    5095 Select = Toolbar.extend({
    5096         initialize: function() {
    5097                 var options = this.options;
    5098 
    5099                 _.bindAll( this, 'clickSelect' );
    5100 
    5101                 _.defaults( options, {
    5102                         event: 'select',
    5103                         state: false,
    5104                         reset: true,
    5105                         close: true,
    5106                         text:  l10n.select,
    5107 
    5108                         // Does the button rely on the selection?
    5109                         requires: {
    5110                                 selection: true
    5111                         }
    5112                 });
    5113 
    5114                 options.items = _.defaults( options.items || {}, {
    5115                         select: {
    5116                                 style:    'primary',
    5117                                 text:     options.text,
    5118                                 priority: 80,
    5119                                 click:    this.clickSelect,
    5120                                 requires: options.requires
    5121                         }
    5122                 });
    5123                 // Call 'initialize' directly on the parent class.
    5124                 Toolbar.prototype.initialize.apply( this, arguments );
    5125         },
    5126 
    5127         clickSelect: function() {
    5128                 var options = this.options,
    5129                         controller = this.controller;
    5130 
    5131                 if ( options.close ) {
    5132                         controller.close();
    5133                 }
    5134 
    5135                 if ( options.event ) {
    5136                         controller.state().trigger( options.event );
    5137                 }
    5138 
    5139                 if ( options.state ) {
    5140                         controller.setState( options.state );
    5141                 }
    5142 
    5143                 if ( options.reset ) {
    5144                         controller.reset();
    5145                 }
    5146         }
    5147 });
    5148 
    5149 module.exports = Select;
    5150 
    5151 
    5152 /***/ }),
    5153 /* 60 */
    5154 /***/ (function(module, exports) {
    5155 
    5156 /**
    5157  * wp.media.view.Toolbar.Embed
    5158  *
    5159  * @class
    5160  * @augments wp.media.view.Toolbar.Select
    5161  * @augments wp.media.view.Toolbar
    5162  * @augments wp.media.View
    5163  * @augments wp.Backbone.View
    5164  * @augments Backbone.View
    5165  */
    5166 var Select = wp.media.view.Toolbar.Select,
    5167         l10n = wp.media.view.l10n,
    5168         Embed;
    5169 
    5170 Embed = Select.extend({
    5171         initialize: function() {
    5172                 _.defaults( this.options, {
    5173                         text: l10n.insertIntoPost,
    5174                         requires: false
    5175                 });
    5176                 // Call 'initialize' directly on the parent class.
    5177                 Select.prototype.initialize.apply( this, arguments );
    5178         },
    5179 
    5180         refresh: function() {
    5181                 var url = this.controller.state().props.get('url');
    5182                 this.get('select').model.set( 'disabled', ! url || url === 'http://' );
    5183                 /**
    5184                  * call 'refresh' directly on the parent class
    5185                  */
    5186                 Select.prototype.refresh.apply( this, arguments );
    5187         }
    5188 });
    5189 
    5190 module.exports = Embed;
    5191 
    5192 
    5193 /***/ }),
    5194 /* 61 */
    5195 /***/ (function(module, exports) {
    5196 
    5197 /**
    5198  * wp.media.view.Button
    5199  *
    5200  * @class
    5201  * @augments wp.media.View
    5202  * @augments wp.Backbone.View
    5203  * @augments Backbone.View
    5204  */
    5205 var Button = wp.media.View.extend({
    5206         tagName:    'button',
    5207         className:  'media-button',
    5208         attributes: { type: 'button' },
    5209 
    5210         events: {
    5211                 'click': 'click'
    5212         },
    5213 
    5214         defaults: {
    5215                 text:     '',
    5216                 style:    '',
    5217                 size:     'large',
    5218                 disabled: false
    5219         },
    5220 
    5221         initialize: function() {
    5222                 /**
    5223                  * Create a model with the provided `defaults`.
    5224                  *
    5225                  * @member {Backbone.Model}
    5226                  */
    5227                 this.model = new Backbone.Model( this.defaults );
    5228 
    5229                 // If any of the `options` have a key from `defaults`, apply its
    5230                 // value to the `model` and remove it from the `options object.
    5231                 _.each( this.defaults, function( def, key ) {
    5232                         var value = this.options[ key ];
    5233                         if ( _.isUndefined( value ) ) {
    5234                                 return;
    5235                         }
    5236 
    5237                         this.model.set( key, value );
    5238                         delete this.options[ key ];
    5239                 }, this );
    5240 
    5241                 this.listenTo( this.model, 'change', this.render );
    5242         },
    5243         /**
    5244          * @returns {wp.media.view.Button} Returns itself to allow chaining
    5245          */
    5246         render: function() {
    5247                 var classes = [ 'button', this.className ],
    5248                         model = this.model.toJSON();
    5249 
    5250                 if ( model.style ) {
    5251                         classes.push( 'button-' + model.style );
    5252                 }
    5253 
    5254                 if ( model.size ) {
    5255                         classes.push( 'button-' + model.size );
    5256                 }
    5257 
    5258                 classes = _.uniq( classes.concat( this.options.classes ) );
    5259                 this.el.className = classes.join(' ');
    5260 
    5261                 this.$el.attr( 'disabled', model.disabled );
    5262                 this.$el.text( this.model.get('text') );
    5263 
    5264                 return this;
    5265         },
    5266         /**
    5267          * @param {Object} event
    5268          */
    5269         click: function( event ) {
    5270                 if ( '#' === this.attributes.href ) {
    5271                         event.preventDefault();
    5272                 }
    5273 
    5274                 if ( this.options.click && ! this.model.get('disabled') ) {
    5275                         this.options.click.apply( this, arguments );
    5276                 }
    5277         }
    5278 });
    5279 
    5280 module.exports = Button;
    5281 
    5282 
    5283 /***/ }),
    5284 /* 62 */
    5285 /***/ (function(module, exports) {
    5286 
    5287 /**
    5288  * wp.media.view.ButtonGroup
    5289  *
    5290  * @class
    5291  * @augments wp.media.View
    5292  * @augments wp.Backbone.View
    5293  * @augments Backbone.View
    5294  */
    5295 var $ = Backbone.$,
    5296         ButtonGroup;
    5297 
    5298 ButtonGroup = wp.media.View.extend({
    5299         tagName:   'div',
    5300         className: 'button-group button-large media-button-group',
    5301 
    5302         initialize: function() {
    5303                 /**
    5304                  * @member {wp.media.view.Button[]}
    5305                  */
    5306                 this.buttons = _.map( this.options.buttons || [], function( button ) {
    5307                         if ( button instanceof Backbone.View ) {
    5308                                 return button;
    5309                         } else {
    5310                                 return new wp.media.view.Button( button ).render();
    5311                         }
    5312                 });
    5313 
    5314                 delete this.options.buttons;
    5315 
    5316                 if ( this.options.classes ) {
    5317                         this.$el.addClass( this.options.classes );
    5318                 }
    5319         },
    5320 
    5321         /**
    5322          * @returns {wp.media.view.ButtonGroup}
    5323          */
    5324         render: function() {
    5325                 this.$el.html( $( _.pluck( this.buttons, 'el' ) ).detach() );
    5326                 return this;
    5327         }
    5328 });
    5329 
    5330 module.exports = ButtonGroup;
    5331 
    5332 
    5333 /***/ }),
    5334 /* 63 */
    5335 /***/ (function(module, exports) {
    5336 
    5337 /**
    5338  * wp.media.view.PriorityList
    5339  *
    5340  * @class
    5341  * @augments wp.media.View
    5342  * @augments wp.Backbone.View
    5343  * @augments Backbone.View
    5344  */
    5345 var PriorityList = wp.media.View.extend({
    5346         tagName:   'div',
    5347 
    5348         initialize: function() {
    5349                 this._views = {};
    5350 
    5351                 this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
    5352                 delete this.options.views;
    5353 
    5354                 if ( ! this.options.silent ) {
    5355                         this.render();
    5356                 }
    5357         },
    5358         /**
    5359          * @param {string} id
    5360          * @param {wp.media.View|Object} view
    5361          * @param {Object} options
    5362          * @returns {wp.media.view.PriorityList} Returns itself to allow chaining
    5363          */
    5364         set: function( id, view, options ) {
    5365                 var priority, views, index;
    5366 
    5367                 options = options || {};
    5368 
    5369                 // Accept an object with an `id` : `view` mapping.
    5370                 if ( _.isObject( id ) ) {
    5371                         _.each( id, function( view, id ) {
    5372                                 this.set( id, view );
    5373                         }, this );
    5374                         return this;
    5375                 }
    5376 
    5377                 if ( ! (view instanceof Backbone.View) ) {
    5378                         view = this.toView( view, id, options );
    5379                 }
    5380                 view.controller = view.controller || this.controller;
    5381 
    5382                 this.unset( id );
    5383 
    5384                 priority = view.options.priority || 10;
    5385                 views = this.views.get() || [];
    5386 
    5387                 _.find( views, function( existing, i ) {
    5388                         if ( existing.options.priority > priority ) {
    5389                                 index = i;
    5390                                 return true;
    5391                         }
    5392                 });
    5393 
    5394                 this._views[ id ] = view;
    5395                 this.views.add( view, {
    5396                         at: _.isNumber( index ) ? index : views.length || 0
    5397                 });
    5398 
    5399                 return this;
    5400         },
    5401         /**
    5402          * @param {string} id
    5403          * @returns {wp.media.View}
    5404          */
    5405         get: function( id ) {
    5406                 return this._views[ id ];
    5407         },
    5408         /**
    5409          * @param {string} id
    5410          * @returns {wp.media.view.PriorityList}
    5411          */
    5412         unset: function( id ) {
    5413                 var view = this.get( id );
    5414 
    5415                 if ( view ) {
    5416                         view.remove();
    5417                 }
    5418 
    5419                 delete this._views[ id ];
    5420                 return this;
    5421         },
    5422         /**
    5423          * @param {Object} options
    5424          * @returns {wp.media.View}
    5425          */
    5426         toView: function( options ) {
    5427                 return new wp.media.View( options );
    5428         }
    5429 });
    5430 
    5431 module.exports = PriorityList;
    5432 
    5433 
    5434 /***/ }),
    5435 /* 64 */
    5436 /***/ (function(module, exports) {
    5437 
    5438 /**
    5439  * wp.media.view.MenuItem
    5440  *
    5441  * @class
    5442  * @augments wp.media.View
    5443  * @augments wp.Backbone.View
    5444  * @augments Backbone.View
    5445  */
    5446 var $ = jQuery,
    5447         MenuItem;
    5448 
    5449 MenuItem = wp.media.View.extend({
    5450         tagName:   'a',
    5451         className: 'media-menu-item',
    5452 
    5453         attributes: {
    5454                 href: '#'
    5455         },
    5456 
    5457         events: {
    5458                 'click': '_click'
    5459         },
    5460         /**
    5461          * @param {Object} event
    5462          */
    5463         _click: function( event ) {
    5464                 var clickOverride = this.options.click;
    5465 
    5466                 if ( event ) {
    5467                         event.preventDefault();
    5468                 }
    5469 
    5470                 if ( clickOverride ) {
    5471                         clickOverride.call( this );
    5472                 } else {
    5473                         this.click();
    5474                 }
    5475 
    5476                 // When selecting a tab along the left side,
    5477                 // focus should be transferred into the main panel
    5478                 if ( ! wp.media.isTouchDevice ) {
    5479                         $('.media-frame-content input').first().focus();
    5480                 }
    5481         },
    5482 
    5483         click: function() {
    5484                 var state = this.options.state;
    5485 
    5486                 if ( state ) {
    5487                         this.controller.setState( state );
    5488                         this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below
    5489                 }
    5490         },
    5491         /**
    5492          * @returns {wp.media.view.MenuItem} returns itself to allow chaining
    5493          */
    5494         render: function() {
    5495                 var options = this.options;
    5496 
    5497                 if ( options.text ) {
    5498                         this.$el.text( options.text );
    5499                 } else if ( options.html ) {
    5500                         this.$el.html( options.html );
    5501                 }
    5502 
    5503                 return this;
    5504         }
    5505 });
    5506 
    5507 module.exports = MenuItem;
    5508 
    5509 
    5510 /***/ }),
    5511 /* 65 */
    5512 /***/ (function(module, exports) {
    5513 
    5514 /**
    5515  * wp.media.view.Menu
    5516  *
    5517  * @class
    5518  * @augments wp.media.view.PriorityList
    5519  * @augments wp.media.View
    5520  * @augments wp.Backbone.View
    5521  * @augments Backbone.View
    5522  */
    5523 var MenuItem = wp.media.view.MenuItem,
    5524         PriorityList = wp.media.view.PriorityList,
    5525         Menu;
    5526 
    5527 Menu = PriorityList.extend({
    5528         tagName:   'div',
    5529         className: 'media-menu',
    5530         property:  'state',
    5531         ItemView:  MenuItem,
    5532         region:    'menu',
    5533 
    5534         /* TODO: alternatively hide on any click anywhere
    5535         events: {
    5536                 'click': 'click'
    5537         },
    5538 
    5539         click: function() {
    5540                 this.$el.removeClass( 'visible' );
    5541         },
    5542         */
    5543 
    5544         /**
    5545          * @param {Object} options
    5546          * @param {string} id
    5547          * @returns {wp.media.View}
    5548          */
    5549         toView: function( options, id ) {
    5550                 options = options || {};
    5551                 options[ this.property ] = options[ this.property ] || id;
    5552                 return new this.ItemView( options ).render();
    5553         },
    5554 
    5555         ready: function() {
    5556                 /**
    5557                  * call 'ready' directly on the parent class
    5558                  */
    5559                 PriorityList.prototype.ready.apply( this, arguments );
    5560                 this.visibility();
    5561         },
    5562 
    5563         set: function() {
    5564                 /**
    5565                  * call 'set' directly on the parent class
    5566                  */
    5567                 PriorityList.prototype.set.apply( this, arguments );
    5568                 this.visibility();
    5569         },
    5570 
    5571         unset: function() {
    5572                 /**
    5573                  * call 'unset' directly on the parent class
    5574                  */
    5575                 PriorityList.prototype.unset.apply( this, arguments );
    5576                 this.visibility();
    5577         },
    5578 
    5579         visibility: function() {
    5580                 var region = this.region,
    5581                         view = this.controller[ region ].get(),
    5582                         views = this.views.get(),
    5583                         hide = ! views || views.length < 2;
    5584 
    5585                 if ( this === view ) {
    5586                         this.controller.$el.toggleClass( 'hide-' + region, hide );
    5587                 }
    5588         },
    5589         /**
    5590          * @param {string} id
    5591          */
    5592         select: function( id ) {
    5593                 var view = this.get( id );
    5594 
    5595                 if ( ! view ) {
    5596                         return;
    5597                 }
    5598 
    5599                 this.deselect();
    5600                 view.$el.addClass('active');
    5601         },
    5602 
    5603         deselect: function() {
    5604                 this.$el.children().removeClass('active');
    5605         },
    5606 
    5607         hide: function( id ) {
    5608                 var view = this.get( id );
    5609 
    5610                 if ( ! view ) {
    5611                         return;
    5612                 }
    5613 
    5614                 view.$el.addClass('hidden');
    5615         },
    5616 
    5617         show: function( id ) {
    5618                 var view = this.get( id );
    5619 
    5620                 if ( ! view ) {
    5621                         return;
    5622                 }
    5623 
    5624                 view.$el.removeClass('hidden');
    5625         }
    5626 });
    5627 
    5628 module.exports = Menu;
    5629 
    5630 
    5631 /***/ }),
    5632 /* 66 */
    5633 /***/ (function(module, exports) {
    5634 
    5635 /**
    5636  * wp.media.view.RouterItem
    5637  *
    5638  * @class
    5639  * @augments wp.media.view.MenuItem
    5640  * @augments wp.media.View
    5641  * @augments wp.Backbone.View
    5642  * @augments Backbone.View
    5643  */
    5644 var RouterItem = wp.media.view.MenuItem.extend({
    5645         /**
    5646          * On click handler to activate the content region's corresponding mode.
    5647          */
    5648         click: function() {
    5649                 var contentMode = this.options.contentMode;
    5650                 if ( contentMode ) {
    5651                         this.controller.content.mode( contentMode );
    5652                 }
    5653         }
    5654 });
    5655 
    5656 module.exports = RouterItem;
    5657 
    5658 
    5659 /***/ }),
    5660 /* 67 */
    5661 /***/ (function(module, exports) {
    5662 
    5663 /**
    5664  * wp.media.view.Router
    5665  *
    5666  * @class
    5667  * @augments wp.media.view.Menu
    5668  * @augments wp.media.view.PriorityList
    5669  * @augments wp.media.View
    5670  * @augments wp.Backbone.View
    5671  * @augments Backbone.View
    5672  */
    5673 var Menu = wp.media.view.Menu,
    5674         Router;
    5675 
    5676 Router = Menu.extend({
    5677         tagName:   'div',
    5678         className: 'media-router',
    5679         property:  'contentMode',
    5680         ItemView:  wp.media.view.RouterItem,
    5681         region:    'router',
    5682 
    5683         initialize: function() {
    5684                 this.controller.on( 'content:render', this.update, this );
    5685                 // Call 'initialize' directly on the parent class.
    5686                 Menu.prototype.initialize.apply( this, arguments );
    5687         },
    5688 
    5689         update: function() {
    5690                 var mode = this.controller.content.mode();
    5691                 if ( mode ) {
    5692                         this.select( mode );
    5693                 }
    5694         }
    5695 });
    5696 
    5697 module.exports = Router;
    5698 
    5699 
    5700 /***/ }),
    5701 /* 68 */
    5702 /***/ (function(module, exports) {
    5703 
    5704 /**
    5705  * wp.media.view.Sidebar
    5706  *
    5707  * @class
    5708  * @augments wp.media.view.PriorityList
    5709  * @augments wp.media.View
    5710  * @augments wp.Backbone.View
    5711  * @augments Backbone.View
    5712  */
    5713 var Sidebar = wp.media.view.PriorityList.extend({
    5714         className: 'media-sidebar'
    5715 });
    5716 
    5717 module.exports = Sidebar;
    5718 
    5719 
    5720 /***/ }),
    5721 /* 69 */
    5722 /***/ (function(module, exports) {
    5723 
    5724 /**
    5725  * wp.media.view.Attachment
    5726  *
    5727  * @class
    5728  * @augments wp.media.View
    5729  * @augments wp.Backbone.View
    5730  * @augments Backbone.View
    5731  */
    5732 var View = wp.media.View,
    5733         $ = jQuery,
    5734         Attachment;
    5735 
    5736 Attachment = View.extend({
    5737         tagName:   'li',
    5738         className: 'attachment',
    5739         template:  wp.template('attachment'),
    5740 
    5741         attributes: function() {
    5742                 return {
    5743                         'tabIndex':     0,
    5744                         'role':         'checkbox',
    5745                         'aria-label':   this.model.get( 'title' ),
    5746                         'aria-checked': false,
    5747                         'data-id':      this.model.get( 'id' )
    5748                 };
    5749         },
    5750 
    5751         events: {
    5752                 'click .js--select-attachment':   'toggleSelectionHandler',
    5753                 'change [data-setting]':          'updateSetting',
    5754                 'change [data-setting] input':    'updateSetting',
    5755                 'change [data-setting] select':   'updateSetting',
    5756                 'change [data-setting] textarea': 'updateSetting',
    5757                 'click .attachment-close':        'removeFromLibrary',
    5758                 'click .check':                   'checkClickHandler',
    5759                 'keydown':                        'toggleSelectionHandler'
    5760         },
    5761 
    5762         buttons: {},
    5763 
    5764         initialize: function() {
    5765                 var selection = this.options.selection,
    5766                         options = _.defaults( this.options, {
    5767                                 rerenderOnModelChange: true
    5768                         } );
    5769 
    5770                 if ( options.rerenderOnModelChange ) {
    5771                         this.listenTo( this.model, 'change', this.render );
    5772                 } else {
    5773                         this.listenTo( this.model, 'change:percent', this.progress );
    5774                 }
    5775                 this.listenTo( this.model, 'change:title', this._syncTitle );
    5776                 this.listenTo( this.model, 'change:caption', this._syncCaption );
    5777                 this.listenTo( this.model, 'change:artist', this._syncArtist );
    5778                 this.listenTo( this.model, 'change:album', this._syncAlbum );
    5779 
    5780                 // Update the selection.
    5781                 this.listenTo( this.model, 'add', this.select );
    5782                 this.listenTo( this.model, 'remove', this.deselect );
    5783                 if ( selection ) {
    5784                         selection.on( 'reset', this.updateSelect, this );
    5785                         // Update the model's details view.
    5786                         this.listenTo( this.model, 'selection:single selection:unsingle', this.details );
    5787                         this.details( this.model, this.controller.state().get('selection') );
    5788                 }
    5789 
    5790                 this.listenTo( this.controller, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
    5791         },
    5792         /**
    5793          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    5794          */
    5795         dispose: function() {
    5796                 var selection = this.options.selection;
    5797 
    5798                 // Make sure all settings are saved before removing the view.
    5799                 this.updateAll();
    5800 
    5801                 if ( selection ) {
    5802                         selection.off( null, null, this );
    5803                 }
    5804                 /**
    5805                  * call 'dispose' directly on the parent class
    5806                  */
    5807                 View.prototype.dispose.apply( this, arguments );
    5808                 return this;
    5809         },
    5810         /**
    5811          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    5812          */
    5813         render: function() {
    5814                 var options = _.defaults( this.model.toJSON(), {
    5815                                 orientation:   'landscape',
    5816                                 uploading:     false,
    5817                                 type:          '',
    5818                                 subtype:       '',
    5819                                 icon:          '',
    5820                                 filename:      '',
    5821                                 caption:       '',
    5822                                 title:         '',
    5823                                 dateFormatted: '',
    5824                                 width:         '',
    5825                                 height:        '',
    5826                                 compat:        false,
    5827                                 alt:           '',
    5828                                 description:   ''
    5829                         }, this.options );
    5830 
    5831                 options.buttons  = this.buttons;
    5832                 options.describe = this.controller.state().get('describe');
    5833 
    5834                 if ( 'image' === options.type ) {
    5835                         options.size = this.imageSize();
    5836                 }
    5837 
    5838                 options.can = {};
    5839                 if ( options.nonces ) {
    5840                         options.can.remove = !! options.nonces['delete'];
    5841                         options.can.save = !! options.nonces.update;
    5842                 }
    5843 
    5844                 if ( this.controller.state().get('allowLocalEdits') ) {
    5845                         options.allowLocalEdits = true;
    5846                 }
    5847 
    5848                 if ( options.uploading && ! options.percent ) {
    5849                         options.percent = 0;
    5850                 }
    5851 
    5852                 this.views.detach();
    5853                 this.$el.html( this.template( options ) );
    5854 
    5855                 this.$el.toggleClass( 'uploading', options.uploading );
    5856 
    5857                 if ( options.uploading ) {
    5858                         this.$bar = this.$('.media-progress-bar div');
    5859                 } else {
    5860                         delete this.$bar;
    5861                 }
    5862 
    5863                 // Check if the model is selected.
    5864                 this.updateSelect();
    5865 
    5866                 // Update the save status.
    5867                 this.updateSave();
    5868 
    5869                 this.views.render();
    5870 
    5871                 return this;
    5872         },
    5873 
    5874         progress: function() {
    5875                 if ( this.$bar && this.$bar.length ) {
    5876                         this.$bar.width( this.model.get('percent') + '%' );
    5877                 }
    5878         },
    5879 
    5880         /**
    5881          * @param {Object} event
    5882          */
    5883         toggleSelectionHandler: function( event ) {
    5884                 var method;
    5885 
    5886                 // Don't do anything inside inputs and on the attachment check and remove buttons.
    5887                 if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName ) {
    5888                         return;
    5889                 }
    5890 
    5891                 // Catch arrow events
    5892                 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
    5893                         this.controller.trigger( 'attachment:keydown:arrow', event );
    5894                         return;
    5895                 }
    5896 
    5897                 // Catch enter and space events
    5898                 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
    5899                         return;
    5900                 }
    5901 
    5902                 event.preventDefault();
    5903 
    5904                 // In the grid view, bubble up an edit:attachment event to the controller.
    5905                 if ( this.controller.isModeActive( 'grid' ) ) {
    5906                         if ( this.controller.isModeActive( 'edit' ) ) {
    5907                                 // Pass the current target to restore focus when closing
    5908                                 this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
    5909                                 return;
    5910                         }
    5911 
    5912                         if ( this.controller.isModeActive( 'select' ) ) {
    5913                                 method = 'toggle';
    5914                         }
    5915                 }
    5916 
    5917                 if ( event.shiftKey ) {
    5918                         method = 'between';
    5919                 } else if ( event.ctrlKey || event.metaKey ) {
    5920                         method = 'toggle';
    5921                 }
    5922 
    5923                 this.toggleSelection({
    5924                         method: method
    5925                 });
    5926 
    5927                 this.controller.trigger( 'selection:toggle' );
    5928         },
    5929         /**
    5930          * @param {Object} options
    5931          */
    5932         toggleSelection: function( options ) {
    5933                 var collection = this.collection,
    5934                         selection = this.options.selection,
    5935                         model = this.model,
    5936                         method = options && options.method,
    5937                         single, models, singleIndex, modelIndex;
    5938 
    5939                 if ( ! selection ) {
    5940                         return;
    5941                 }
    5942 
    5943                 single = selection.single();
    5944                 method = _.isUndefined( method ) ? selection.multiple : method;
    5945 
    5946                 // If the `method` is set to `between`, select all models that
    5947                 // exist between the current and the selected model.
    5948                 if ( 'between' === method && single && selection.multiple ) {
    5949                         // If the models are the same, short-circuit.
    5950                         if ( single === model ) {
    5951                                 return;
    5952                         }
    5953 
    5954                         singleIndex = collection.indexOf( single );
    5955                         modelIndex  = collection.indexOf( this.model );
    5956 
    5957                         if ( singleIndex < modelIndex ) {
    5958                                 models = collection.models.slice( singleIndex, modelIndex + 1 );
    5959                         } else {
    5960                                 models = collection.models.slice( modelIndex, singleIndex + 1 );
    5961                         }
    5962 
    5963                         selection.add( models );
    5964                         selection.single( model );
    5965                         return;
    5966 
    5967                 // If the `method` is set to `toggle`, just flip the selection
    5968                 // status, regardless of whether the model is the single model.
    5969                 } else if ( 'toggle' === method ) {
    5970                         selection[ this.selected() ? 'remove' : 'add' ]( model );
    5971                         selection.single( model );
    5972                         return;
    5973                 } else if ( 'add' === method ) {
    5974                         selection.add( model );
    5975                         selection.single( model );
    5976                         return;
    5977                 }
    5978 
    5979                 // Fixes bug that loses focus when selecting a featured image
    5980                 if ( ! method ) {
    5981                         method = 'add';
    5982                 }
    5983 
    5984                 if ( method !== 'add' ) {
    5985                         method = 'reset';
    5986                 }
    5987 
    5988                 if ( this.selected() ) {
    5989                         // If the model is the single model, remove it.
    5990                         // If it is not the same as the single model,
    5991                         // it now becomes the single model.
    5992                         selection[ single === model ? 'remove' : 'single' ]( model );
    5993                 } else {
    5994                         // If the model is not selected, run the `method` on the
    5995                         // selection. By default, we `reset` the selection, but the
    5996                         // `method` can be set to `add` the model to the selection.
    5997                         selection[ method ]( model );
    5998                         selection.single( model );
    5999                 }
    6000         },
    6001 
    6002         updateSelect: function() {
    6003                 this[ this.selected() ? 'select' : 'deselect' ]();
    6004         },
    6005         /**
    6006          * @returns {unresolved|Boolean}
    6007          */
    6008         selected: function() {
    6009                 var selection = this.options.selection;
    6010                 if ( selection ) {
    6011                         return !! selection.get( this.model.cid );
    6012                 }
    6013         },
    6014         /**
    6015          * @param {Backbone.Model} model
    6016          * @param {Backbone.Collection} collection
    6017          */
    6018         select: function( model, collection ) {
    6019                 var selection = this.options.selection,
    6020                         controller = this.controller;
    6021 
    6022                 // Check if a selection exists and if it's the collection provided.
    6023                 // If they're not the same collection, bail; we're in another
    6024                 // selection's event loop.
    6025                 if ( ! selection || ( collection && collection !== selection ) ) {
    6026                         return;
    6027                 }
    6028 
    6029                 // Bail if the model is already selected.
    6030                 if ( this.$el.hasClass( 'selected' ) ) {
    6031                         return;
    6032                 }
    6033 
    6034                 // Add 'selected' class to model, set aria-checked to true.
    6035                 this.$el.addClass( 'selected' ).attr( 'aria-checked', true );
    6036                 //  Make the checkbox tabable, except in media grid (bulk select mode).
    6037                 if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) {
    6038                         this.$( '.check' ).attr( 'tabindex', '0' );
    6039                 }
    6040         },
    6041         /**
    6042          * @param {Backbone.Model} model
    6043          * @param {Backbone.Collection} collection
    6044          */
    6045         deselect: function( model, collection ) {
    6046                 var selection = this.options.selection;
    6047 
    6048                 // Check if a selection exists and if it's the collection provided.
    6049                 // If they're not the same collection, bail; we're in another
    6050                 // selection's event loop.
    6051                 if ( ! selection || ( collection && collection !== selection ) ) {
    6052                         return;
    6053                 }
    6054                 this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )
    6055                         .find( '.check' ).attr( 'tabindex', '-1' );
    6056         },
    6057         /**
    6058          * @param {Backbone.Model} model
    6059          * @param {Backbone.Collection} collection
    6060          */
    6061         details: function( model, collection ) {
    6062                 var selection = this.options.selection,
    6063                         details;
    6064 
    6065                 if ( selection !== collection ) {
    6066                         return;
    6067                 }
    6068 
    6069                 details = selection.single();
    6070                 this.$el.toggleClass( 'details', details === this.model );
    6071         },
    6072         /**
    6073          * @param {string} size
    6074          * @returns {Object}
    6075          */
    6076         imageSize: function( size ) {
    6077                 var sizes = this.model.get('sizes'), matched = false;
    6078 
    6079                 size = size || 'medium';
    6080 
    6081                 // Use the provided image size if possible.
    6082                 if ( sizes ) {
    6083                         if ( sizes[ size ] ) {
    6084                                 matched = sizes[ size ];
    6085                         } else if ( sizes.large ) {
    6086                                 matched = sizes.large;
    6087                         } else if ( sizes.thumbnail ) {
    6088                                 matched = sizes.thumbnail;
    6089                         } else if ( sizes.full ) {
    6090                                 matched = sizes.full;
    6091                         }
    6092 
    6093                         if ( matched ) {
    6094                                 return _.clone( matched );
    6095                         }
    6096                 }
    6097 
    6098                 return {
    6099                         url:         this.model.get('url'),
    6100                         width:       this.model.get('width'),
    6101                         height:      this.model.get('height'),
    6102                         orientation: this.model.get('orientation')
    6103                 };
    6104         },
    6105         /**
    6106          * @param {Object} event
    6107          */
    6108         updateSetting: function( event ) {
    6109                 var $setting = $( event.target ).closest('[data-setting]'),
    6110                         setting, value;
    6111 
    6112                 if ( ! $setting.length ) {
    6113                         return;
    6114                 }
    6115 
    6116                 setting = $setting.data('setting');
    6117                 value   = event.target.value;
    6118 
    6119                 if ( this.model.get( setting ) !== value ) {
    6120                         this.save( setting, value );
    6121                 }
    6122         },
    6123 
    6124         /**
    6125          * Pass all the arguments to the model's save method.
    6126          *
    6127          * Records the aggregate status of all save requests and updates the
    6128          * view's classes accordingly.
    6129          */
    6130         save: function() {
    6131                 var view = this,
    6132                         save = this._save = this._save || { status: 'ready' },
    6133                         request = this.model.save.apply( this.model, arguments ),
    6134                         requests = save.requests ? $.when( request, save.requests ) : request;
    6135 
    6136                 // If we're waiting to remove 'Saved.', stop.
    6137                 if ( save.savedTimer ) {
    6138                         clearTimeout( save.savedTimer );
    6139                 }
    6140 
    6141                 this.updateSave('waiting');
    6142                 save.requests = requests;
    6143                 requests.always( function() {
    6144                         // If we've performed another request since this one, bail.
    6145                         if ( save.requests !== requests ) {
    6146                                 return;
    6147                         }
    6148 
    6149                         view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' );
    6150                         save.savedTimer = setTimeout( function() {
    6151                                 view.updateSave('ready');
    6152                                 delete save.savedTimer;
    6153                         }, 2000 );
    6154                 });
    6155         },
    6156         /**
    6157          * @param {string} status
    6158          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    6159          */
    6160         updateSave: function( status ) {
    6161                 var save = this._save = this._save || { status: 'ready' };
    6162 
    6163                 if ( status && status !== save.status ) {
    6164                         this.$el.removeClass( 'save-' + save.status );
    6165                         save.status = status;
    6166                 }
    6167 
    6168                 this.$el.addClass( 'save-' + save.status );
    6169                 return this;
    6170         },
    6171 
    6172         updateAll: function() {
    6173                 var $settings = this.$('[data-setting]'),
    6174                         model = this.model,
    6175                         changed;
    6176 
    6177                 changed = _.chain( $settings ).map( function( el ) {
    6178                         var $input = $('input, textarea, select, [value]', el ),
    6179                                 setting, value;
    6180 
    6181                         if ( ! $input.length ) {
    6182                                 return;
    6183                         }
    6184 
    6185                         setting = $(el).data('setting');
    6186                         value = $input.val();
    6187 
    6188                         // Record the value if it changed.
    6189                         if ( model.get( setting ) !== value ) {
    6190                                 return [ setting, value ];
    6191                         }
    6192                 }).compact().object().value();
    6193 
    6194                 if ( ! _.isEmpty( changed ) ) {
    6195                         model.save( changed );
    6196                 }
    6197         },
    6198         /**
    6199          * @param {Object} event
    6200          */
    6201         removeFromLibrary: function( event ) {
    6202                 // Catch enter and space events
    6203                 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
    6204                         return;
    6205                 }
    6206 
    6207                 // Stop propagation so the model isn't selected.
    6208                 event.stopPropagation();
    6209 
    6210                 this.collection.remove( this.model );
    6211         },
    6212 
    6213         /**
    6214          * Add the model if it isn't in the selection, if it is in the selection,
    6215          * remove it.
    6216          *
    6217          * @param  {[type]} event [description]
    6218          * @return {[type]}       [description]
    6219          */
    6220         checkClickHandler: function ( event ) {
    6221                 var selection = this.options.selection;
    6222                 if ( ! selection ) {
    6223                         return;
    6224                 }
    6225                 event.stopPropagation();
    6226                 if ( selection.where( { id: this.model.get( 'id' ) } ).length ) {
    6227                         selection.remove( this.model );
    6228                         // Move focus back to the attachment tile (from the check).
    6229                         this.$el.focus();
    6230                 } else {
    6231                         selection.add( this.model );
    6232                 }
    6233         }
    6234 });
    6235 
    6236 // Ensure settings remain in sync between attachment views.
    6237 _.each({
    6238         caption: '_syncCaption',
    6239         title:   '_syncTitle',
    6240         artist:  '_syncArtist',
    6241         album:   '_syncAlbum'
    6242 }, function( method, setting ) {
    6243         /**
    6244          * @param {Backbone.Model} model
    6245          * @param {string} value
    6246          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    6247          */
    6248         Attachment.prototype[ method ] = function( model, value ) {
    6249                 var $setting = this.$('[data-setting="' + setting + '"]');
    6250 
    6251                 if ( ! $setting.length ) {
    6252                         return this;
    6253                 }
    6254 
    6255                 // If the updated value is in sync with the value in the DOM, there
    6256                 // is no need to re-render. If we're currently editing the value,
    6257                 // it will automatically be in sync, suppressing the re-render for
    6258                 // the view we're editing, while updating any others.
    6259                 if ( value === $setting.find('input, textarea, select, [value]').val() ) {
    6260                         return this;
    6261                 }
    6262 
    6263                 return this.render();
    6264         };
    6265 });
    6266 
    6267 module.exports = Attachment;
    6268 
    6269 
    6270 /***/ }),
    6271 /* 70 */
    6272 /***/ (function(module, exports) {
    6273 
    6274 /**
    6275  * wp.media.view.Attachment.Library
    6276  *
    6277  * @class
    6278  * @augments wp.media.view.Attachment
    6279  * @augments wp.media.View
    6280  * @augments wp.Backbone.View
    6281  * @augments Backbone.View
    6282  */
    6283 var Library = wp.media.view.Attachment.extend({
    6284         buttons: {
    6285                 check: true
    6286         }
    6287 });
    6288 
    6289 module.exports = Library;
    6290 
    6291 
    6292 /***/ }),
    6293 /* 71 */
    6294 /***/ (function(module, exports) {
    6295 
    6296 /**
    6297  * wp.media.view.Attachment.EditLibrary
    6298  *
    6299  * @class
    6300  * @augments wp.media.view.Attachment
    6301  * @augments wp.media.View
    6302  * @augments wp.Backbone.View
    6303  * @augments Backbone.View
    6304  */
    6305 var EditLibrary = wp.media.view.Attachment.extend({
    6306         buttons: {
    6307                 close: true
    6308         }
    6309 });
    6310 
    6311 module.exports = EditLibrary;
    6312 
    6313 
    6314 /***/ }),
    6315 /* 72 */
    6316 /***/ (function(module, exports) {
    6317 
    6318 /**
    6319  * wp.media.view.Attachments
    6320  *
    6321  * @class
    6322  * @augments wp.media.View
    6323  * @augments wp.Backbone.View
    6324  * @augments Backbone.View
    6325  */
    6326 var View = wp.media.View,
    6327         $ = jQuery,
    6328         Attachments;
    6329 
    6330 Attachments = View.extend({
    6331         tagName:   'ul',
    6332         className: 'attachments',
    6333 
    6334         attributes: {
    6335                 tabIndex: -1
    6336         },
    6337 
    6338         initialize: function() {
    6339                 this.el.id = _.uniqueId('__attachments-view-');
    6340 
    6341                 _.defaults( this.options, {
    6342                         refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
    6343                         refreshThreshold:   3,
    6344                         AttachmentView:     wp.media.view.Attachment,
    6345                         sortable:           false,
    6346                         resize:             true,
    6347                         idealColumnWidth:   $( window ).width() < 640 ? 135 : 150
    6348                 });
    6349 
    6350                 this._viewsByCid = {};
    6351                 this.$window = $( window );
    6352                 this.resizeEvent = 'resize.media-modal-columns';
    6353 
    6354                 this.collection.on( 'add', function( attachment ) {
    6355                         this.views.add( this.createAttachmentView( attachment ), {
    6356                                 at: this.collection.indexOf( attachment )
    6357                         });
    6358                 }, this );
    6359 
    6360                 this.collection.on( 'remove', function( attachment ) {
    6361                         var view = this._viewsByCid[ attachment.cid ];
    6362                         delete this._viewsByCid[ attachment.cid ];
    6363 
    6364                         if ( view ) {
    6365                                 view.remove();
    6366                         }
    6367                 }, this );
    6368 
    6369                 this.collection.on( 'reset', this.render, this );
    6370 
    6371                 this.listenTo( this.controller, 'library:selection:add',    this.attachmentFocus );
    6372 
    6373                 // Throttle the scroll handler and bind this.
    6374                 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
    6375 
    6376                 this.options.scrollElement = this.options.scrollElement || this.el;
    6377                 $( this.options.scrollElement ).on( 'scroll', this.scroll );
    6378 
    6379                 this.initSortable();
    6380 
    6381                 _.bindAll( this, 'setColumns' );
    6382 
    6383                 if ( this.options.resize ) {
    6384                         this.on( 'ready', this.bindEvents );
    6385                         this.controller.on( 'open', this.setColumns );
    6386 
    6387                         // Call this.setColumns() after this view has been rendered in the DOM so
    6388                         // attachments get proper width applied.
    6389                         _.defer( this.setColumns, this );
    6390                 }
    6391         },
    6392 
    6393         bindEvents: function() {
    6394                 this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) );
    6395         },
    6396 
    6397         attachmentFocus: function() {
    6398                 this.$( 'li:first' ).focus();
    6399         },
    6400 
    6401         restoreFocus: function() {
    6402                 this.$( 'li.selected:first' ).focus();
    6403         },
    6404 
    6405         arrowEvent: function( event ) {
    6406                 var attachments = this.$el.children( 'li' ),
    6407                         perRow = this.columns,
    6408                         index = attachments.filter( ':focus' ).index(),
    6409                         row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow );
    6410 
    6411                 if ( index === -1 ) {
    6412                         return;
    6413                 }
    6414 
    6415                 // Left arrow
    6416                 if ( 37 === event.keyCode ) {
    6417                         if ( 0 === index ) {
    6418                                 return;
    6419                         }
    6420                         attachments.eq( index - 1 ).focus();
    6421                 }
    6422 
    6423                 // Up arrow
    6424                 if ( 38 === event.keyCode ) {
    6425                         if ( 1 === row ) {
    6426                                 return;
    6427                         }
    6428                         attachments.eq( index - perRow ).focus();
    6429                 }
    6430 
    6431                 // Right arrow
    6432                 if ( 39 === event.keyCode ) {
    6433                         if ( attachments.length === index ) {
    6434                                 return;
    6435                         }
    6436                         attachments.eq( index + 1 ).focus();
    6437                 }
    6438 
    6439                 // Down arrow
    6440                 if ( 40 === event.keyCode ) {
    6441                         if ( Math.ceil( attachments.length / perRow ) === row ) {
    6442                                 return;
    6443                         }
    6444                         attachments.eq( index + perRow ).focus();
    6445                 }
    6446         },
    6447 
    6448         dispose: function() {
    6449                 this.collection.props.off( null, null, this );
    6450                 if ( this.options.resize ) {
    6451                         this.$window.off( this.resizeEvent );
    6452                 }
    6453 
    6454                 /**
    6455                  * call 'dispose' directly on the parent class
    6456                  */
    6457                 View.prototype.dispose.apply( this, arguments );
    6458         },
    6459 
    6460         setColumns: function() {
    6461                 var prev = this.columns,
    6462                         width = this.$el.width();
    6463 
    6464                 if ( width ) {
    6465                         this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1;
    6466 
    6467                         if ( ! prev || prev !== this.columns ) {
    6468                                 this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns );
    6469                         }
    6470                 }
    6471         },
    6472 
    6473         initSortable: function() {
    6474                 var collection = this.collection;
    6475 
    6476                 if ( wp.media.isTouchDevice || ! this.options.sortable || ! $.fn.sortable ) {
    6477                         return;
    6478                 }
    6479 
    6480                 this.$el.sortable( _.extend({
    6481                         // If the `collection` has a `comparator`, disable sorting.
    6482                         disabled: !! collection.comparator,
    6483 
    6484                         // Change the position of the attachment as soon as the
    6485                         // mouse pointer overlaps a thumbnail.
    6486                         tolerance: 'pointer',
    6487 
    6488                         // Record the initial `index` of the dragged model.
    6489                         start: function( event, ui ) {
    6490                                 ui.item.data('sortableIndexStart', ui.item.index());
    6491                         },
    6492 
    6493                         // Update the model's index in the collection.
    6494                         // Do so silently, as the view is already accurate.
    6495                         update: function( event, ui ) {
    6496                                 var model = collection.at( ui.item.data('sortableIndexStart') ),
    6497                                         comparator = collection.comparator;
    6498 
    6499                                 // Temporarily disable the comparator to prevent `add`
    6500                                 // from re-sorting.
    6501                                 delete collection.comparator;
    6502 
    6503                                 // Silently shift the model to its new index.
    6504                                 collection.remove( model, {
    6505                                         silent: true
    6506                                 });
    6507                                 collection.add( model, {
    6508                                         silent: true,
    6509                                         at:     ui.item.index()
    6510                                 });
    6511 
    6512                                 // Restore the comparator.
    6513                                 collection.comparator = comparator;
    6514 
    6515                                 // Fire the `reset` event to ensure other collections sync.
    6516                                 collection.trigger( 'reset', collection );
    6517 
    6518                                 // If the collection is sorted by menu order,
    6519                                 // update the menu order.
    6520                                 collection.saveMenuOrder();
    6521                         }
    6522                 }, this.options.sortable ) );
    6523 
    6524                 // If the `orderby` property is changed on the `collection`,
    6525                 // check to see if we have a `comparator`. If so, disable sorting.
    6526                 collection.props.on( 'change:orderby', function() {
    6527                         this.$el.sortable( 'option', 'disabled', !! collection.comparator );
    6528                 }, this );
    6529 
    6530                 this.collection.props.on( 'change:orderby', this.refreshSortable, this );
    6531                 this.refreshSortable();
    6532         },
    6533 
    6534         refreshSortable: function() {
    6535                 if ( wp.media.isTouchDevice || ! this.options.sortable || ! $.fn.sortable ) {
    6536                         return;
    6537                 }
    6538 
    6539                 // If the `collection` has a `comparator`, disable sorting.
    6540                 var collection = this.collection,
    6541                         orderby = collection.props.get('orderby'),
    6542                         enabled = 'menuOrder' === orderby || ! collection.comparator;
    6543 
    6544                 this.$el.sortable( 'option', 'disabled', ! enabled );
    6545         },
    6546 
    6547         /**
    6548          * @param {wp.media.model.Attachment} attachment
    6549          * @returns {wp.media.View}
    6550          */
    6551         createAttachmentView: function( attachment ) {
    6552                 var view = new this.options.AttachmentView({
    6553                         controller:           this.controller,
    6554                         model:                attachment,
    6555                         collection:           this.collection,
    6556                         selection:            this.options.selection
    6557                 });
    6558 
    6559                 return this._viewsByCid[ attachment.cid ] = view;
    6560         },
    6561 
    6562         prepare: function() {
    6563                 // Create all of the Attachment views, and replace
    6564                 // the list in a single DOM operation.
    6565                 if ( this.collection.length ) {
    6566                         this.views.set( this.collection.map( this.createAttachmentView, this ) );
    6567 
    6568                 // If there are no elements, clear the views and load some.
    6569                 } else {
    6570                         this.views.unset();
    6571                         this.collection.more().done( this.scroll );
    6572                 }
    6573         },
    6574 
    6575         ready: function() {
    6576                 // Trigger the scroll event to check if we're within the
    6577                 // threshold to query for additional attachments.
    6578                 this.scroll();
    6579         },
    6580 
    6581         scroll: function() {
    6582                 var view = this,
    6583                         el = this.options.scrollElement,
    6584                         scrollTop = el.scrollTop,
    6585                         toolbar;
    6586 
    6587                 // The scroll event occurs on the document, but the element
    6588                 // that should be checked is the document body.
    6589                 if ( el === document ) {
    6590                         el = document.body;
    6591                         scrollTop = $(document).scrollTop();
    6592                 }
    6593 
    6594                 if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) {
    6595                         return;
    6596                 }
    6597 
    6598                 toolbar = this.views.parent.toolbar;
    6599 
    6600                 // Show the spinner only if we are close to the bottom.
    6601                 if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) {
    6602                         toolbar.get('spinner').show();
    6603                 }
    6604 
    6605                 if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) {
    6606                         this.collection.more().done(function() {
    6607                                 view.scroll();
    6608                                 toolbar.get('spinner').hide();
    6609                         });
    6610                 }
    6611         }
    6612 });
    6613 
    6614 module.exports = Attachments;
    6615 
    6616 
    6617 /***/ }),
    6618 /* 73 */
    6619 /***/ (function(module, exports) {
    6620 
    6621 /**
    6622  * wp.media.view.Search
    6623  *
    6624  * @class
    6625  * @augments wp.media.View
    6626  * @augments wp.Backbone.View
    6627  * @augments Backbone.View
    6628  */
    6629 var l10n = wp.media.view.l10n,
    6630         Search;
    6631 
    6632 Search = wp.media.View.extend({
    6633         tagName:   'input',
    6634         className: 'search',
    6635         id:        'media-search-input',
    6636 
    6637         attributes: {
    6638                 type:        'search',
    6639                 placeholder: l10n.search
    6640         },
    6641 
    6642         events: {
    6643                 'input':  'search',
    6644                 'keyup':  'search',
    6645                 'change': 'search',
    6646                 'search': 'search'
    6647         },
    6648 
    6649         /**
    6650          * @returns {wp.media.view.Search} Returns itself to allow chaining
    6651          */
    6652         render: function() {
    6653                 this.el.value = this.model.escape('search');
    6654                 return this;
    6655         },
    6656 
    6657         search: function( event ) {
    6658                 if ( event.target.value ) {
    6659                         this.model.set( 'search', event.target.value );
    6660                 } else {
    6661                         this.model.unset('search');
    6662                 }
    6663         }
    6664 });
    6665 
    6666 module.exports = Search;
    6667 
    6668 
    6669 /***/ }),
    6670 /* 74 */
    6671 /***/ (function(module, exports) {
    6672 
     2366module.exports = AttachmentCompat;
     2367
     2368},{}],21:[function(require,module,exports){
    66732369/**
    66742370 * wp.media.view.AttachmentFilters
    … …  
    67472443module.exports = AttachmentFilters;
    67482444
    6749 
    6750 /***/ }),
    6751 /* 75 */
    6752 /***/ (function(module, exports) {
    6753 
     2445},{}],22:[function(require,module,exports){
     2446/**
     2447 * wp.media.view.AttachmentFilters.All
     2448 *
     2449 * @class
     2450 * @augments wp.media.view.AttachmentFilters
     2451 * @augments wp.media.View
     2452 * @augments wp.Backbone.View
     2453 * @augments Backbone.View
     2454 */
     2455var l10n = wp.media.view.l10n,
     2456        All;
     2457
     2458All = wp.media.view.AttachmentFilters.extend({
     2459        createFilters: function() {
     2460                var filters = {};
     2461
     2462                _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) {
     2463                        filters[ key ] = {
     2464                                text: text,
     2465                                props: {
     2466                                        status:  null,
     2467                                        type:    key,
     2468                                        uploadedTo: null,
     2469                                        orderby: 'date',
     2470                                        order:   'DESC'
     2471                                }
     2472                        };
     2473                });
     2474
     2475                filters.all = {
     2476                        text:  l10n.allMediaItems,
     2477                        props: {
     2478                                status:  null,
     2479                                type:    null,
     2480                                uploadedTo: null,
     2481                                orderby: 'date',
     2482                                order:   'DESC'
     2483                        },
     2484                        priority: 10
     2485                };
     2486
     2487                if ( wp.media.view.settings.post.id ) {
     2488                        filters.uploaded = {
     2489                                text:  l10n.uploadedToThisPost,
     2490                                props: {
     2491                                        status:  null,
     2492                                        type:    null,
     2493                                        uploadedTo: wp.media.view.settings.post.id,
     2494                                        orderby: 'menuOrder',
     2495                                        order:   'ASC'
     2496                                },
     2497                                priority: 20
     2498                        };
     2499                }
     2500
     2501                filters.unattached = {
     2502                        text:  l10n.unattached,
     2503                        props: {
     2504                                status:     null,
     2505                                uploadedTo: 0,
     2506                                type:       null,
     2507                                orderby:    'menuOrder',
     2508                                order:      'ASC'
     2509                        },
     2510                        priority: 50
     2511                };
     2512
     2513                if ( wp.media.view.settings.mediaTrash &&
     2514                        this.controller.isModeActive( 'grid' ) ) {
     2515
     2516                        filters.trash = {
     2517                                text:  l10n.trash,
     2518                                props: {
     2519                                        uploadedTo: null,
     2520                                        status:     'trash',
     2521                                        type:       null,
     2522                                        orderby:    'date',
     2523                                        order:      'DESC'
     2524                                },
     2525                                priority: 50
     2526                        };
     2527                }
     2528
     2529                this.filters = filters;
     2530        }
     2531});
     2532
     2533module.exports = All;
     2534
     2535},{}],23:[function(require,module,exports){
    67542536/**
    67552537 * A filter dropdown for month/dates.
    … …  
    67922574module.exports = DateFilter;
    67932575
    6794 
    6795 /***/ }),
    6796 /* 76 */
    6797 /***/ (function(module, exports) {
    6798 
     2576},{}],24:[function(require,module,exports){
    67992577/**
    68002578 * wp.media.view.AttachmentFilters.Uploaded
    … …  
    68552633module.exports = Uploaded;
    68562634
    6857 
    6858 /***/ }),
    6859 /* 77 */
    6860 /***/ (function(module, exports) {
    6861 
     2635},{}],25:[function(require,module,exports){
    68622636/**
    6863  * wp.media.view.AttachmentFilters.All
     2637 * wp.media.view.Attachment
    68642638 *
    68652639 * @class
    6866  * @augments wp.media.view.AttachmentFilters
    68672640 * @augments wp.media.View
    68682641 * @augments wp.Backbone.View
    68692642 * @augments Backbone.View
    68702643 */
    6871 var l10n = wp.media.view.l10n,
    6872         All;
    6873 
    6874 All = wp.media.view.AttachmentFilters.extend({
    6875         createFilters: function() {
    6876                 var filters = {};
    6877 
    6878                 _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) {
    6879                         filters[ key ] = {
    6880                                 text: text,
    6881                                 props: {
    6882                                         status:  null,
    6883                                         type:    key,
    6884                                         uploadedTo: null,
    6885                                         orderby: 'date',
    6886                                         order:   'DESC'
    6887                                 }
    6888                         };
     2644var View = wp.media.View,
     2645        $ = jQuery,
     2646        Attachment;
     2647
     2648Attachment = View.extend({
     2649        tagName:   'li',
     2650        className: 'attachment',
     2651        template:  wp.template('attachment'),
     2652
     2653        attributes: function() {
     2654                return {
     2655                        'tabIndex':     0,
     2656                        'role':         'checkbox',
     2657                        'aria-label':   this.model.get( 'title' ),
     2658                        'aria-checked': false,
     2659                        'data-id':      this.model.get( 'id' )
     2660                };
     2661        },
     2662
     2663        events: {
     2664                'click .js--select-attachment':   'toggleSelectionHandler',
     2665                'change [data-setting]':          'updateSetting',
     2666                'change [data-setting] input':    'updateSetting',
     2667                'change [data-setting] select':   'updateSetting',
     2668                'change [data-setting] textarea': 'updateSetting',
     2669                'click .attachment-close':        'removeFromLibrary',
     2670                'click .check':                   'checkClickHandler',
     2671                'keydown':                        'toggleSelectionHandler'
     2672        },
     2673
     2674        buttons: {},
     2675
     2676        initialize: function() {
     2677                var selection = this.options.selection,
     2678                        options = _.defaults( this.options, {
     2679                                rerenderOnModelChange: true
     2680                        } );
     2681
     2682                if ( options.rerenderOnModelChange ) {
     2683                        this.listenTo( this.model, 'change', this.render );
     2684                } else {
     2685                        this.listenTo( this.model, 'change:percent', this.progress );
     2686                }
     2687                this.listenTo( this.model, 'change:title', this._syncTitle );
     2688                this.listenTo( this.model, 'change:caption', this._syncCaption );
     2689                this.listenTo( this.model, 'change:artist', this._syncArtist );
     2690                this.listenTo( this.model, 'change:album', this._syncAlbum );
     2691
     2692                // Update the selection.
     2693                this.listenTo( this.model, 'add', this.select );
     2694                this.listenTo( this.model, 'remove', this.deselect );
     2695                if ( selection ) {
     2696                        selection.on( 'reset', this.updateSelect, this );
     2697                        // Update the model's details view.
     2698                        this.listenTo( this.model, 'selection:single selection:unsingle', this.details );
     2699                        this.details( this.model, this.controller.state().get('selection') );
     2700                }
     2701
     2702                this.listenTo( this.controller, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
     2703        },
     2704        /**
     2705         * @returns {wp.media.view.Attachment} Returns itself to allow chaining
     2706         */
     2707        dispose: function() {
     2708                var selection = this.options.selection;
     2709
     2710                // Make sure all settings are saved before removing the view.
     2711                this.updateAll();
     2712
     2713                if ( selection ) {
     2714                        selection.off( null, null, this );
     2715                }
     2716                /**
     2717                 * call 'dispose' directly on the parent class
     2718                 */
     2719                View.prototype.dispose.apply( this, arguments );
     2720                return this;
     2721        },
     2722        /**
     2723         * @returns {wp.media.view.Attachment} Returns itself to allow chaining
     2724         */
     2725        render: function() {
     2726                var options = _.defaults( this.model.toJSON(), {
     2727                                orientation:   'landscape',
     2728                                uploading:     false,
     2729                                type:          '',
     2730                                subtype:       '',
     2731                                icon:          '',
     2732                                filename:      '',
     2733                                caption:       '',
     2734                                title:         '',
     2735                                dateFormatted: '',
     2736                                width:         '',
     2737                                height:        '',
     2738                                compat:        false,
     2739                                alt:           '',
     2740                                description:   ''
     2741                        }, this.options );
     2742
     2743                options.buttons  = this.buttons;
     2744                options.describe = this.controller.state().get('describe');
     2745
     2746                if ( 'image' === options.type ) {
     2747                        options.size = this.imageSize();
     2748                }
     2749
     2750                options.can = {};
     2751                if ( options.nonces ) {
     2752                        options.can.remove = !! options.nonces['delete'];
     2753                        options.can.save = !! options.nonces.update;
     2754                }
     2755
     2756                if ( this.controller.state().get('allowLocalEdits') ) {
     2757                        options.allowLocalEdits = true;
     2758                }
     2759
     2760                if ( options.uploading && ! options.percent ) {
     2761                        options.percent = 0;
     2762                }
     2763
     2764                this.views.detach();
     2765                this.$el.html( this.template( options ) );
     2766
     2767                this.$el.toggleClass( 'uploading', options.uploading );
     2768
     2769                if ( options.uploading ) {
     2770                        this.$bar = this.$('.media-progress-bar div');
     2771                } else {
     2772                        delete this.$bar;
     2773                }
     2774
     2775                // Check if the model is selected.
     2776                this.updateSelect();
     2777
     2778                // Update the save status.
     2779                this.updateSave();
     2780
     2781                this.views.render();
     2782
     2783                return this;
     2784        },
     2785
     2786        progress: function() {
     2787                if ( this.$bar && this.$bar.length ) {
     2788                        this.$bar.width( this.model.get('percent') + '%' );
     2789                }
     2790        },
     2791
     2792        /**
     2793         * @param {Object} event
     2794         */
     2795        toggleSelectionHandler: function( event ) {
     2796                var method;
     2797
     2798                // Don't do anything inside inputs and on the attachment check and remove buttons.
     2799                if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName ) {
     2800                        return;
     2801                }
     2802
     2803                // Catch arrow events
     2804                if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
     2805                        this.controller.trigger( 'attachment:keydown:arrow', event );
     2806                        return;
     2807                }
     2808
     2809                // Catch enter and space events
     2810                if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
     2811                        return;
     2812                }
     2813
     2814                event.preventDefault();
     2815
     2816                // In the grid view, bubble up an edit:attachment event to the controller.
     2817                if ( this.controller.isModeActive( 'grid' ) ) {
     2818                        if ( this.controller.isModeActive( 'edit' ) ) {
     2819                                // Pass the current target to restore focus when closing
     2820                                this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
     2821                                return;
     2822                        }
     2823
     2824                        if ( this.controller.isModeActive( 'select' ) ) {
     2825                                method = 'toggle';
     2826                        }
     2827                }
     2828
     2829                if ( event.shiftKey ) {
     2830                        method = 'between';
     2831                } else if ( event.ctrlKey || event.metaKey ) {
     2832                        method = 'toggle';
     2833                }
     2834
     2835                this.toggleSelection({
     2836                        method: method
    68892837                });
    68902838
    6891                 filters.all = {
    6892                         text:  l10n.allMediaItems,
    6893                         props: {
    6894                                 status:  null,
    6895                                 type:    null,
    6896                                 uploadedTo: null,
    6897                                 orderby: 'date',
    6898                                 order:   'DESC'
    6899                         },
    6900                         priority: 10
     2839                this.controller.trigger( 'selection:toggle' );
     2840        },
     2841        /**
     2842         * @param {Object} options
     2843         */
     2844        toggleSelection: function( options ) {
     2845                var collection = this.collection,
     2846                        selection = this.options.selection,
     2847                        model = this.model,
     2848                        method = options && options.method,
     2849                        single, models, singleIndex, modelIndex;
     2850
     2851                if ( ! selection ) {
     2852                        return;
     2853                }
     2854
     2855                single = selection.single();
     2856                method = _.isUndefined( method ) ? selection.multiple : method;
     2857
     2858                // If the `method` is set to `between`, select all models that
     2859                // exist between the current and the selected model.
     2860                if ( 'between' === method && single && selection.multiple ) {
     2861                        // If the models are the same, short-circuit.
     2862                        if ( single === model ) {
     2863                                return;
     2864                        }
     2865
     2866                        singleIndex = collection.indexOf( single );
     2867                        modelIndex  = collection.indexOf( this.model );
     2868
     2869                        if ( singleIndex < modelIndex ) {
     2870                                models = collection.models.slice( singleIndex, modelIndex + 1 );
     2871                        } else {
     2872                                models = collection.models.slice( modelIndex, singleIndex + 1 );
     2873                        }
     2874
     2875                        selection.add( models );
     2876                        selection.single( model );
     2877                        return;
     2878
     2879                // If the `method` is set to `toggle`, just flip the selection
     2880                // status, regardless of whether the model is the single model.
     2881                } else if ( 'toggle' === method ) {
     2882                        selection[ this.selected() ? 'remove' : 'add' ]( model );
     2883                        selection.single( model );
     2884                        return;
     2885                } else if ( 'add' === method ) {
     2886                        selection.add( model );
     2887                        selection.single( model );
     2888                        return;
     2889                }
     2890
     2891                // Fixes bug that loses focus when selecting a featured image
     2892                if ( ! method ) {
     2893                        method = 'add';
     2894                }
     2895
     2896                if ( method !== 'add' ) {
     2897                        method = 'reset';
     2898                }
     2899
     2900                if ( this.selected() ) {
     2901                        // If the model is the single model, remove it.
     2902                        // If it is not the same as the single model,
     2903                        // it now becomes the single model.
     2904                        selection[ single === model ? 'remove' : 'single' ]( model );
     2905                } else {
     2906                        // If the model is not selected, run the `method` on the
     2907                        // selection. By default, we `reset` the selection, but the
     2908                        // `method` can be set to `add` the model to the selection.
     2909                        selection[ method ]( model );
     2910                        selection.single( model );
     2911                }
     2912        },
     2913
     2914        updateSelect: function() {
     2915                this[ this.selected() ? 'select' : 'deselect' ]();
     2916        },
     2917        /**
     2918         * @returns {unresolved|Boolean}
     2919         */
     2920        selected: function() {
     2921                var selection = this.options.selection;
     2922                if ( selection ) {
     2923                        return !! selection.get( this.model.cid );
     2924                }
     2925        },
     2926        /**
     2927         * @param {Backbone.Model} model
     2928         * @param {Backbone.Collection} collection
     2929         */
     2930        select: function( model, collection ) {
     2931                var selection = this.options.selection,
     2932                        controller = this.controller;
     2933
     2934                // Check if a selection exists and if it's the collection provided.
     2935                // If they're not the same collection, bail; we're in another
     2936                // selection's event loop.
     2937                if ( ! selection || ( collection && collection !== selection ) ) {
     2938                        return;
     2939                }
     2940
     2941                // Bail if the model is already selected.
     2942                if ( this.$el.hasClass( 'selected' ) ) {
     2943                        return;
     2944                }
     2945
     2946                // Add 'selected' class to model, set aria-checked to true.
     2947                this.$el.addClass( 'selected' ).attr( 'aria-checked', true );
     2948                //  Make the checkbox tabable, except in media grid (bulk select mode).
     2949                if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) {
     2950                        this.$( '.check' ).attr( 'tabindex', '0' );
     2951                }
     2952        },
     2953        /**
     2954         * @param {Backbone.Model} model
     2955         * @param {Backbone.Collection} collection
     2956         */
     2957        deselect: function( model, collection ) {
     2958                var selection = this.options.selection;
     2959
     2960                // Check if a selection exists and if it's the collection provided.
     2961                // If they're not the same collection, bail; we're in another
     2962                // selection's event loop.
     2963                if ( ! selection || ( collection && collection !== selection ) ) {
     2964                        return;
     2965                }
     2966                this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )
     2967                        .find( '.check' ).attr( 'tabindex', '-1' );
     2968        },
     2969        /**
     2970         * @param {Backbone.Model} model
     2971         * @param {Backbone.Collection} collection
     2972         */
     2973        details: function( model, collection ) {
     2974                var selection = this.options.selection,
     2975                        details;
     2976
     2977                if ( selection !== collection ) {
     2978                        return;
     2979                }
     2980
     2981                details = selection.single();
     2982                this.$el.toggleClass( 'details', details === this.model );
     2983        },
     2984        /**
     2985         * @param {string} size
     2986         * @returns {Object}
     2987         */
     2988        imageSize: function( size ) {
     2989                var sizes = this.model.get('sizes'), matched = false;
     2990
     2991                size = size || 'medium';
     2992
     2993                // Use the provided image size if possible.
     2994                if ( sizes ) {
     2995                        if ( sizes[ size ] ) {
     2996                                matched = sizes[ size ];
     2997                        } else if ( sizes.large ) {
     2998                                matched = sizes.large;
     2999                        } else if ( sizes.thumbnail ) {
     3000                                matched = sizes.thumbnail;
     3001                        } else if ( sizes.full ) {
     3002                                matched = sizes.full;
     3003                        }
     3004
     3005                        if ( matched ) {
     3006                                return _.clone( matched );
     3007                        }
     3008                }
     3009
     3010                return {
     3011                        url:         this.model.get('url'),
     3012                        width:       this.model.get('width'),
     3013                        height:      this.model.get('height'),
     3014                        orientation: this.model.get('orientation')
    69013015                };
    6902 
    6903                 if ( wp.media.view.settings.post.id ) {
    6904                         filters.uploaded = {
    6905                                 text:  l10n.uploadedToThisPost,
    6906                                 props: {
    6907                                         status:  null,
    6908                                         type:    null,
    6909                                         uploadedTo: wp.media.view.settings.post.id,
    6910                                         orderby: 'menuOrder',
    6911                                         order:   'ASC'
    6912                                 },
    6913                                 priority: 20
    6914                         };
    6915                 }
    6916 
    6917                 filters.unattached = {
    6918                         text:  l10n.unattached,
    6919                         props: {
    6920                                 status:     null,
    6921                                 uploadedTo: 0,
    6922                                 type:       null,
    6923                                 orderby:    'menuOrder',
    6924                                 order:      'ASC'
    6925                         },
    6926                         priority: 50
    6927                 };
    6928 
    6929                 if ( wp.media.view.settings.mediaTrash &&
    6930                         this.controller.isModeActive( 'grid' ) ) {
    6931 
    6932                         filters.trash = {
    6933                                 text:  l10n.trash,
    6934                                 props: {
    6935                                         uploadedTo: null,
    6936                                         status:     'trash',
    6937                                         type:       null,
    6938                                         orderby:    'date',
    6939                                         order:      'DESC'
    6940                                 },
    6941                                 priority: 50
    6942                         };
    6943                 }
    6944 
    6945                 this.filters = filters;
     3016        },
     3017        /**
     3018         * @param {Object} event
     3019         */
     3020        updateSetting: function( event ) {
     3021                var $setting = $( event.target ).closest('[data-setting]'),
     3022                        setting, value;
     3023
     3024                if ( ! $setting.length ) {
     3025                        return;
     3026                }
     3027
     3028                setting = $setting.data('setting');
     3029                value   = event.target.value;
     3030
     3031                if ( this.model.get( setting ) !== value ) {
     3032                        this.save( setting, value );
     3033                }
     3034        },
     3035
     3036        /**
     3037         * Pass all the arguments to the model's save method.
     3038         *
     3039         * Records the aggregate status of all save requests and updates the
     3040         * view's classes accordingly.
     3041         */
     3042        save: function() {
     3043                var view = this,
     3044                        save = this._save = this._save || { status: 'ready' },
     3045                        request = this.model.save.apply( this.model, arguments ),
     3046                        requests = save.requests ? $.when( request, save.requests ) : request;
     3047
     3048                // If we're waiting to remove 'Saved.', stop.
     3049                if ( save.savedTimer ) {
     3050                        clearTimeout( save.savedTimer );
     3051                }
     3052
     3053                this.updateSave('waiting');
     3054                save.requests = requests;
     3055                requests.always( function() {
     3056                        // If we've performed another request since this one, bail.
     3057                        if ( save.requests !== requests ) {
     3058                                return;
     3059                        }
     3060
     3061                        view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' );
     3062                        save.savedTimer = setTimeout( function() {
     3063                                view.updateSave('ready');
     3064                                delete save.savedTimer;
     3065                        }, 2000 );
     3066                });
     3067        },
     3068        /**
     3069         * @param {string} status
     3070         * @returns {wp.media.view.Attachment} Returns itself to allow chaining
     3071         */
     3072        updateSave: function( status ) {
     3073                var save = this._save = this._save || { status: 'ready' };
     3074
     3075                if ( status && status !== save.status ) {
     3076                        this.$el.removeClass( 'save-' + save.status );
     3077                        save.status = status;
     3078                }
     3079
     3080                this.$el.addClass( 'save-' + save.status );
     3081                return this;
     3082        },
     3083
     3084        updateAll: function() {
     3085                var $settings = this.$('[data-setting]'),
     3086                        model = this.model,
     3087                        changed;
     3088
     3089                changed = _.chain( $settings ).map( function( el ) {
     3090                        var $input = $('input, textarea, select, [value]', el ),
     3091                                setting, value;
     3092
     3093                        if ( ! $input.length ) {
     3094                                return;
     3095                        }
     3096
     3097                        setting = $(el).data('setting');
     3098                        value = $input.val();
     3099
     3100                        // Record the value if it changed.
     3101                        if ( model.get( setting ) !== value ) {
     3102                                return [ setting, value ];
     3103                        }
     3104                }).compact().object().value();
     3105
     3106                if ( ! _.isEmpty( changed ) ) {
     3107                        model.save( changed );
     3108                }
     3109        },
     3110        /**
     3111         * @param {Object} event
     3112         */
     3113        removeFromLibrary: function( event ) {
     3114                // Catch enter and space events
     3115                if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
     3116                        return;
     3117                }
     3118
     3119                // Stop propagation so the model isn't selected.
     3120                event.stopPropagation();
     3121
     3122                this.collection.remove( this.model );
     3123        },
     3124
     3125        /**
     3126         * Add the model if it isn't in the selection, if it is in the selection,
     3127         * remove it.
     3128         *
     3129         * @param  {[type]} event [description]
     3130         * @return {[type]}       [description]
     3131         */
     3132        checkClickHandler: function ( event ) {
     3133                var selection = this.options.selection;
     3134                if ( ! selection ) {
     3135                        return;
     3136                }
     3137                event.stopPropagation();
     3138                if ( selection.where( { id: this.model.get( 'id' ) } ).length ) {
     3139                        selection.remove( this.model );
     3140                        // Move focus back to the attachment tile (from the check).
     3141                        this.$el.focus();
     3142                } else {
     3143                        selection.add( this.model );
     3144                }
    69463145        }
    69473146});
    69483147
    6949 module.exports = All;
    6950 
    6951 
    6952 /***/ }),
    6953 /* 78 */
    6954 /***/ (function(module, exports) {
    6955 
     3148// Ensure settings remain in sync between attachment views.
     3149_.each({
     3150        caption: '_syncCaption',
     3151        title:   '_syncTitle',
     3152        artist:  '_syncArtist',
     3153        album:   '_syncAlbum'
     3154}, function( method, setting ) {
     3155        /**
     3156         * @param {Backbone.Model} model
     3157         * @param {string} value
     3158         * @returns {wp.media.view.Attachment} Returns itself to allow chaining
     3159         */
     3160        Attachment.prototype[ method ] = function( model, value ) {
     3161                var $setting = this.$('[data-setting="' + setting + '"]');
     3162
     3163                if ( ! $setting.length ) {
     3164                        return this;
     3165                }
     3166
     3167                // If the updated value is in sync with the value in the DOM, there
     3168                // is no need to re-render. If we're currently editing the value,
     3169                // it will automatically be in sync, suppressing the re-render for
     3170                // the view we're editing, while updating any others.
     3171                if ( value === $setting.find('input, textarea, select, [value]').val() ) {
     3172                        return this;
     3173                }
     3174
     3175                return this.render();
     3176        };
     3177});
     3178
     3179module.exports = Attachment;
     3180
     3181},{}],26:[function(require,module,exports){
     3182/**
     3183 * wp.media.view.Attachment.Details
     3184 *
     3185 * @class
     3186 * @augments wp.media.view.Attachment
     3187 * @augments wp.media.View
     3188 * @augments wp.Backbone.View
     3189 * @augments Backbone.View
     3190 */
     3191var Attachment = wp.media.view.Attachment,
     3192        l10n = wp.media.view.l10n,
     3193        Details;
     3194
     3195Details = Attachment.extend({
     3196        tagName:   'div',
     3197        className: 'attachment-details',
     3198        template:  wp.template('attachment-details'),
     3199
     3200        attributes: function() {
     3201                return {
     3202                        'tabIndex':     0,
     3203                        'data-id':      this.model.get( 'id' )
     3204                };
     3205        },
     3206
     3207        events: {
     3208                'change [data-setting]':          'updateSetting',
     3209                'change [data-setting] input':    'updateSetting',
     3210                'change [data-setting] select':   'updateSetting',
     3211                'change [data-setting] textarea': 'updateSetting',
     3212                'click .delete-attachment':       'deleteAttachment',
     3213                'click .trash-attachment':        'trashAttachment',
     3214                'click .untrash-attachment':      'untrashAttachment',
     3215                'click .edit-attachment':         'editAttachment',
     3216                'keydown':                        'toggleSelectionHandler'
     3217        },
     3218
     3219        initialize: function() {
     3220                this.options = _.defaults( this.options, {
     3221                        rerenderOnModelChange: false
     3222                });
     3223
     3224                this.on( 'ready', this.initialFocus );
     3225                // Call 'initialize' directly on the parent class.
     3226                Attachment.prototype.initialize.apply( this, arguments );
     3227        },
     3228
     3229        initialFocus: function() {
     3230                if ( ! wp.media.isTouchDevice ) {
     3231                        /*
     3232                        Previously focused the first ':input' (the readonly URL text field).
     3233                        Since the first ':input' is now a button (delete/trash): when pressing
     3234                        spacebar on an attachment, Firefox fires deleteAttachment/trashAttachment
     3235                        as soon as focus is moved. Explicitly target the first text field for now.
     3236                        @todo change initial focus logic, also for accessibility.
     3237                        */
     3238                        this.$( 'input[type="text"]' ).eq( 0 ).focus();
     3239                }
     3240        },
     3241        /**
     3242         * @param {Object} event
     3243         */
     3244        deleteAttachment: function( event ) {
     3245                event.preventDefault();
     3246
     3247                if ( window.confirm( l10n.warnDelete ) ) {
     3248                        this.model.destroy();
     3249                        // Keep focus inside media modal
     3250                        // after image is deleted
     3251                        this.controller.modal.focusManager.focus();
     3252                }
     3253        },
     3254        /**
     3255         * @param {Object} event
     3256         */
     3257        trashAttachment: function( event ) {
     3258                var library = this.controller.library;
     3259                event.preventDefault();
     3260
     3261                if ( wp.media.view.settings.mediaTrash &&
     3262                        'edit-metadata' === this.controller.content.mode() ) {
     3263
     3264                        this.model.set( 'status', 'trash' );
     3265                        this.model.save().done( function() {
     3266                                library._requery( true );
     3267                        } );
     3268                }  else {
     3269                        this.model.destroy();
     3270                }
     3271        },
     3272        /**
     3273         * @param {Object} event
     3274         */
     3275        untrashAttachment: function( event ) {
     3276                var library = this.controller.library;
     3277                event.preventDefault();
     3278
     3279                this.model.set( 'status', 'inherit' );
     3280                this.model.save().done( function() {
     3281                        library._requery( true );
     3282                } );
     3283        },
     3284        /**
     3285         * @param {Object} event
     3286         */
     3287        editAttachment: function( event ) {
     3288                var editState = this.controller.states.get( 'edit-image' );
     3289                if ( window.imageEdit && editState ) {
     3290                        event.preventDefault();
     3291
     3292                        editState.set( 'image', this.model );
     3293                        this.controller.setState( 'edit-image' );
     3294                } else {
     3295                        this.$el.addClass('needs-refresh');
     3296                }
     3297        },
     3298        /**
     3299         * When reverse tabbing(shift+tab) out of the right details panel, deliver
     3300         * the focus to the item in the list that was being edited.
     3301         *
     3302         * @param {Object} event
     3303         */
     3304        toggleSelectionHandler: function( event ) {
     3305                if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) {
     3306                        this.controller.trigger( 'attachment:details:shift-tab', event );
     3307                        return false;
     3308                }
     3309
     3310                if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
     3311                        this.controller.trigger( 'attachment:keydown:arrow', event );
     3312                        return;
     3313                }
     3314        }
     3315});
     3316
     3317module.exports = Details;
     3318
     3319},{}],27:[function(require,module,exports){
     3320/**
     3321 * wp.media.view.Attachment.EditLibrary
     3322 *
     3323 * @class
     3324 * @augments wp.media.view.Attachment
     3325 * @augments wp.media.View
     3326 * @augments wp.Backbone.View
     3327 * @augments Backbone.View
     3328 */
     3329var EditLibrary = wp.media.view.Attachment.extend({
     3330        buttons: {
     3331                close: true
     3332        }
     3333});
     3334
     3335module.exports = EditLibrary;
     3336
     3337},{}],28:[function(require,module,exports){
     3338/**
     3339 * wp.media.view.Attachments.EditSelection
     3340 *
     3341 * @class
     3342 * @augments wp.media.view.Attachment.Selection
     3343 * @augments wp.media.view.Attachment
     3344 * @augments wp.media.View
     3345 * @augments wp.Backbone.View
     3346 * @augments Backbone.View
     3347 */
     3348var EditSelection = wp.media.view.Attachment.Selection.extend({
     3349        buttons: {
     3350                close: true
     3351        }
     3352});
     3353
     3354module.exports = EditSelection;
     3355
     3356},{}],29:[function(require,module,exports){
     3357/**
     3358 * wp.media.view.Attachment.Library
     3359 *
     3360 * @class
     3361 * @augments wp.media.view.Attachment
     3362 * @augments wp.media.View
     3363 * @augments wp.Backbone.View
     3364 * @augments Backbone.View
     3365 */
     3366var Library = wp.media.view.Attachment.extend({
     3367        buttons: {
     3368                check: true
     3369        }
     3370});
     3371
     3372module.exports = Library;
     3373
     3374},{}],30:[function(require,module,exports){
     3375/**
     3376 * wp.media.view.Attachment.Selection
     3377 *
     3378 * @class
     3379 * @augments wp.media.view.Attachment
     3380 * @augments wp.media.View
     3381 * @augments wp.Backbone.View
     3382 * @augments Backbone.View
     3383 */
     3384var Selection = wp.media.view.Attachment.extend({
     3385        className: 'attachment selection',
     3386
     3387        // On click, just select the model, instead of removing the model from
     3388        // the selection.
     3389        toggleSelection: function() {
     3390                this.options.selection.single( this.model );
     3391        }
     3392});
     3393
     3394module.exports = Selection;
     3395
     3396},{}],31:[function(require,module,exports){
     3397/**
     3398 * wp.media.view.Attachments
     3399 *
     3400 * @class
     3401 * @augments wp.media.View
     3402 * @augments wp.Backbone.View
     3403 * @augments Backbone.View
     3404 */
     3405var View = wp.media.View,
     3406        $ = jQuery,
     3407        Attachments;
     3408
     3409Attachments = View.extend({
     3410        tagName:   'ul',
     3411        className: 'attachments',
     3412
     3413        attributes: {
     3414                tabIndex: -1
     3415        },
     3416
     3417        initialize: function() {
     3418                this.el.id = _.uniqueId('__attachments-view-');
     3419
     3420                _.defaults( this.options, {
     3421                        refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
     3422                        refreshThreshold:   3,
     3423                        AttachmentView:     wp.media.view.Attachment,
     3424                        sortable:           false,
     3425                        resize:             true,
     3426                        idealColumnWidth:   $( window ).width() < 640 ? 135 : 150
     3427                });
     3428
     3429                this._viewsByCid = {};
     3430                this.$window = $( window );
     3431                this.resizeEvent = 'resize.media-modal-columns';
     3432
     3433                this.collection.on( 'add', function( attachment ) {
     3434                        this.views.add( this.createAttachmentView( attachment ), {
     3435                                at: this.collection.indexOf( attachment )
     3436                        });
     3437                }, this );
     3438
     3439                this.collection.on( 'remove', function( attachment ) {
     3440                        var view = this._viewsByCid[ attachment.cid ];
     3441                        delete this._viewsByCid[ attachment.cid ];
     3442
     3443                        if ( view ) {
     3444                                view.remove();
     3445                        }
     3446                }, this );
     3447
     3448                this.collection.on( 'reset', this.render, this );
     3449
     3450                this.listenTo( this.controller, 'library:selection:add',    this.attachmentFocus );
     3451
     3452                // Throttle the scroll handler and bind this.
     3453                this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
     3454
     3455                this.options.scrollElement = this.options.scrollElement || this.el;
     3456                $( this.options.scrollElement ).on( 'scroll', this.scroll );
     3457
     3458                this.initSortable();
     3459
     3460                _.bindAll( this, 'setColumns' );
     3461
     3462                if ( this.options.resize ) {
     3463                        this.on( 'ready', this.bindEvents );
     3464                        this.controller.on( 'open', this.setColumns );
     3465
     3466                        // Call this.setColumns() after this view has been rendered in the DOM so
     3467                        // attachments get proper width applied.
     3468                        _.defer( this.setColumns, this );
     3469                }
     3470        },
     3471
     3472        bindEvents: function() {
     3473                this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) );
     3474        },
     3475
     3476        attachmentFocus: function() {
     3477                this.$( 'li:first' ).focus();
     3478        },
     3479
     3480        restoreFocus: function() {
     3481                this.$( 'li.selected:first' ).focus();
     3482        },
     3483
     3484        arrowEvent: function( event ) {
     3485                var attachments = this.$el.children( 'li' ),
     3486                        perRow = this.columns,
     3487                        index = attachments.filter( ':focus' ).index(),
     3488                        row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow );
     3489
     3490                if ( index === -1 ) {
     3491                        return;
     3492                }
     3493
     3494                // Left arrow
     3495                if ( 37 === event.keyCode ) {
     3496                        if ( 0 === index ) {
     3497                                return;
     3498                        }
     3499                        attachments.eq( index - 1 ).focus();
     3500                }
     3501
     3502                // Up arrow
     3503                if ( 38 === event.keyCode ) {
     3504                        if ( 1 === row ) {
     3505                                return;
     3506                        }
     3507                        attachments.eq( index - perRow ).focus();
     3508                }
     3509
     3510                // Right arrow
     3511                if ( 39 === event.keyCode ) {
     3512                        if ( attachments.length === index ) {
     3513                                return;
     3514                        }
     3515                        attachments.eq( index + 1 ).focus();
     3516                }
     3517
     3518                // Down arrow
     3519                if ( 40 === event.keyCode ) {
     3520                        if ( Math.ceil( attachments.length / perRow ) === row ) {
     3521                                return;
     3522                        }
     3523                        attachments.eq( index + perRow ).focus();
     3524                }
     3525        },
     3526
     3527        dispose: function() {
     3528                this.collection.props.off( null, null, this );
     3529                if ( this.options.resize ) {
     3530                        this.$window.off( this.resizeEvent );
     3531                }
     3532
     3533                /**
     3534                 * call 'dispose' directly on the parent class
     3535                 */
     3536                View.prototype.dispose.apply( this, arguments );
     3537        },
     3538
     3539        setColumns: function() {
     3540                var prev = this.columns,
     3541                        width = this.$el.width();
     3542
     3543                if ( width ) {
     3544                        this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1;
     3545
     3546                        if ( ! prev || prev !== this.columns ) {
     3547                                this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns );
     3548                        }
     3549                }
     3550        },
     3551
     3552        initSortable: function() {
     3553                var collection = this.collection;
     3554
     3555                if ( wp.media.isTouchDevice || ! this.options.sortable || ! $.fn.sortable ) {
     3556                        return;
     3557                }
     3558
     3559                this.$el.sortable( _.extend({
     3560                        // If the `collection` has a `comparator`, disable sorting.
     3561                        disabled: !! collection.comparator,
     3562
     3563                        // Change the position of the attachment as soon as the
     3564                        // mouse pointer overlaps a thumbnail.
     3565                        tolerance: 'pointer',
     3566
     3567                        // Record the initial `index` of the dragged model.
     3568                        start: function( event, ui ) {
     3569                                ui.item.data('sortableIndexStart', ui.item.index());
     3570                        },
     3571
     3572                        // Update the model's index in the collection.
     3573                        // Do so silently, as the view is already accurate.
     3574                        update: function( event, ui ) {
     3575                                var model = collection.at( ui.item.data('sortableIndexStart') ),
     3576                                        comparator = collection.comparator;
     3577
     3578                                // Temporarily disable the comparator to prevent `add`
     3579                                // from re-sorting.
     3580                                delete collection.comparator;
     3581
     3582                                // Silently shift the model to its new index.
     3583                                collection.remove( model, {
     3584                                        silent: true
     3585                                });
     3586                                collection.add( model, {
     3587                                        silent: true,
     3588                                        at:     ui.item.index()
     3589                                });
     3590
     3591                                // Restore the comparator.
     3592                                collection.comparator = comparator;
     3593
     3594                                // Fire the `reset` event to ensure other collections sync.
     3595                                collection.trigger( 'reset', collection );
     3596
     3597                                // If the collection is sorted by menu order,
     3598                                // update the menu order.
     3599                                collection.saveMenuOrder();
     3600                        }
     3601                }, this.options.sortable ) );
     3602
     3603                // If the `orderby` property is changed on the `collection`,
     3604                // check to see if we have a `comparator`. If so, disable sorting.
     3605                collection.props.on( 'change:orderby', function() {
     3606                        this.$el.sortable( 'option', 'disabled', !! collection.comparator );
     3607                }, this );
     3608
     3609                this.collection.props.on( 'change:orderby', this.refreshSortable, this );
     3610                this.refreshSortable();
     3611        },
     3612
     3613        refreshSortable: function() {
     3614                if ( wp.media.isTouchDevice || ! this.options.sortable || ! $.fn.sortable ) {
     3615                        return;
     3616                }
     3617
     3618                // If the `collection` has a `comparator`, disable sorting.
     3619                var collection = this.collection,
     3620                        orderby = collection.props.get('orderby'),
     3621                        enabled = 'menuOrder' === orderby || ! collection.comparator;
     3622
     3623                this.$el.sortable( 'option', 'disabled', ! enabled );
     3624        },
     3625
     3626        /**
     3627         * @param {wp.media.model.Attachment} attachment
     3628         * @returns {wp.media.View}
     3629         */
     3630        createAttachmentView: function( attachment ) {
     3631                var view = new this.options.AttachmentView({
     3632                        controller:           this.controller,
     3633                        model:                attachment,
     3634                        collection:           this.collection,
     3635                        selection:            this.options.selection
     3636                });
     3637
     3638                return this._viewsByCid[ attachment.cid ] = view;
     3639        },
     3640
     3641        prepare: function() {
     3642                // Create all of the Attachment views, and replace
     3643                // the list in a single DOM operation.
     3644                if ( this.collection.length ) {
     3645                        this.views.set( this.collection.map( this.createAttachmentView, this ) );
     3646
     3647                // If there are no elements, clear the views and load some.
     3648                } else {
     3649                        this.views.unset();
     3650                        this.collection.more().done( this.scroll );
     3651                }
     3652        },
     3653
     3654        ready: function() {
     3655                // Trigger the scroll event to check if we're within the
     3656                // threshold to query for additional attachments.
     3657                this.scroll();
     3658        },
     3659
     3660        scroll: function() {
     3661                var view = this,
     3662                        el = this.options.scrollElement,
     3663                        scrollTop = el.scrollTop,
     3664                        toolbar;
     3665
     3666                // The scroll event occurs on the document, but the element
     3667                // that should be checked is the document body.
     3668                if ( el === document ) {
     3669                        el = document.body;
     3670                        scrollTop = $(document).scrollTop();
     3671                }
     3672
     3673                if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) {
     3674                        return;
     3675                }
     3676
     3677                toolbar = this.views.parent.toolbar;
     3678
     3679                // Show the spinner only if we are close to the bottom.
     3680                if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) {
     3681                        toolbar.get('spinner').show();
     3682                }
     3683
     3684                if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) {
     3685                        this.collection.more().done(function() {
     3686                                view.scroll();
     3687                                toolbar.get('spinner').hide();
     3688                        });
     3689                }
     3690        }
     3691});
     3692
     3693module.exports = Attachments;
     3694
     3695},{}],32:[function(require,module,exports){
    69563696/**
    69573697 * wp.media.view.AttachmentsBrowser
    … …  
    73974137module.exports = AttachmentsBrowser;
    73984138
    7399 
    7400 /***/ }),
    7401 /* 79 */
    7402 /***/ (function(module, exports) {
    7403 
    7404 /**
    7405  * wp.media.view.Selection
    7406  *
    7407  * @class
    7408  * @augments wp.media.View
    7409  * @augments wp.Backbone.View
    7410  * @augments Backbone.View
    7411  */
    7412 var l10n = wp.media.view.l10n,
    7413         Selection;
    7414 
    7415 Selection = wp.media.View.extend({
    7416         tagName:   'div',
    7417         className: 'media-selection',
    7418         template:  wp.template('media-selection'),
    7419 
    7420         events: {
    7421                 'click .edit-selection':  'edit',
    7422                 'click .clear-selection': 'clear'
    7423         },
    7424 
    7425         initialize: function() {
    7426                 _.defaults( this.options, {
    7427                         editable:  false,
    7428                         clearable: true
    7429                 });
    7430 
    7431                 /**
    7432                  * @member {wp.media.view.Attachments.Selection}
    7433                  */
    7434                 this.attachments = new wp.media.view.Attachments.Selection({
    7435                         controller: this.controller,
    7436                         collection: this.collection,
    7437                         selection:  this.collection,
    7438                         model:      new Backbone.Model()
    7439                 });
    7440 
    7441                 this.views.set( '.selection-view', this.attachments );
    7442                 this.collection.on( 'add remove reset', this.refresh, this );
    7443                 this.controller.on( 'content:activate', this.refresh, this );
    7444         },
    7445 
    7446         ready: function() {
    7447                 this.refresh();
    7448         },
    7449 
    7450         refresh: function() {
    7451                 // If the selection hasn't been rendered, bail.
    7452                 if ( ! this.$el.children().length ) {
    7453                         return;
    7454                 }
    7455 
    7456                 var collection = this.collection,
    7457                         editing = 'edit-selection' === this.controller.content.mode();
    7458 
    7459                 // If nothing is selected, display nothing.
    7460                 this.$el.toggleClass( 'empty', ! collection.length );
    7461                 this.$el.toggleClass( 'one', 1 === collection.length );
    7462                 this.$el.toggleClass( 'editing', editing );
    7463 
    7464                 this.$('.count').text( l10n.selected.replace('%d', collection.length) );
    7465         },
    7466 
    7467         edit: function( event ) {
    7468                 event.preventDefault();
    7469                 if ( this.options.editable ) {
    7470                         this.options.editable.call( this, this.collection );
    7471                 }
    7472         },
    7473 
    7474         clear: function( event ) {
    7475                 event.preventDefault();
    7476                 this.collection.reset();
    7477 
    7478                 // Keep focus inside media modal
    7479                 // after clear link is selected
    7480                 this.controller.modal.focusManager.focus();
    7481         }
    7482 });
    7483 
    7484 module.exports = Selection;
    7485 
    7486 
    7487 /***/ }),
    7488 /* 80 */
    7489 /***/ (function(module, exports) {
    7490 
    7491 /**
    7492  * wp.media.view.Attachment.Selection
    7493  *
    7494  * @class
    7495  * @augments wp.media.view.Attachment
    7496  * @augments wp.media.View
    7497  * @augments wp.Backbone.View
    7498  * @augments Backbone.View
    7499  */
    7500 var Selection = wp.media.view.Attachment.extend({
    7501         className: 'attachment selection',
    7502 
    7503         // On click, just select the model, instead of removing the model from
    7504         // the selection.
    7505         toggleSelection: function() {
    7506                 this.options.selection.single( this.model );
    7507         }
    7508 });
    7509 
    7510 module.exports = Selection;
    7511 
    7512 
    7513 /***/ }),
    7514 /* 81 */
    7515 /***/ (function(module, exports) {
    7516 
     4139},{}],33:[function(require,module,exports){
    75174140/**
    75184141 * wp.media.view.Attachments.Selection
    … …  
    75444167module.exports = Selection;
    75454168
    7546 
    7547 /***/ }),
    7548 /* 82 */
    7549 /***/ (function(module, exports) {
    7550 
     4169},{}],34:[function(require,module,exports){
    75514170/**
    7552  * wp.media.view.Attachments.EditSelection
     4171 * wp.media.view.ButtonGroup
    75534172 *
    75544173 * @class
    7555  * @augments wp.media.view.Attachment.Selection
    7556  * @augments wp.media.view.Attachment
    75574174 * @augments wp.media.View
    75584175 * @augments wp.Backbone.View
    75594176 * @augments Backbone.View
    75604177 */
    7561 var EditSelection = wp.media.view.Attachment.Selection.extend({
    7562         buttons: {
    7563                 close: true
     4178var $ = Backbone.$,
     4179        ButtonGroup;
     4180
     4181ButtonGroup = wp.media.View.extend({
     4182        tagName:   'div',
     4183        className: 'button-group button-large media-button-group',
     4184
     4185        initialize: function() {
     4186                /**
     4187                 * @member {wp.media.view.Button[]}
     4188                 */
     4189                this.buttons = _.map( this.options.buttons || [], function( button ) {
     4190                        if ( button instanceof Backbone.View ) {
     4191                                return button;
     4192                        } else {
     4193                                return new wp.media.view.Button( button ).render();
     4194                        }
     4195                });
     4196
     4197                delete this.options.buttons;
     4198
     4199                if ( this.options.classes ) {
     4200                        this.$el.addClass( this.options.classes );
     4201                }
     4202        },
     4203
     4204        /**
     4205         * @returns {wp.media.view.ButtonGroup}
     4206         */
     4207        render: function() {
     4208                this.$el.html( $( _.pluck( this.buttons, 'el' ) ).detach() );
     4209                return this;
    75644210        }
    75654211});
    75664212
    7567 module.exports = EditSelection;
    7568 
    7569 
    7570 /***/ }),
    7571 /* 83 */
    7572 /***/ (function(module, exports) {
    7573 
     4213module.exports = ButtonGroup;
     4214
     4215},{}],35:[function(require,module,exports){
    75744216/**
    7575  * wp.media.view.Settings
     4217 * wp.media.view.Button
     4218 *
     4219 * @class
     4220 * @augments wp.media.View
     4221 * @augments wp.Backbone.View
     4222 * @augments Backbone.View
     4223 */
     4224var Button = wp.media.View.extend({
     4225        tagName:    'button',
     4226        className:  'media-button',
     4227        attributes: { type: 'button' },
     4228
     4229        events: {
     4230                'click': 'click'
     4231        },
     4232
     4233        defaults: {
     4234                text:     '',
     4235                style:    '',
     4236                size:     'large',
     4237                disabled: false
     4238        },
     4239
     4240        initialize: function() {
     4241                /**
     4242                 * Create a model with the provided `defaults`.
     4243                 *
     4244                 * @member {Backbone.Model}
     4245                 */
     4246                this.model = new Backbone.Model( this.defaults );
     4247
     4248                // If any of the `options` have a key from `defaults`, apply its
     4249                // value to the `model` and remove it from the `options object.
     4250                _.each( this.defaults, function( def, key ) {
     4251                        var value = this.options[ key ];
     4252                        if ( _.isUndefined( value ) ) {
     4253                                return;
     4254                        }
     4255
     4256                        this.model.set( key, value );
     4257                        delete this.options[ key ];
     4258                }, this );
     4259
     4260                this.listenTo( this.model, 'change', this.render );
     4261        },
     4262        /**
     4263         * @returns {wp.media.view.Button} Returns itself to allow chaining
     4264         */
     4265        render: function() {
     4266                var classes = [ 'button', this.className ],
     4267                        model = this.model.toJSON();
     4268
     4269                if ( model.style ) {
     4270                        classes.push( 'button-' + model.style );
     4271                }
     4272
     4273                if ( model.size ) {
     4274                        classes.push( 'button-' + model.size );
     4275                }
     4276
     4277                classes = _.uniq( classes.concat( this.options.classes ) );
     4278                this.el.className = classes.join(' ');
     4279
     4280                this.$el.attr( 'disabled', model.disabled );
     4281                this.$el.text( this.model.get('text') );
     4282
     4283                return this;
     4284        },
     4285        /**
     4286         * @param {Object} event
     4287         */
     4288        click: function( event ) {
     4289                if ( '#' === this.attributes.href ) {
     4290                        event.preventDefault();
     4291                }
     4292
     4293                if ( this.options.click && ! this.model.get('disabled') ) {
     4294                        this.options.click.apply( this, arguments );
     4295                }
     4296        }
     4297});
     4298
     4299module.exports = Button;
     4300
     4301},{}],36:[function(require,module,exports){
     4302/**
     4303 * wp.media.view.Cropper
     4304 *
     4305 * Uses the imgAreaSelect plugin to allow a user to crop an image.
     4306 *
     4307 * Takes imgAreaSelect options from
     4308 * wp.customize.HeaderControl.calculateImageSelectOptions via
     4309 * wp.customize.HeaderControl.openMM.
    75764310 *
    75774311 * @class
    … …  
    75814315 */
    75824316var View = wp.media.View,
    7583         $ = Backbone.$,
    7584         Settings;
    7585 
    7586 Settings = View.extend({
    7587         events: {
    7588                 'click button':    'updateHandler',
    7589                 'change input':    'updateHandler',
    7590                 'change select':   'updateHandler',
    7591                 'change textarea': 'updateHandler'
    7592         },
    7593 
     4317        UploaderStatus = wp.media.view.UploaderStatus,
     4318        l10n = wp.media.view.l10n,
     4319        $ = jQuery,
     4320        Cropper;
     4321
     4322Cropper = View.extend({
     4323        className: 'crop-content',
     4324        template: wp.template('crop-content'),
    75944325        initialize: function() {
    7595                 this.model = this.model || new Backbone.Model();
    7596                 this.listenTo( this.model, 'change', this.updateChanges );
    7597         },
    7598 
     4326                _.bindAll(this, 'onImageLoad');
     4327        },
     4328        ready: function() {
     4329                this.controller.frame.on('content:error:crop', this.onError, this);
     4330                this.$image = this.$el.find('.crop-image');
     4331                this.$image.on('load', this.onImageLoad);
     4332                $(window).on('resize.cropper', _.debounce(this.onImageLoad, 250));
     4333        },
     4334        remove: function() {
     4335                $(window).off('resize.cropper');
     4336                this.$el.remove();
     4337                this.$el.off();
     4338                View.prototype.remove.apply(this, arguments);
     4339        },
    75994340        prepare: function() {
    7600                 return _.defaults({
    7601                         model: this.model.toJSON()
    7602                 }, this.options );
    7603         },
    7604         /**
    7605          * @returns {wp.media.view.Settings} Returns itself to allow chaining
    7606          */
    7607         render: function() {
    7608                 View.prototype.render.apply( this, arguments );
    7609                 // Select the correct values.
    7610                 _( this.model.attributes ).chain().keys().each( this.update, this );
    7611                 return this;
    7612         },
    7613         /**
    7614          * @param {string} key
    7615          */
    7616         update: function( key ) {
    7617                 var value = this.model.get( key ),
    7618                         $setting = this.$('[data-setting="' + key + '"]'),
    7619                         $buttons, $value;
    7620 
    7621                 // Bail if we didn't find a matching setting.
    7622                 if ( ! $setting.length ) {
    7623                         return;
    7624                 }
    7625 
    7626                 // Attempt to determine how the setting is rendered and update
    7627                 // the selected value.
    7628 
    7629                 // Handle dropdowns.
    7630                 if ( $setting.is('select') ) {
    7631                         $value = $setting.find('[value="' + value + '"]');
    7632 
    7633                         if ( $value.length ) {
    7634                                 $setting.find('option').prop( 'selected', false );
    7635                                 $value.prop( 'selected', true );
    7636                         } else {
    7637                                 // If we can't find the desired value, record what *is* selected.
    7638                                 this.model.set( key, $setting.find(':selected').val() );
    7639                         }
    7640 
    7641                 // Handle button groups.
    7642                 } else if ( $setting.hasClass('button-group') ) {
    7643                         $buttons = $setting.find('button').removeClass('active');
    7644                         $buttons.filter( '[value="' + value + '"]' ).addClass('active');
    7645 
    7646                 // Handle text inputs and textareas.
    7647                 } else if ( $setting.is('input[type="text"], textarea') ) {
    7648                         if ( ! $setting.is(':focus') ) {
    7649                                 $setting.val( value );
    7650                         }
    7651                 // Handle checkboxes.
    7652                 } else if ( $setting.is('input[type="checkbox"]') ) {
    7653                         $setting.prop( 'checked', !! value && 'false' !== value );
    7654                 }
    7655         },
    7656         /**
    7657          * @param {Object} event
    7658          */
    7659         updateHandler: function( event ) {
    7660                 var $setting = $( event.target ).closest('[data-setting]'),
    7661                         value = event.target.value,
    7662                         userSetting;
    7663 
    7664                 event.preventDefault();
    7665 
    7666                 if ( ! $setting.length ) {
    7667                         return;
    7668                 }
    7669 
    7670                 // Use the correct value for checkboxes.
    7671                 if ( $setting.is('input[type="checkbox"]') ) {
    7672                         value = $setting[0].checked;
    7673                 }
    7674 
    7675                 // Update the corresponding setting.
    7676                 this.model.set( $setting.data('setting'), value );
    7677 
    7678                 // If the setting has a corresponding user setting,
    7679                 // update that as well.
    7680                 if ( userSetting = $setting.data('userSetting') ) {
    7681                         window.setUserSetting( userSetting, value );
    7682                 }
    7683         },
    7684 
    7685         updateChanges: function( model ) {
    7686                 if ( model.hasChanged() ) {
    7687                         _( model.changed ).chain().keys().each( this.update, this );
    7688                 }
     4341                return {
     4342                        title: l10n.cropYourImage,
     4343                        url: this.options.attachment.get('url')
     4344                };
     4345        },
     4346        onImageLoad: function() {
     4347                var imgOptions = this.controller.get('imgSelectOptions');
     4348                if (typeof imgOptions === 'function') {
     4349                        imgOptions = imgOptions(this.options.attachment, this.controller);
     4350                }
     4351
     4352                imgOptions = _.extend(imgOptions, {parent: this.$el});
     4353                this.trigger('image-loaded');
     4354                this.controller.imgSelect = this.$image.imgAreaSelect(imgOptions);
     4355        },
     4356        onError: function() {
     4357                var filename = this.options.attachment.get('filename');
     4358
     4359                this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({
     4360                        filename: UploaderStatus.prototype.filename(filename),
     4361                        message: window._wpMediaViewsL10n.cropError
     4362                }), { at: 0 });
    76894363        }
    76904364});
    76914365
    7692 module.exports = Settings;
    7693 
    7694 
    7695 /***/ }),
    7696 /* 84 */
    7697 /***/ (function(module, exports) {
    7698 
     4366module.exports = Cropper;
     4367
     4368},{}],37:[function(require,module,exports){
    76994369/**
    7700  * wp.media.view.Settings.AttachmentDisplay
    7701  *
    7702  * @class
    7703  * @augments wp.media.view.Settings
    7704  * @augments wp.media.View
    7705  * @augments wp.Backbone.View
    7706  * @augments Backbone.View
    7707  */
    7708 var Settings = wp.media.view.Settings,
    7709         AttachmentDisplay;
    7710 
    7711 AttachmentDisplay = Settings.extend({
    7712         className: 'attachment-display-settings',
    7713         template:  wp.template('attachment-display-settings'),
    7714 
    7715         initialize: function() {
    7716                 var attachment = this.options.attachment;
    7717 
    7718                 _.defaults( this.options, {
    7719                         userSettings: false
    7720                 });
    7721                 // Call 'initialize' directly on the parent class.
    7722                 Settings.prototype.initialize.apply( this, arguments );
    7723                 this.listenTo( this.model, 'change:link', this.updateLinkTo );
    7724 
    7725                 if ( attachment ) {
    7726                         attachment.on( 'change:uploading', this.render, this );
    7727                 }
    7728         },
    7729 
    7730         dispose: function() {
    7731                 var attachment = this.options.attachment;
    7732                 if ( attachment ) {
    7733                         attachment.off( null, null, this );
    7734                 }
    7735                 /**
    7736                  * call 'dispose' directly on the parent class
    7737                  */
    7738                 Settings.prototype.dispose.apply( this, arguments );
    7739         },
    7740         /**
    7741          * @returns {wp.media.view.AttachmentDisplay} Returns itself to allow chaining
    7742          */
    7743         render: function() {
    7744                 var attachment = this.options.attachment;
    7745                 if ( attachment ) {
    7746                         _.extend( this.options, {
    7747                                 sizes: attachment.get('sizes'),
    7748                                 type:  attachment.get('type')
    7749                         });
    7750                 }
    7751                 /**
    7752                  * call 'render' directly on the parent class
    7753                  */
    7754                 Settings.prototype.render.call( this );
    7755                 this.updateLinkTo();
    7756                 return this;
    7757         },
    7758 
    7759         updateLinkTo: function() {
    7760                 var linkTo = this.model.get('link'),
    7761                         $input = this.$('.link-to-custom'),
    7762                         attachment = this.options.attachment;
    7763 
    7764                 if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
    7765                         $input.addClass( 'hidden' );
    7766                         return;
    7767                 }
    7768 
    7769                 if ( attachment ) {
    7770                         if ( 'post' === linkTo ) {
    7771                                 $input.val( attachment.get('link') );
    7772                         } else if ( 'file' === linkTo ) {
    7773                                 $input.val( attachment.get('url') );
    7774                         } else if ( ! this.model.get('linkUrl') ) {
    7775                                 $input.val('http://');
    7776                         }
    7777 
    7778                         $input.prop( 'readonly', 'custom' !== linkTo );
    7779                 }
    7780 
    7781                 $input.removeClass( 'hidden' );
    7782 
    7783                 // If the input is visible, focus and select its contents.
    7784                 if ( ! wp.media.isTouchDevice && $input.is(':visible') ) {
    7785                         $input.focus()[0].select();
    7786                 }
    7787         }
    7788 });
    7789 
    7790 module.exports = AttachmentDisplay;
    7791 
    7792 
    7793 /***/ }),
    7794 /* 85 */
    7795 /***/ (function(module, exports) {
    7796 
    7797 /**
    7798  * wp.media.view.Settings.Gallery
    7799  *
    7800  * @class
    7801  * @augments wp.media.view.Settings
    7802  * @augments wp.media.View
    7803  * @augments wp.Backbone.View
    7804  * @augments Backbone.View
    7805  */
    7806 var Gallery = wp.media.view.Settings.extend({
    7807         className: 'collection-settings gallery-settings',
    7808         template:  wp.template('gallery-settings')
    7809 });
    7810 
    7811 module.exports = Gallery;
    7812 
    7813 
    7814 /***/ }),
    7815 /* 86 */
    7816 /***/ (function(module, exports) {
    7817 
    7818 /**
    7819  * wp.media.view.Settings.Playlist
    7820  *
    7821  * @class
    7822  * @augments wp.media.view.Settings
    7823  * @augments wp.media.View
    7824  * @augments wp.Backbone.View
    7825  * @augments Backbone.View
    7826  */
    7827 var Playlist = wp.media.view.Settings.extend({
    7828         className: 'collection-settings playlist-settings',
    7829         template:  wp.template('playlist-settings')
    7830 });
    7831 
    7832 module.exports = Playlist;
    7833 
    7834 
    7835 /***/ }),
    7836 /* 87 */
    7837 /***/ (function(module, exports) {
    7838 
    7839 /**
    7840  * wp.media.view.Attachment.Details
    7841  *
    7842  * @class
    7843  * @augments wp.media.view.Attachment
    7844  * @augments wp.media.View
    7845  * @augments wp.Backbone.View
    7846  * @augments Backbone.View
    7847  */
    7848 var Attachment = wp.media.view.Attachment,
    7849         l10n = wp.media.view.l10n,
    7850         Details;
    7851 
    7852 Details = Attachment.extend({
    7853         tagName:   'div',
    7854         className: 'attachment-details',
    7855         template:  wp.template('attachment-details'),
    7856 
    7857         attributes: function() {
    7858                 return {
    7859                         'tabIndex':     0,
    7860                         'data-id':      this.model.get( 'id' )
    7861                 };
    7862         },
    7863 
    7864         events: {
    7865                 'change [data-setting]':          'updateSetting',
    7866                 'change [data-setting] input':    'updateSetting',
    7867                 'change [data-setting] select':   'updateSetting',
    7868                 'change [data-setting] textarea': 'updateSetting',
    7869                 'click .delete-attachment':       'deleteAttachment',
    7870                 'click .trash-attachment':        'trashAttachment',
    7871                 'click .untrash-attachment':      'untrashAttachment',
    7872                 'click .edit-attachment':         'editAttachment',
    7873                 'keydown':                        'toggleSelectionHandler'
    7874         },
    7875 
    7876         initialize: function() {
    7877                 this.options = _.defaults( this.options, {
    7878                         rerenderOnModelChange: false
    7879                 });
    7880 
    7881                 this.on( 'ready', this.initialFocus );
    7882                 // Call 'initialize' directly on the parent class.
    7883                 Attachment.prototype.initialize.apply( this, arguments );
    7884         },
    7885 
    7886         initialFocus: function() {
    7887                 if ( ! wp.media.isTouchDevice ) {
    7888                         /*
    7889                         Previously focused the first ':input' (the readonly URL text field).
    7890                         Since the first ':input' is now a button (delete/trash): when pressing
    7891                         spacebar on an attachment, Firefox fires deleteAttachment/trashAttachment
    7892                         as soon as focus is moved. Explicitly target the first text field for now.
    7893                         @todo change initial focus logic, also for accessibility.
    7894                         */
    7895                         this.$( 'input[type="text"]' ).eq( 0 ).focus();
    7896                 }
    7897         },
    7898         /**
    7899          * @param {Object} event
    7900          */
    7901         deleteAttachment: function( event ) {
    7902                 event.preventDefault();
    7903 
    7904                 if ( window.confirm( l10n.warnDelete ) ) {
    7905                         this.model.destroy();
    7906                         // Keep focus inside media modal
    7907                         // after image is deleted
    7908                         this.controller.modal.focusManager.focus();
    7909                 }
    7910         },
    7911         /**
    7912          * @param {Object} event
    7913          */
    7914         trashAttachment: function( event ) {
    7915                 var library = this.controller.library;
    7916                 event.preventDefault();
    7917 
    7918                 if ( wp.media.view.settings.mediaTrash &&
    7919                         'edit-metadata' === this.controller.content.mode() ) {
    7920 
    7921                         this.model.set( 'status', 'trash' );
    7922                         this.model.save().done( function() {
    7923                                 library._requery( true );
    7924                         } );
    7925                 }  else {
    7926                         this.model.destroy();
    7927                 }
    7928         },
    7929         /**
    7930          * @param {Object} event
    7931          */
    7932         untrashAttachment: function( event ) {
    7933                 var library = this.controller.library;
    7934                 event.preventDefault();
    7935 
    7936                 this.model.set( 'status', 'inherit' );
    7937                 this.model.save().done( function() {
    7938                         library._requery( true );
    7939                 } );
    7940         },
    7941         /**
    7942          * @param {Object} event
    7943          */
    7944         editAttachment: function( event ) {
    7945                 var editState = this.controller.states.get( 'edit-image' );
    7946                 if ( window.imageEdit && editState ) {
    7947                         event.preventDefault();
    7948 
    7949                         editState.set( 'image', this.model );
    7950                         this.controller.setState( 'edit-image' );
    7951                 } else {
    7952                         this.$el.addClass('needs-refresh');
    7953                 }
    7954         },
    7955         /**
    7956          * When reverse tabbing(shift+tab) out of the right details panel, deliver
    7957          * the focus to the item in the list that was being edited.
    7958          *
    7959          * @param {Object} event
    7960          */
    7961         toggleSelectionHandler: function( event ) {
    7962                 if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) {
    7963                         this.controller.trigger( 'attachment:details:shift-tab', event );
    7964                         return false;
    7965                 }
    7966 
    7967                 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
    7968                         this.controller.trigger( 'attachment:keydown:arrow', event );
    7969                         return;
    7970                 }
    7971         }
    7972 });
    7973 
    7974 module.exports = Details;
    7975 
    7976 
    7977 /***/ }),
    7978 /* 88 */
    7979 /***/ (function(module, exports) {
    7980 
    7981 /**
    7982  * wp.media.view.AttachmentCompat
    7983  *
    7984  * A view to display fields added via the `attachment_fields_to_edit` filter.
     4370 * wp.media.view.EditImage
    79854371 *
    79864372 * @class
    … …  
    79904376 */
    79914377var View = wp.media.View,
    7992         AttachmentCompat;
    7993 
    7994 AttachmentCompat = View.extend({
    7995         tagName:   'form',
    7996         className: 'compat-item',
    7997 
    7998         events: {
    7999                 'submit':          'preventDefault',
    8000                 'change input':    'save',
    8001                 'change select':   'save',
    8002                 'change textarea': 'save'
    8003         },
    8004 
    8005         initialize: function() {
    8006                 this.listenTo( this.model, 'change:compat', this.render );
    8007         },
    8008         /**
    8009          * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
    8010          */
    8011         dispose: function() {
    8012                 if ( this.$(':focus').length ) {
    8013                         this.save();
    8014                 }
    8015                 /**
    8016                  * call 'dispose' directly on the parent class
    8017                  */
    8018                 return View.prototype.dispose.apply( this, arguments );
    8019         },
    8020         /**
    8021          * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
    8022          */
    8023         render: function() {
    8024                 var compat = this.model.get('compat');
    8025                 if ( ! compat || ! compat.item ) {
    8026                         return;
    8027                 }
    8028 
    8029                 this.views.detach();
    8030                 this.$el.html( compat.item );
    8031                 this.views.render();
    8032                 return this;
    8033         },
    8034         /**
    8035          * @param {Object} event
    8036          */
    8037         preventDefault: function( event ) {
    8038                 event.preventDefault();
    8039         },
    8040         /**
    8041          * @param {Object} event
    8042          */
    8043         save: function( event ) {
    8044                 var data = {};
    8045 
    8046                 if ( event ) {
    8047                         event.preventDefault();
    8048                 }
    8049 
    8050                 _.each( this.$el.serializeArray(), function( pair ) {
    8051                         data[ pair.name ] = pair.value;
    8052                 });
    8053 
    8054                 this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
    8055                 this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
    8056         },
    8057 
    8058         postSave: function() {
    8059                 this.controller.trigger( 'attachment:compat:ready', ['ready'] );
     4378        EditImage;
     4379
     4380EditImage = View.extend({
     4381        className: 'image-editor',
     4382        template: wp.template('image-editor'),
     4383
     4384        initialize: function( options ) {
     4385                this.editor = window.imageEdit;
     4386                this.controller = options.controller;
     4387                View.prototype.initialize.apply( this, arguments );
     4388        },
     4389
     4390        prepare: function() {
     4391                return this.model.toJSON();
     4392        },
     4393
     4394        loadEditor: function() {
     4395                var dfd = this.editor.open( this.model.get('id'), this.model.get('nonces').edit, this );
     4396                dfd.done( _.bind( this.focus, this ) );
     4397        },
     4398
     4399        focus: function() {
     4400                this.$( '.imgedit-submit .button' ).eq( 0 ).focus();
     4401        },
     4402
     4403        back: function() {
     4404                var lastState = this.controller.lastState();
     4405                this.controller.setState( lastState );
     4406        },
     4407
     4408        refresh: function() {
     4409                this.model.fetch();
     4410        },
     4411
     4412        save: function() {
     4413                var lastState = this.controller.lastState();
     4414
     4415                this.model.fetch().done( _.bind( function() {
     4416                        this.controller.setState( lastState );
     4417                }, this ) );
    80604418        }
     4419
    80614420});
    80624421
    8063 module.exports = AttachmentCompat;
    8064 
    8065 
    8066 /***/ }),
    8067 /* 89 */
    8068 /***/ (function(module, exports) {
    8069 
    8070 /**
    8071  * wp.media.view.Iframe
    8072  *
    8073  * @class
    8074  * @augments wp.media.View
    8075  * @augments wp.Backbone.View
    8076  * @augments Backbone.View
    8077  */
    8078 var Iframe = wp.media.View.extend({
    8079         className: 'media-iframe',
    8080         /**
    8081          * @returns {wp.media.view.Iframe} Returns itself to allow chaining
    8082          */
    8083         render: function() {
    8084                 this.views.detach();
    8085                 this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
    8086                 this.views.render();
    8087                 return this;
    8088         }
    8089 });
    8090 
    8091 module.exports = Iframe;
    8092 
    8093 
    8094 /***/ }),
    8095 /* 90 */
    8096 /***/ (function(module, exports) {
    8097 
     4422module.exports = EditImage;
     4423
     4424},{}],38:[function(require,module,exports){
    80984425/**
    80994426 * wp.media.view.Embed
    … …  
    81594486module.exports = Embed;
    81604487
    8161 
    8162 /***/ }),
    8163 /* 91 */
    8164 /***/ (function(module, exports) {
    8165 
     4488},{}],39:[function(require,module,exports){
    81664489/**
    8167  * wp.media.view.Label
     4490 * wp.media.view.EmbedImage
    81684491 *
    81694492 * @class
     4493 * @augments wp.media.view.Settings.AttachmentDisplay
     4494 * @augments wp.media.view.Settings
    81704495 * @augments wp.media.View
    81714496 * @augments wp.Backbone.View
    81724497 * @augments Backbone.View
    81734498 */
    8174 var Label = wp.media.View.extend({
    8175         tagName: 'label',
    8176         className: 'screen-reader-text',
     4499var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
     4500        EmbedImage;
     4501
     4502EmbedImage = AttachmentDisplay.extend({
     4503        className: 'embed-media-settings',
     4504        template:  wp.template('embed-image-settings'),
    81774505
    81784506        initialize: function() {
    8179                 this.value = this.options.value;
    8180         },
    8181 
    8182         render: function() {
    8183                 this.$el.html( this.value );
    8184 
    8185                 return this;
     4507                /**
     4508                 * Call `initialize` directly on parent class with passed arguments
     4509                 */
     4510                AttachmentDisplay.prototype.initialize.apply( this, arguments );
     4511                this.listenTo( this.model, 'change:url', this.updateImage );
     4512        },
     4513
     4514        updateImage: function() {
     4515                this.$('img').attr( 'src', this.model.get('url') );
    81864516        }
    81874517});
    81884518
    8189 module.exports = Label;
    8190 
    8191 
    8192 /***/ }),
    8193 /* 92 */
    8194 /***/ (function(module, exports) {
    8195 
    8196 /**
    8197  * wp.media.view.EmbedUrl
    8198  *
    8199  * @class
    8200  * @augments wp.media.View
    8201  * @augments wp.Backbone.View
    8202  * @augments Backbone.View
    8203  */
    8204 var View = wp.media.View,
    8205         $ = jQuery,
    8206         EmbedUrl;
    8207 
    8208 EmbedUrl = View.extend({
    8209         tagName:   'label',
    8210         className: 'embed-url',
    8211 
    8212         events: {
    8213                 'input':  'url',
    8214                 'keyup':  'url',
    8215                 'change': 'url'
    8216         },
    8217 
    8218         initialize: function() {
    8219                 this.$input = $('<input id="embed-url-field" type="url" />').val( this.model.get('url') );
    8220                 this.input = this.$input[0];
    8221 
    8222                 this.spinner = $('<span class="spinner" />')[0];
    8223                 this.$el.append([ this.input, this.spinner ]);
    8224 
    8225                 this.listenTo( this.model, 'change:url', this.render );
    8226 
    8227                 if ( this.model.get( 'url' ) ) {
    8228                         _.delay( _.bind( function () {
    8229                                 this.model.trigger( 'change:url' );
    8230                         }, this ), 500 );
    8231                 }
    8232         },
    8233         /**
    8234          * @returns {wp.media.view.EmbedUrl} Returns itself to allow chaining
    8235          */
    8236         render: function() {
    8237                 var $input = this.$input;
    8238 
    8239                 if ( $input.is(':focus') ) {
    8240                         return;
    8241                 }
    8242 
    8243                 this.input.value = this.model.get('url') || 'http://';
    8244                 /**
    8245                  * Call `render` directly on parent class with passed arguments
    8246                  */
    8247                 View.prototype.render.apply( this, arguments );
    8248                 return this;
    8249         },
    8250 
    8251         ready: function() {
    8252                 if ( ! wp.media.isTouchDevice ) {
    8253                         this.focus();
    8254                 }
    8255         },
    8256 
    8257         url: function( event ) {
    8258                 this.model.set( 'url', event.target.value );
    8259         },
    8260 
    8261         /**
    8262          * If the input is visible, focus and select its contents.
    8263          */
    8264         focus: function() {
    8265                 var $input = this.$input;
    8266                 if ( $input.is(':visible') ) {
    8267                         $input.focus()[0].select();
    8268                 }
    8269         }
    8270 });
    8271 
    8272 module.exports = EmbedUrl;
    8273 
    8274 
    8275 /***/ }),
    8276 /* 93 */
    8277 /***/ (function(module, exports) {
    8278 
     4519module.exports = EmbedImage;
     4520
     4521},{}],40:[function(require,module,exports){
    82794522/**
    82804523 * wp.media.view.EmbedLink
    … …  
    83654608module.exports = EmbedLink;
    83664609
    8367 
    8368 /***/ }),
    8369 /* 94 */
    8370 /***/ (function(module, exports) {
    8371 
     4610},{}],41:[function(require,module,exports){
    83724611/**
    8373  * wp.media.view.EmbedImage
     4612 * wp.media.view.EmbedUrl
    83744613 *
    83754614 * @class
    8376  * @augments wp.media.view.Settings.AttachmentDisplay
    8377  * @augments wp.media.view.Settings
    83784615 * @augments wp.media.View
    83794616 * @augments wp.Backbone.View
    83804617 * @augments Backbone.View
    83814618 */
    8382 var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
    8383         EmbedImage;
    8384 
    8385 EmbedImage = AttachmentDisplay.extend({
    8386         className: 'embed-media-settings',
    8387         template:  wp.template('embed-image-settings'),
     4619var View = wp.media.View,
     4620        $ = jQuery,
     4621        EmbedUrl;
     4622
     4623EmbedUrl = View.extend({
     4624        tagName:   'label',
     4625        className: 'embed-url',
     4626
     4627        events: {
     4628                'input':  'url',
     4629                'keyup':  'url',
     4630                'change': 'url'
     4631        },
    83884632
    83894633        initialize: function() {
     4634                this.$input = $('<input id="embed-url-field" type="url" />').val( this.model.get('url') );
     4635                this.input = this.$input[0];
     4636
     4637                this.spinner = $('<span class="spinner" />')[0];
     4638                this.$el.append([ this.input, this.spinner ]);
     4639
     4640                this.listenTo( this.model, 'change:url', this.render );
     4641
     4642                if ( this.model.get( 'url' ) ) {
     4643                        _.delay( _.bind( function () {
     4644                                this.model.trigger( 'change:url' );
     4645                        }, this ), 500 );
     4646                }
     4647        },
     4648        /**
     4649         * @returns {wp.media.view.EmbedUrl} Returns itself to allow chaining
     4650         */
     4651        render: function() {
     4652                var $input = this.$input;
     4653
     4654                if ( $input.is(':focus') ) {
     4655                        return;
     4656                }
     4657
     4658                this.input.value = this.model.get('url') || 'http://';
    83904659                /**
    8391                  * Call `initialize` directly on parent class with passed arguments
     4660                 * Call `render` directly on parent class with passed arguments
    83924661                 */
    8393                 AttachmentDisplay.prototype.initialize.apply( this, arguments );
    8394                 this.listenTo( this.model, 'change:url', this.updateImage );
    8395         },
    8396 
    8397         updateImage: function() {
    8398                 this.$('img').attr( 'src', this.model.get('url') );
     4662                View.prototype.render.apply( this, arguments );
     4663                return this;
     4664        },
     4665
     4666        ready: function() {
     4667                if ( ! wp.media.isTouchDevice ) {
     4668                        this.focus();
     4669                }
     4670        },
     4671
     4672        url: function( event ) {
     4673                this.model.set( 'url', event.target.value );
     4674        },
     4675
     4676        /**
     4677         * If the input is visible, focus and select its contents.
     4678         */
     4679        focus: function() {
     4680                var $input = this.$input;
     4681                if ( $input.is(':visible') ) {
     4682                        $input.focus()[0].select();
     4683                }
    83994684        }
    84004685});
    84014686
    8402 module.exports = EmbedImage;
    8403 
    8404 
    8405 /***/ }),
    8406 /* 95 */
    8407 /***/ (function(module, exports) {
    8408 
     4687module.exports = EmbedUrl;
     4688
     4689},{}],42:[function(require,module,exports){
     4690/**
     4691 * wp.media.view.FocusManager
     4692 *
     4693 * @class
     4694 * @augments wp.media.View
     4695 * @augments wp.Backbone.View
     4696 * @augments Backbone.View
     4697 */
     4698var FocusManager = wp.media.View.extend({
     4699
     4700        events: {
     4701                'keydown': 'constrainTabbing'
     4702        },
     4703
     4704        focus: function() { // Reset focus on first left menu item
     4705                this.$('.media-menu-item').first().focus();
     4706        },
     4707        /**
     4708         * @param {Object} event
     4709         */
     4710        constrainTabbing: function( event ) {
     4711                var tabbables;
     4712
     4713                // Look for the tab key.
     4714                if ( 9 !== event.keyCode ) {
     4715                        return;
     4716                }
     4717
     4718                // Skip the file input added by Plupload.
     4719                tabbables = this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' );
     4720
     4721                // Keep tab focus within media modal while it's open
     4722                if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
     4723                        tabbables.first().focus();
     4724                        return false;
     4725                } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
     4726                        tabbables.last().focus();
     4727                        return false;
     4728                }
     4729        }
     4730
     4731});
     4732
     4733module.exports = FocusManager;
     4734
     4735},{}],43:[function(require,module,exports){
     4736/**
     4737 * wp.media.view.Frame
     4738 *
     4739 * A frame is a composite view consisting of one or more regions and one or more
     4740 * states.
     4741 *
     4742 * @see wp.media.controller.State
     4743 * @see wp.media.controller.Region
     4744 *
     4745 * @class
     4746 * @augments wp.media.View
     4747 * @augments wp.Backbone.View
     4748 * @augments Backbone.View
     4749 * @mixes wp.media.controller.StateMachine
     4750 */
     4751var Frame = wp.media.View.extend({
     4752        initialize: function() {
     4753                _.defaults( this.options, {
     4754                        mode: [ 'select' ]
     4755                });
     4756                this._createRegions();
     4757                this._createStates();
     4758                this._createModes();
     4759        },
     4760
     4761        _createRegions: function() {
     4762                // Clone the regions array.
     4763                this.regions = this.regions ? this.regions.slice() : [];
     4764
     4765                // Initialize regions.
     4766                _.each( this.regions, function( region ) {
     4767                        this[ region ] = new wp.media.controller.Region({
     4768                                view:     this,
     4769                                id:       region,
     4770                                selector: '.media-frame-' + region
     4771                        });
     4772                }, this );
     4773        },
     4774        /**
     4775         * Create the frame's states.
     4776         *
     4777         * @see wp.media.controller.State
     4778         * @see wp.media.controller.StateMachine
     4779         *
     4780         * @fires wp.media.controller.State#ready
     4781         */
     4782        _createStates: function() {
     4783                // Create the default `states` collection.
     4784                this.states = new Backbone.Collection( null, {
     4785                        model: wp.media.controller.State
     4786                });
     4787
     4788                // Ensure states have a reference to the frame.
     4789                this.states.on( 'add', function( model ) {
     4790                        model.frame = this;
     4791                        model.trigger('ready');
     4792                }, this );
     4793
     4794                if ( this.options.states ) {
     4795                        this.states.add( this.options.states );
     4796                }
     4797        },
     4798
     4799        /**
     4800         * A frame can be in a mode or multiple modes at one time.
     4801         *
     4802         * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode.
     4803         */
     4804        _createModes: function() {
     4805                // Store active "modes" that the frame is in. Unrelated to region modes.
     4806                this.activeModes = new Backbone.Collection();
     4807                this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) );
     4808
     4809                _.each( this.options.mode, function( mode ) {
     4810                        this.activateMode( mode );
     4811                }, this );
     4812        },
     4813        /**
     4814         * Reset all states on the frame to their defaults.
     4815         *
     4816         * @returns {wp.media.view.Frame} Returns itself to allow chaining
     4817         */
     4818        reset: function() {
     4819                this.states.invoke( 'trigger', 'reset' );
     4820                return this;
     4821        },
     4822        /**
     4823         * Map activeMode collection events to the frame.
     4824         */
     4825        triggerModeEvents: function( model, collection, options ) {
     4826                var collectionEvent,
     4827                        modeEventMap = {
     4828                                add: 'activate',
     4829                                remove: 'deactivate'
     4830                        },
     4831                        eventToTrigger;
     4832                // Probably a better way to do this.
     4833                _.each( options, function( value, key ) {
     4834                        if ( value ) {
     4835                                collectionEvent = key;
     4836                        }
     4837                } );
     4838
     4839                if ( ! _.has( modeEventMap, collectionEvent ) ) {
     4840                        return;
     4841                }
     4842
     4843                eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent];
     4844                this.trigger( eventToTrigger );
     4845        },
     4846        /**
     4847         * Activate a mode on the frame.
     4848         *
     4849         * @param string mode Mode ID.
     4850         * @returns {this} Returns itself to allow chaining.
     4851         */
     4852        activateMode: function( mode ) {
     4853                // Bail if the mode is already active.
     4854                if ( this.isModeActive( mode ) ) {
     4855                        return;
     4856                }
     4857                this.activeModes.add( [ { id: mode } ] );
     4858                // Add a CSS class to the frame so elements can be styled for the mode.
     4859                this.$el.addClass( 'mode-' + mode );
     4860
     4861                return this;
     4862        },
     4863        /**
     4864         * Deactivate a mode on the frame.
     4865         *
     4866         * @param string mode Mode ID.
     4867         * @returns {this} Returns itself to allow chaining.
     4868         */
     4869        deactivateMode: function( mode ) {
     4870                // Bail if the mode isn't active.
     4871                if ( ! this.isModeActive( mode ) ) {
     4872                        return this;
     4873                }
     4874                this.activeModes.remove( this.activeModes.where( { id: mode } ) );
     4875                this.$el.removeClass( 'mode-' + mode );
     4876                /**
     4877                 * Frame mode deactivation event.
     4878                 *
     4879                 * @event this#{mode}:deactivate
     4880                 */
     4881                this.trigger( mode + ':deactivate' );
     4882
     4883                return this;
     4884        },
     4885        /**
     4886         * Check if a mode is enabled on the frame.
     4887         *
     4888         * @param  string mode Mode ID.
     4889         * @return bool
     4890         */
     4891        isModeActive: function( mode ) {
     4892                return Boolean( this.activeModes.where( { id: mode } ).length );
     4893        }
     4894});
     4895
     4896// Make the `Frame` a `StateMachine`.
     4897_.extend( Frame.prototype, wp.media.controller.StateMachine.prototype );
     4898
     4899module.exports = Frame;
     4900
     4901},{}],44:[function(require,module,exports){
     4902/**
     4903 * wp.media.view.MediaFrame.ImageDetails
     4904 *
     4905 * A media frame for manipulating an image that's already been inserted
     4906 * into a post.
     4907 *
     4908 * @class
     4909 * @augments wp.media.view.MediaFrame.Select
     4910 * @augments wp.media.view.MediaFrame
     4911 * @augments wp.media.view.Frame
     4912 * @augments wp.media.View
     4913 * @augments wp.Backbone.View
     4914 * @augments Backbone.View
     4915 * @mixes wp.media.controller.StateMachine
     4916 */
     4917var Select = wp.media.view.MediaFrame.Select,
     4918        l10n = wp.media.view.l10n,
     4919        ImageDetails;
     4920
     4921ImageDetails = Select.extend({
     4922        defaults: {
     4923                id:      'image',
     4924                url:     '',
     4925                menu:    'image-details',
     4926                content: 'image-details',
     4927                toolbar: 'image-details',
     4928                type:    'link',
     4929                title:    l10n.imageDetailsTitle,
     4930                priority: 120
     4931        },
     4932
     4933        initialize: function( options ) {
     4934                this.image = new wp.media.model.PostImage( options.metadata );
     4935                this.options.selection = new wp.media.model.Selection( this.image.attachment, { multiple: false } );
     4936                Select.prototype.initialize.apply( this, arguments );
     4937        },
     4938
     4939        bindHandlers: function() {
     4940                Select.prototype.bindHandlers.apply( this, arguments );
     4941                this.on( 'menu:create:image-details', this.createMenu, this );
     4942                this.on( 'content:create:image-details', this.imageDetailsContent, this );
     4943                this.on( 'content:render:edit-image', this.editImageContent, this );
     4944                this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this );
     4945                // override the select toolbar
     4946                this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this );
     4947        },
     4948
     4949        createStates: function() {
     4950                this.states.add([
     4951                        new wp.media.controller.ImageDetails({
     4952                                image: this.image,
     4953                                editable: false
     4954                        }),
     4955                        new wp.media.controller.ReplaceImage({
     4956                                id: 'replace-image',
     4957                                library: wp.media.query( { type: 'image' } ),
     4958                                image: this.image,
     4959                                multiple:  false,
     4960                                title:     l10n.imageReplaceTitle,
     4961                                toolbar: 'replace',
     4962                                priority:  80,
     4963                                displaySettings: true
     4964                        }),
     4965                        new wp.media.controller.EditImage( {
     4966                                image: this.image,
     4967                                selection: this.options.selection
     4968                        } )
     4969                ]);
     4970        },
     4971
     4972        imageDetailsContent: function( options ) {
     4973                options.view = new wp.media.view.ImageDetails({
     4974                        controller: this,
     4975                        model: this.state().image,
     4976                        attachment: this.state().image.attachment
     4977                });
     4978        },
     4979
     4980        editImageContent: function() {
     4981                var state = this.state(),
     4982                        model = state.get('image'),
     4983                        view;
     4984
     4985                if ( ! model ) {
     4986                        return;
     4987                }
     4988
     4989                view = new wp.media.view.EditImage( { model: model, controller: this } ).render();
     4990
     4991                this.content.set( view );
     4992
     4993                // after bringing in the frame, load the actual editor via an ajax call
     4994                view.loadEditor();
     4995
     4996        },
     4997
     4998        renderImageDetailsToolbar: function() {
     4999                this.toolbar.set( new wp.media.view.Toolbar({
     5000                        controller: this,
     5001                        items: {
     5002                                select: {
     5003                                        style:    'primary',
     5004                                        text:     l10n.update,
     5005                                        priority: 80,
     5006
     5007                                        click: function() {
     5008                                                var controller = this.controller,
     5009                                                        state = controller.state();
     5010
     5011                                                controller.close();
     5012
     5013                                                // not sure if we want to use wp.media.string.image which will create a shortcode or
     5014                                                // perhaps wp.html.string to at least to build the <img />
     5015                                                state.trigger( 'update', controller.image.toJSON() );
     5016
     5017                                                // Restore and reset the default state.
     5018                                                controller.setState( controller.options.state );
     5019                                                controller.reset();
     5020                                        }
     5021                                }
     5022                        }
     5023                }) );
     5024        },
     5025
     5026        renderReplaceImageToolbar: function() {
     5027                var frame = this,
     5028                        lastState = frame.lastState(),
     5029                        previous = lastState && lastState.id;
     5030
     5031                this.toolbar.set( new wp.media.view.Toolbar({
     5032                        controller: this,
     5033                        items: {
     5034                                back: {
     5035                                        text:     l10n.back,
     5036                                        priority: 20,
     5037                                        click:    function() {
     5038                                                if ( previous ) {
     5039                                                        frame.setState( previous );
     5040                                                } else {
     5041                                                        frame.close();
     5042                                                }
     5043                                        }
     5044                                },
     5045
     5046                                replace: {
     5047                                        style:    'primary',
     5048                                        text:     l10n.replace,
     5049                                        priority: 80,
     5050
     5051                                        click: function() {
     5052                                                var controller = this.controller,
     5053                                                        state = controller.state(),
     5054                                                        selection = state.get( 'selection' ),
     5055                                                        attachment = selection.single();
     5056
     5057                                                controller.close();
     5058
     5059                                                controller.image.changeAttachment( attachment, state.display( attachment ) );
     5060
     5061                                                // not sure if we want to use wp.media.string.image which will create a shortcode or
     5062                                                // perhaps wp.html.string to at least to build the <img />
     5063                                                state.trigger( 'replace', controller.image.toJSON() );
     5064
     5065                                                // Restore and reset the default state.
     5066                                                controller.setState( controller.options.state );
     5067                                                controller.reset();
     5068                                        }
     5069                                }
     5070                        }
     5071                }) );
     5072        }
     5073
     5074});
     5075
     5076module.exports = ImageDetails;
     5077
     5078},{}],45:[function(require,module,exports){
     5079/**
     5080 * wp.media.view.MediaFrame.Post
     5081 *
     5082 * The frame for manipulating media on the Edit Post page.
     5083 *
     5084 * @class
     5085 * @augments wp.media.view.MediaFrame.Select
     5086 * @augments wp.media.view.MediaFrame
     5087 * @augments wp.media.view.Frame
     5088 * @augments wp.media.View
     5089 * @augments wp.Backbone.View
     5090 * @augments Backbone.View
     5091 * @mixes wp.media.controller.StateMachine
     5092 */
     5093var Select = wp.media.view.MediaFrame.Select,
     5094        Library = wp.media.controller.Library,
     5095        l10n = wp.media.view.l10n,
     5096        Post;
     5097
     5098Post = Select.extend({
     5099        initialize: function() {
     5100                this.counts = {
     5101                        audio: {
     5102                                count: wp.media.view.settings.attachmentCounts.audio,
     5103                                state: 'playlist'
     5104                        },
     5105                        video: {
     5106                                count: wp.media.view.settings.attachmentCounts.video,
     5107                                state: 'video-playlist'
     5108                        }
     5109                };
     5110
     5111                _.defaults( this.options, {
     5112                        multiple:  true,
     5113                        editing:   false,
     5114                        state:    'insert',
     5115                        metadata:  {}
     5116                });
     5117
     5118                // Call 'initialize' directly on the parent class.
     5119                Select.prototype.initialize.apply( this, arguments );
     5120                this.createIframeStates();
     5121
     5122        },
     5123
     5124        /**
     5125         * Create the default states.
     5126         */
     5127        createStates: function() {
     5128                var options = this.options;
     5129
     5130                this.states.add([
     5131                        // Main states.
     5132                        new Library({
     5133                                id:         'insert',
     5134                                title:      l10n.insertMediaTitle,
     5135                                priority:   20,
     5136                                toolbar:    'main-insert',
     5137                                filterable: 'all',
     5138                                library:    wp.media.query( options.library ),
     5139                                multiple:   options.multiple ? 'reset' : false,
     5140                                editable:   true,
     5141
     5142                                // If the user isn't allowed to edit fields,
     5143                                // can they still edit it locally?
     5144                                allowLocalEdits: true,
     5145
     5146                                // Show the attachment display settings.
     5147                                displaySettings: true,
     5148                                // Update user settings when users adjust the
     5149                                // attachment display settings.
     5150                                displayUserSettings: true
     5151                        }),
     5152
     5153                        new Library({
     5154                                id:         'gallery',
     5155                                title:      l10n.createGalleryTitle,
     5156                                priority:   40,
     5157                                toolbar:    'main-gallery',
     5158                                filterable: 'uploaded',
     5159                                multiple:   'add',
     5160                                editable:   false,
     5161
     5162                                library:  wp.media.query( _.defaults({
     5163                                        type: 'image'
     5164                                }, options.library ) )
     5165                        }),
     5166
     5167                        // Embed states.
     5168                        new wp.media.controller.Embed( { metadata: options.metadata } ),
     5169
     5170                        new wp.media.controller.EditImage( { model: options.editImage } ),
     5171
     5172                        // Gallery states.
     5173                        new wp.media.controller.GalleryEdit({
     5174                                library: options.selection,
     5175                                editing: options.editing,
     5176                                menu:    'gallery'
     5177                        }),
     5178
     5179                        new wp.media.controller.GalleryAdd(),
     5180
     5181                        new Library({
     5182                                id:         'playlist',
     5183                                title:      l10n.createPlaylistTitle,
     5184                                priority:   60,
     5185                                toolbar:    'main-playlist',
     5186                                filterable: 'uploaded',
     5187                                multiple:   'add',
     5188                                editable:   false,
     5189
     5190                                library:  wp.media.query( _.defaults({
     5191                                        type: 'audio'
     5192                                }, options.library ) )
     5193                        }),
     5194
     5195                        // Playlist states.
     5196                        new wp.media.controller.CollectionEdit({
     5197                                type: 'audio',
     5198                                collectionType: 'playlist',
     5199                                title:          l10n.editPlaylistTitle,
     5200                                SettingsView:   wp.media.view.Settings.Playlist,
     5201                                library:        options.selection,
     5202                                editing:        options.editing,
     5203                                menu:           'playlist',
     5204                                dragInfoText:   l10n.playlistDragInfo,
     5205                                dragInfo:       false
     5206                        }),
     5207
     5208                        new wp.media.controller.CollectionAdd({
     5209                                type: 'audio',
     5210                                collectionType: 'playlist',
     5211                                title: l10n.addToPlaylistTitle
     5212                        }),
     5213
     5214                        new Library({
     5215                                id:         'video-playlist',
     5216                                title:      l10n.createVideoPlaylistTitle,
     5217                                priority:   60,
     5218                                toolbar:    'main-video-playlist',
     5219                                filterable: 'uploaded',
     5220                                multiple:   'add',
     5221                                editable:   false,
     5222
     5223                                library:  wp.media.query( _.defaults({
     5224                                        type: 'video'
     5225                                }, options.library ) )
     5226                        }),
     5227
     5228                        new wp.media.controller.CollectionEdit({
     5229                                type: 'video',
     5230                                collectionType: 'playlist',
     5231                                title:          l10n.editVideoPlaylistTitle,
     5232                                SettingsView:   wp.media.view.Settings.Playlist,
     5233                                library:        options.selection,
     5234                                editing:        options.editing,
     5235                                menu:           'video-playlist',
     5236                                dragInfoText:   l10n.videoPlaylistDragInfo,
     5237                                dragInfo:       false
     5238                        }),
     5239
     5240                        new wp.media.controller.CollectionAdd({
     5241                                type: 'video',
     5242                                collectionType: 'playlist',
     5243                                title: l10n.addToVideoPlaylistTitle
     5244                        })
     5245                ]);
     5246
     5247                if ( wp.media.view.settings.post.featuredImageId ) {
     5248                        this.states.add( new wp.media.controller.FeaturedImage() );
     5249                }
     5250        },
     5251
     5252        bindHandlers: function() {
     5253                var handlers, checkCounts;
     5254
     5255                Select.prototype.bindHandlers.apply( this, arguments );
     5256
     5257                this.on( 'activate', this.activate, this );
     5258
     5259                // Only bother checking media type counts if one of the counts is zero
     5260                checkCounts = _.find( this.counts, function( type ) {
     5261                        return type.count === 0;
     5262                } );
     5263
     5264                if ( typeof checkCounts !== 'undefined' ) {
     5265                        this.listenTo( wp.media.model.Attachments.all, 'change:type', this.mediaTypeCounts );
     5266                }
     5267
     5268                this.on( 'menu:create:gallery', this.createMenu, this );
     5269                this.on( 'menu:create:playlist', this.createMenu, this );
     5270                this.on( 'menu:create:video-playlist', this.createMenu, this );
     5271                this.on( 'toolbar:create:main-insert', this.createToolbar, this );
     5272                this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
     5273                this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
     5274                this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
     5275                this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
     5276                this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
     5277
     5278                handlers = {
     5279                        menu: {
     5280                                'default': 'mainMenu',
     5281                                'gallery': 'galleryMenu',
     5282                                'playlist': 'playlistMenu',
     5283                                'video-playlist': 'videoPlaylistMenu'
     5284                        },
     5285
     5286                        content: {
     5287                                'embed':          'embedContent',
     5288                                'edit-image':     'editImageContent',
     5289                                'edit-selection': 'editSelectionContent'
     5290                        },
     5291
     5292                        toolbar: {
     5293                                'main-insert':      'mainInsertToolbar',
     5294                                'main-gallery':     'mainGalleryToolbar',
     5295                                'gallery-edit':     'galleryEditToolbar',
     5296                                'gallery-add':      'galleryAddToolbar',
     5297                                'main-playlist':        'mainPlaylistToolbar',
     5298                                'playlist-edit':        'playlistEditToolbar',
     5299                                'playlist-add':         'playlistAddToolbar',
     5300                                'main-video-playlist': 'mainVideoPlaylistToolbar',
     5301                                'video-playlist-edit': 'videoPlaylistEditToolbar',
     5302                                'video-playlist-add': 'videoPlaylistAddToolbar'
     5303                        }
     5304                };
     5305
     5306                _.each( handlers, function( regionHandlers, region ) {
     5307                        _.each( regionHandlers, function( callback, handler ) {
     5308                                this.on( region + ':render:' + handler, this[ callback ], this );
     5309                        }, this );
     5310                }, this );
     5311        },
     5312
     5313        activate: function() {
     5314                // Hide menu items for states tied to particular media types if there are no items
     5315                _.each( this.counts, function( type ) {
     5316                        if ( type.count < 1 ) {
     5317                                this.menuItemVisibility( type.state, 'hide' );
     5318                        }
     5319                }, this );
     5320        },
     5321
     5322        mediaTypeCounts: function( model, attr ) {
     5323                if ( typeof this.counts[ attr ] !== 'undefined' && this.counts[ attr ].count < 1 ) {
     5324                        this.counts[ attr ].count++;
     5325                        this.menuItemVisibility( this.counts[ attr ].state, 'show' );
     5326                }
     5327        },
     5328
     5329        // Menus
     5330        /**
     5331         * @param {wp.Backbone.View} view
     5332         */
     5333        mainMenu: function( view ) {
     5334                view.set({
     5335                        'library-separator': new wp.media.View({
     5336                                className: 'separator',
     5337                                priority: 100
     5338                        })
     5339                });
     5340        },
     5341
     5342        menuItemVisibility: function( state, visibility ) {
     5343                var menu = this.menu.get();
     5344                if ( visibility === 'hide' ) {
     5345                        menu.hide( state );
     5346                } else if ( visibility === 'show' ) {
     5347                        menu.show( state );
     5348                }
     5349        },
     5350        /**
     5351         * @param {wp.Backbone.View} view
     5352         */
     5353        galleryMenu: function( view ) {
     5354                var lastState = this.lastState(),
     5355                        previous = lastState && lastState.id,
     5356                        frame = this;
     5357
     5358                view.set({
     5359                        cancel: {
     5360                                text:     l10n.cancelGalleryTitle,
     5361                                priority: 20,
     5362                                click:    function() {
     5363                                        if ( previous ) {
     5364                                                frame.setState( previous );
     5365                                        } else {
     5366                                                frame.close();
     5367                                        }
     5368
     5369                                        // Keep focus inside media modal
     5370                                        // after canceling a gallery
     5371                                        this.controller.modal.focusManager.focus();
     5372                                }
     5373                        },
     5374                        separateCancel: new wp.media.View({
     5375                                className: 'separator',
     5376                                priority: 40
     5377                        })
     5378                });
     5379        },
     5380
     5381        playlistMenu: function( view ) {
     5382                var lastState = this.lastState(),
     5383                        previous = lastState && lastState.id,
     5384                        frame = this;
     5385
     5386                view.set({
     5387                        cancel: {
     5388                                text:     l10n.cancelPlaylistTitle,
     5389                                priority: 20,
     5390                                click:    function() {
     5391                                        if ( previous ) {
     5392                                                frame.setState( previous );
     5393                                        } else {
     5394                                                frame.close();
     5395                                        }
     5396                                }
     5397                        },
     5398                        separateCancel: new wp.media.View({
     5399                                className: 'separator',
     5400                                priority: 40
     5401                        })
     5402                });
     5403        },
     5404
     5405        videoPlaylistMenu: function( view ) {
     5406                var lastState = this.lastState(),
     5407                        previous = lastState && lastState.id,
     5408                        frame = this;
     5409
     5410                view.set({
     5411                        cancel: {
     5412                                text:     l10n.cancelVideoPlaylistTitle,
     5413                                priority: 20,
     5414                                click:    function() {
     5415                                        if ( previous ) {
     5416                                                frame.setState( previous );
     5417                                        } else {
     5418                                                frame.close();
     5419                                        }
     5420                                }
     5421                        },
     5422                        separateCancel: new wp.media.View({
     5423                                className: 'separator',
     5424                                priority: 40
     5425                        })
     5426                });
     5427        },
     5428
     5429        // Content
     5430        embedContent: function() {
     5431                var view = new wp.media.view.Embed({
     5432                        controller: this,
     5433                        model:      this.state()
     5434                }).render();
     5435
     5436                this.content.set( view );
     5437
     5438                if ( ! wp.media.isTouchDevice ) {
     5439                        view.url.focus();
     5440                }
     5441        },
     5442
     5443        editSelectionContent: function() {
     5444                var state = this.state(),
     5445                        selection = state.get('selection'),
     5446                        view;
     5447
     5448                view = new wp.media.view.AttachmentsBrowser({
     5449                        controller: this,
     5450                        collection: selection,
     5451                        selection:  selection,
     5452                        model:      state,
     5453                        sortable:   true,
     5454                        search:     false,
     5455                        date:       false,
     5456                        dragInfo:   true,
     5457
     5458                        AttachmentView: wp.media.view.Attachments.EditSelection
     5459                }).render();
     5460
     5461                view.toolbar.set( 'backToLibrary', {
     5462                        text:     l10n.returnToLibrary,
     5463                        priority: -100,
     5464
     5465                        click: function() {
     5466                                this.controller.content.mode('browse');
     5467                        }
     5468                });
     5469
     5470                // Browse our library of attachments.
     5471                this.content.set( view );
     5472
     5473                // Trigger the controller to set focus
     5474                this.trigger( 'edit:selection', this );
     5475        },
     5476
     5477        editImageContent: function() {
     5478                var image = this.state().get('image'),
     5479                        view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
     5480
     5481                this.content.set( view );
     5482
     5483                // after creating the wrapper view, load the actual editor via an ajax call
     5484                view.loadEditor();
     5485
     5486        },
     5487
     5488        // Toolbars
     5489
     5490        /**
     5491         * @param {wp.Backbone.View} view
     5492         */
     5493        selectionStatusToolbar: function( view ) {
     5494                var editable = this.state().get('editable');
     5495
     5496                view.set( 'selection', new wp.media.view.Selection({
     5497                        controller: this,
     5498                        collection: this.state().get('selection'),
     5499                        priority:   -40,
     5500
     5501                        // If the selection is editable, pass the callback to
     5502                        // switch the content mode.
     5503                        editable: editable && function() {
     5504                                this.controller.content.mode('edit-selection');
     5505                        }
     5506                }).render() );
     5507        },
     5508
     5509        /**
     5510         * @param {wp.Backbone.View} view
     5511         */
     5512        mainInsertToolbar: function( view ) {
     5513                var controller = this;
     5514
     5515                this.selectionStatusToolbar( view );
     5516
     5517                view.set( 'insert', {
     5518                        style:    'primary',
     5519                        priority: 80,
     5520                        text:     l10n.insertIntoPost,
     5521                        requires: { selection: true },
     5522
     5523                        /**
     5524                         * @fires wp.media.controller.State#insert
     5525                         */
     5526                        click: function() {
     5527                                var state = controller.state(),
     5528                                        selection = state.get('selection');
     5529
     5530                                controller.close();
     5531                                state.trigger( 'insert', selection ).reset();
     5532                        }
     5533                });
     5534        },
     5535
     5536        /**
     5537         * @param {wp.Backbone.View} view
     5538         */
     5539        mainGalleryToolbar: function( view ) {
     5540                var controller = this;
     5541
     5542                this.selectionStatusToolbar( view );
     5543
     5544                view.set( 'gallery', {
     5545                        style:    'primary',
     5546                        text:     l10n.createNewGallery,
     5547                        priority: 60,
     5548                        requires: { selection: true },
     5549
     5550                        click: function() {
     5551                                var selection = controller.state().get('selection'),
     5552                                        edit = controller.state('gallery-edit'),
     5553                                        models = selection.where({ type: 'image' });
     5554
     5555                                edit.set( 'library', new wp.media.model.Selection( models, {
     5556                                        props:    selection.props.toJSON(),
     5557                                        multiple: true
     5558                                }) );
     5559
     5560                                this.controller.setState('gallery-edit');
     5561
     5562                                // Keep focus inside media modal
     5563                                // after jumping to gallery view
     5564                                this.controller.modal.focusManager.focus();
     5565                        }
     5566                });
     5567        },
     5568
     5569        mainPlaylistToolbar: function( view ) {
     5570                var controller = this;
     5571
     5572                this.selectionStatusToolbar( view );
     5573
     5574                view.set( 'playlist', {
     5575                        style:    'primary',
     5576                        text:     l10n.createNewPlaylist,
     5577                        priority: 100,
     5578                        requires: { selection: true },
     5579
     5580                        click: function() {
     5581                                var selection = controller.state().get('selection'),
     5582                                        edit = controller.state('playlist-edit'),
     5583                                        models = selection.where({ type: 'audio' });
     5584
     5585                                edit.set( 'library', new wp.media.model.Selection( models, {
     5586                                        props:    selection.props.toJSON(),
     5587                                        multiple: true
     5588                                }) );
     5589
     5590                                this.controller.setState('playlist-edit');
     5591
     5592                                // Keep focus inside media modal
     5593                                // after jumping to playlist view
     5594                                this.controller.modal.focusManager.focus();
     5595                        }
     5596                });
     5597        },
     5598
     5599        mainVideoPlaylistToolbar: function( view ) {
     5600                var controller = this;
     5601
     5602                this.selectionStatusToolbar( view );
     5603
     5604                view.set( 'video-playlist', {
     5605                        style:    'primary',
     5606                        text:     l10n.createNewVideoPlaylist,
     5607                        priority: 100,
     5608                        requires: { selection: true },
     5609
     5610                        click: function() {
     5611                                var selection = controller.state().get('selection'),
     5612                                        edit = controller.state('video-playlist-edit'),
     5613                                        models = selection.where({ type: 'video' });
     5614
     5615                                edit.set( 'library', new wp.media.model.Selection( models, {
     5616                                        props:    selection.props.toJSON(),
     5617                                        multiple: true
     5618                                }) );
     5619
     5620                                this.controller.setState('video-playlist-edit');
     5621
     5622                                // Keep focus inside media modal
     5623                                // after jumping to video playlist view
     5624                                this.controller.modal.focusManager.focus();
     5625                        }
     5626                });
     5627        },
     5628
     5629        featuredImageToolbar: function( toolbar ) {
     5630                this.createSelectToolbar( toolbar, {
     5631                        text:  l10n.setFeaturedImage,
     5632                        state: this.options.state
     5633                });
     5634        },
     5635
     5636        mainEmbedToolbar: function( toolbar ) {
     5637                toolbar.view = new wp.media.view.Toolbar.Embed({
     5638                        controller: this
     5639                });
     5640        },
     5641
     5642        galleryEditToolbar: function() {
     5643                var editing = this.state().get('editing');
     5644                this.toolbar.set( new wp.media.view.Toolbar({
     5645                        controller: this,
     5646                        items: {
     5647                                insert: {
     5648                                        style:    'primary',
     5649                                        text:     editing ? l10n.updateGallery : l10n.insertGallery,
     5650                                        priority: 80,
     5651                                        requires: { library: true },
     5652
     5653                                        /**
     5654                                         * @fires wp.media.controller.State#update
     5655                                         */
     5656                                        click: function() {
     5657                                                var controller = this.controller,
     5658                                                        state = controller.state();
     5659
     5660                                                controller.close();
     5661                                                state.trigger( 'update', state.get('library') );
     5662
     5663                                                // Restore and reset the default state.
     5664                                                controller.setState( controller.options.state );
     5665                                                controller.reset();
     5666                                        }
     5667                                }
     5668                        }
     5669                }) );
     5670        },
     5671
     5672        galleryAddToolbar: function() {
     5673                this.toolbar.set( new wp.media.view.Toolbar({
     5674                        controller: this,
     5675                        items: {
     5676                                insert: {
     5677                                        style:    'primary',
     5678                                        text:     l10n.addToGallery,
     5679                                        priority: 80,
     5680                                        requires: { selection: true },
     5681
     5682                                        /**
     5683                                         * @fires wp.media.controller.State#reset
     5684                                         */
     5685                                        click: function() {
     5686                                                var controller = this.controller,
     5687                                                        state = controller.state(),
     5688                                                        edit = controller.state('gallery-edit');
     5689
     5690                                                edit.get('library').add( state.get('selection').models );
     5691                                                state.trigger('reset');
     5692                                                controller.setState('gallery-edit');
     5693                                        }
     5694                                }
     5695                        }
     5696                }) );
     5697        },
     5698
     5699        playlistEditToolbar: function() {
     5700                var editing = this.state().get('editing');
     5701                this.toolbar.set( new wp.media.view.Toolbar({
     5702                        controller: this,
     5703                        items: {
     5704                                insert: {
     5705                                        style:    'primary',
     5706                                        text:     editing ? l10n.updatePlaylist : l10n.insertPlaylist,
     5707                                        priority: 80,
     5708                                        requires: { library: true },
     5709
     5710                                        /**
     5711                                         * @fires wp.media.controller.State#update
     5712                                         */
     5713                                        click: function() {
     5714                                                var controller = this.controller,
     5715                                                        state = controller.state();
     5716
     5717                                                controller.close();
     5718                                                state.trigger( 'update', state.get('library') );
     5719
     5720                                                // Restore and reset the default state.
     5721                                                controller.setState( controller.options.state );
     5722                                                controller.reset();
     5723                                        }
     5724                                }
     5725                        }
     5726                }) );
     5727        },
     5728
     5729        playlistAddToolbar: function() {
     5730                this.toolbar.set( new wp.media.view.Toolbar({
     5731                        controller: this,
     5732                        items: {
     5733                                insert: {
     5734                                        style:    'primary',
     5735                                        text:     l10n.addToPlaylist,
     5736                                        priority: 80,
     5737                                        requires: { selection: true },
     5738
     5739                                        /**
     5740                                         * @fires wp.media.controller.State#reset
     5741                                         */
     5742                                        click: function() {
     5743                                                var controller = this.controller,
     5744                                                        state = controller.state(),
     5745                                                        edit = controller.state('playlist-edit');
     5746
     5747                                                edit.get('library').add( state.get('selection').models );
     5748                                                state.trigger('reset');
     5749                                                controller.setState('playlist-edit');
     5750                                        }
     5751                                }
     5752                        }
     5753                }) );
     5754        },
     5755
     5756        videoPlaylistEditToolbar: function() {
     5757                var editing = this.state().get('editing');
     5758                this.toolbar.set( new wp.media.view.Toolbar({
     5759                        controller: this,
     5760                        items: {
     5761                                insert: {
     5762                                        style:    'primary',
     5763                                        text:     editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
     5764                                        priority: 140,
     5765                                        requires: { library: true },
     5766
     5767                                        click: function() {
     5768                                                var controller = this.controller,
     5769                                                        state = controller.state(),
     5770                                                        library = state.get('library');
     5771
     5772                                                library.type = 'video';
     5773
     5774                                                controller.close();
     5775                                                state.trigger( 'update', library );
     5776
     5777                                                // Restore and reset the default state.
     5778                                                controller.setState( controller.options.state );
     5779                                                controller.reset();
     5780                                        }
     5781                                }
     5782                        }
     5783                }) );
     5784        },
     5785
     5786        videoPlaylistAddToolbar: function() {
     5787                this.toolbar.set( new wp.media.view.Toolbar({
     5788                        controller: this,
     5789                        items: {
     5790                                insert: {
     5791                                        style:    'primary',
     5792                                        text:     l10n.addToVideoPlaylist,
     5793                                        priority: 140,
     5794                                        requires: { selection: true },
     5795
     5796                                        click: function() {
     5797                                                var controller = this.controller,
     5798                                                        state = controller.state(),
     5799                                                        edit = controller.state('video-playlist-edit');
     5800
     5801                                                edit.get('library').add( state.get('selection').models );
     5802                                                state.trigger('reset');
     5803                                                controller.setState('video-playlist-edit');
     5804                                        }
     5805                                }
     5806                        }
     5807                }) );
     5808        }
     5809});
     5810
     5811module.exports = Post;
     5812
     5813},{}],46:[function(require,module,exports){
     5814/**
     5815 * wp.media.view.MediaFrame.Select
     5816 *
     5817 * A frame for selecting an item or items from the media library.
     5818 *
     5819 * @class
     5820 * @augments wp.media.view.MediaFrame
     5821 * @augments wp.media.view.Frame
     5822 * @augments wp.media.View
     5823 * @augments wp.Backbone.View
     5824 * @augments Backbone.View
     5825 * @mixes wp.media.controller.StateMachine
     5826 */
     5827
     5828var MediaFrame = wp.media.view.MediaFrame,
     5829        l10n = wp.media.view.l10n,
     5830        Select;
     5831
     5832Select = MediaFrame.extend({
     5833        initialize: function() {
     5834                // Call 'initialize' directly on the parent class.
     5835                MediaFrame.prototype.initialize.apply( this, arguments );
     5836
     5837                _.defaults( this.options, {
     5838                        selection: [],
     5839                        library:   {},
     5840                        multiple:  false,
     5841                        state:    'library'
     5842                });
     5843
     5844                this.createSelection();
     5845                this.createStates();
     5846                this.bindHandlers();
     5847        },
     5848
     5849        /**
     5850         * Attach a selection collection to the frame.
     5851         *
     5852         * A selection is a collection of attachments used for a specific purpose
     5853         * by a media frame. e.g. Selecting an attachment (or many) to insert into
     5854         * post content.
     5855         *
     5856         * @see media.model.Selection
     5857         */
     5858        createSelection: function() {
     5859                var selection = this.options.selection;
     5860
     5861                if ( ! (selection instanceof wp.media.model.Selection) ) {
     5862                        this.options.selection = new wp.media.model.Selection( selection, {
     5863                                multiple: this.options.multiple
     5864                        });
     5865                }
     5866
     5867                this._selection = {
     5868                        attachments: new wp.media.model.Attachments(),
     5869                        difference: []
     5870                };
     5871        },
     5872
     5873        /**
     5874         * Create the default states on the frame.
     5875         */
     5876        createStates: function() {
     5877                var options = this.options;
     5878
     5879                if ( this.options.states ) {
     5880                        return;
     5881                }
     5882
     5883                // Add the default states.
     5884                this.states.add([
     5885                        // Main states.
     5886                        new wp.media.controller.Library({
     5887                                library:   wp.media.query( options.library ),
     5888                                multiple:  options.multiple,
     5889                                title:     options.title,
     5890                                priority:  20
     5891                        })
     5892                ]);
     5893        },
     5894
     5895        /**
     5896         * Bind region mode event callbacks.
     5897         *
     5898         * @see media.controller.Region.render
     5899         */
     5900        bindHandlers: function() {
     5901                this.on( 'router:create:browse', this.createRouter, this );
     5902                this.on( 'router:render:browse', this.browseRouter, this );
     5903                this.on( 'content:create:browse', this.browseContent, this );
     5904                this.on( 'content:render:upload', this.uploadContent, this );
     5905                this.on( 'toolbar:create:select', this.createSelectToolbar, this );
     5906        },
     5907
     5908        /**
     5909         * Render callback for the router region in the `browse` mode.
     5910         *
     5911         * @param {wp.media.view.Router} routerView
     5912         */
     5913        browseRouter: function( routerView ) {
     5914                routerView.set({
     5915                        upload: {
     5916                                text:     l10n.uploadFilesTitle,
     5917                                priority: 20
     5918                        },
     5919                        browse: {
     5920                                text:     l10n.mediaLibraryTitle,
     5921                                priority: 40
     5922                        }
     5923                });
     5924        },
     5925
     5926        /**
     5927         * Render callback for the content region in the `browse` mode.
     5928         *
     5929         * @param {wp.media.controller.Region} contentRegion
     5930         */
     5931        browseContent: function( contentRegion ) {
     5932                var state = this.state();
     5933
     5934                this.$el.removeClass('hide-toolbar');
     5935
     5936                // Browse our library of attachments.
     5937                contentRegion.view = new wp.media.view.AttachmentsBrowser({
     5938                        controller: this,
     5939                        collection: state.get('library'),
     5940                        selection:  state.get('selection'),
     5941                        model:      state,
     5942                        sortable:   state.get('sortable'),
     5943                        search:     state.get('searchable'),
     5944                        filters:    state.get('filterable'),
     5945                        date:       state.get('date'),
     5946                        display:    state.has('display') ? state.get('display') : state.get('displaySettings'),
     5947                        dragInfo:   state.get('dragInfo'),
     5948
     5949                        idealColumnWidth: state.get('idealColumnWidth'),
     5950                        suggestedWidth:   state.get('suggestedWidth'),
     5951                        suggestedHeight:  state.get('suggestedHeight'),
     5952
     5953                        AttachmentView: state.get('AttachmentView')
     5954                });
     5955        },
     5956
     5957        /**
     5958         * Render callback for the content region in the `upload` mode.
     5959         */
     5960        uploadContent: function() {
     5961                this.$el.removeClass( 'hide-toolbar' );
     5962                this.content.set( new wp.media.view.UploaderInline({
     5963                        controller: this
     5964                }) );
     5965        },
     5966
     5967        /**
     5968         * Toolbars
     5969         *
     5970         * @param {Object} toolbar
     5971         * @param {Object} [options={}]
     5972         * @this wp.media.controller.Region
     5973         */
     5974        createSelectToolbar: function( toolbar, options ) {
     5975                options = options || this.options.button || {};
     5976                options.controller = this;
     5977
     5978                toolbar.view = new wp.media.view.Toolbar.Select( options );
     5979        }
     5980});
     5981
     5982module.exports = Select;
     5983
     5984},{}],47:[function(require,module,exports){
     5985/**
     5986 * wp.media.view.Iframe
     5987 *
     5988 * @class
     5989 * @augments wp.media.View
     5990 * @augments wp.Backbone.View
     5991 * @augments Backbone.View
     5992 */
     5993var Iframe = wp.media.View.extend({
     5994        className: 'media-iframe',
     5995        /**
     5996         * @returns {wp.media.view.Iframe} Returns itself to allow chaining
     5997         */
     5998        render: function() {
     5999                this.views.detach();
     6000                this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
     6001                this.views.render();
     6002                return this;
     6003        }
     6004});
     6005
     6006module.exports = Iframe;
     6007
     6008},{}],48:[function(require,module,exports){
    84096009/**
    84106010 * wp.media.view.ImageDetails
    … …  
    85746174module.exports = ImageDetails;
    85756175
    8576 
    8577 /***/ }),
    8578 /* 96 */
    8579 /***/ (function(module, exports) {
    8580 
     6176},{}],49:[function(require,module,exports){
    85816177/**
    8582  * wp.media.view.Cropper
    8583  *
    8584  * Uses the imgAreaSelect plugin to allow a user to crop an image.
    8585  *
    8586  * Takes imgAreaSelect options from
    8587  * wp.customize.HeaderControl.calculateImageSelectOptions via
    8588  * wp.customize.HeaderControl.openMM.
     6178 * wp.media.view.Label
     6179 *
     6180 * @class
     6181 * @augments wp.media.View
     6182 * @augments wp.Backbone.View
     6183 * @augments Backbone.View
     6184 */
     6185var Label = wp.media.View.extend({
     6186        tagName: 'label',
     6187        className: 'screen-reader-text',
     6188
     6189        initialize: function() {
     6190                this.value = this.options.value;
     6191        },
     6192
     6193        render: function() {
     6194                this.$el.html( this.value );
     6195
     6196                return this;
     6197        }
     6198});
     6199
     6200module.exports = Label;
     6201
     6202},{}],50:[function(require,module,exports){
     6203/**
     6204 * wp.media.view.MediaFrame
     6205 *
     6206 * The frame used to create the media modal.
     6207 *
     6208 * @class
     6209 * @augments wp.media.view.Frame
     6210 * @augments wp.media.View
     6211 * @augments wp.Backbone.View
     6212 * @augments Backbone.View
     6213 * @mixes wp.media.controller.StateMachine
     6214 */
     6215var Frame = wp.media.view.Frame,
     6216        $ = jQuery,
     6217        MediaFrame;
     6218
     6219MediaFrame = Frame.extend({
     6220        className: 'media-frame',
     6221        template:  wp.template('media-frame'),
     6222        regions:   ['menu','title','content','toolbar','router'],
     6223
     6224        events: {
     6225                'click div.media-frame-title h1': 'toggleMenu'
     6226        },
     6227
     6228        /**
     6229         * @global wp.Uploader
     6230         */
     6231        initialize: function() {
     6232                Frame.prototype.initialize.apply( this, arguments );
     6233
     6234                _.defaults( this.options, {
     6235                        title:    '',
     6236                        modal:    true,
     6237                        uploader: true
     6238                });
     6239
     6240                // Ensure core UI is enabled.
     6241                this.$el.addClass('wp-core-ui');
     6242
     6243                // Initialize modal container view.
     6244                if ( this.options.modal ) {
     6245                        this.modal = new wp.media.view.Modal({
     6246                                controller: this,
     6247                                title:      this.options.title
     6248                        });
     6249
     6250                        this.modal.content( this );
     6251                }
     6252
     6253                // Force the uploader off if the upload limit has been exceeded or
     6254                // if the browser isn't supported.
     6255                if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
     6256                        this.options.uploader = false;
     6257                }
     6258
     6259                // Initialize window-wide uploader.
     6260                if ( this.options.uploader ) {
     6261                        this.uploader = new wp.media.view.UploaderWindow({
     6262                                controller: this,
     6263                                uploader: {
     6264                                        dropzone:  this.modal ? this.modal.$el : this.$el,
     6265                                        container: this.$el
     6266                                }
     6267                        });
     6268                        this.views.set( '.media-frame-uploader', this.uploader );
     6269                }
     6270
     6271                this.on( 'attach', _.bind( this.views.ready, this.views ), this );
     6272
     6273                // Bind default title creation.
     6274                this.on( 'title:create:default', this.createTitle, this );
     6275                this.title.mode('default');
     6276
     6277                this.on( 'title:render', function( view ) {
     6278                        view.$el.append( '<span class="dashicons dashicons-arrow-down"></span>' );
     6279                });
     6280
     6281                // Bind default menu.
     6282                this.on( 'menu:create:default', this.createMenu, this );
     6283        },
     6284        /**
     6285         * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
     6286         */
     6287        render: function() {
     6288                // Activate the default state if no active state exists.
     6289                if ( ! this.state() && this.options.state ) {
     6290                        this.setState( this.options.state );
     6291                }
     6292                /**
     6293                 * call 'render' directly on the parent class
     6294                 */
     6295                return Frame.prototype.render.apply( this, arguments );
     6296        },
     6297        /**
     6298         * @param {Object} title
     6299         * @this wp.media.controller.Region
     6300         */
     6301        createTitle: function( title ) {
     6302                title.view = new wp.media.View({
     6303                        controller: this,
     6304                        tagName: 'h1'
     6305                });
     6306        },
     6307        /**
     6308         * @param {Object} menu
     6309         * @this wp.media.controller.Region
     6310         */
     6311        createMenu: function( menu ) {
     6312                menu.view = new wp.media.view.Menu({
     6313                        controller: this
     6314                });
     6315        },
     6316
     6317        toggleMenu: function() {
     6318                this.$el.find( '.media-menu' ).toggleClass( 'visible' );
     6319        },
     6320
     6321        /**
     6322         * @param {Object} toolbar
     6323         * @this wp.media.controller.Region
     6324         */
     6325        createToolbar: function( toolbar ) {
     6326                toolbar.view = new wp.media.view.Toolbar({
     6327                        controller: this
     6328                });
     6329        },
     6330        /**
     6331         * @param {Object} router
     6332         * @this wp.media.controller.Region
     6333         */
     6334        createRouter: function( router ) {
     6335                router.view = new wp.media.view.Router({
     6336                        controller: this
     6337                });
     6338        },
     6339        /**
     6340         * @param {Object} options
     6341         */
     6342        createIframeStates: function( options ) {
     6343                var settings = wp.media.view.settings,
     6344                        tabs = settings.tabs,
     6345                        tabUrl = settings.tabUrl,
     6346                        $postId;
     6347
     6348                if ( ! tabs || ! tabUrl ) {
     6349                        return;
     6350                }
     6351
     6352                // Add the post ID to the tab URL if it exists.
     6353                $postId = $('#post_ID');
     6354                if ( $postId.length ) {
     6355                        tabUrl += '&post_id=' + $postId.val();
     6356                }
     6357
     6358                // Generate the tab states.
     6359                _.each( tabs, function( title, id ) {
     6360                        this.state( 'iframe:' + id ).set( _.defaults({
     6361                                tab:     id,
     6362                                src:     tabUrl + '&tab=' + id,
     6363                                title:   title,
     6364                                content: 'iframe',
     6365                                menu:    'default'
     6366                        }, options ) );
     6367                }, this );
     6368
     6369                this.on( 'content:create:iframe', this.iframeContent, this );
     6370                this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
     6371                this.on( 'menu:render:default', this.iframeMenu, this );
     6372                this.on( 'open', this.hijackThickbox, this );
     6373                this.on( 'close', this.restoreThickbox, this );
     6374        },
     6375
     6376        /**
     6377         * @param {Object} content
     6378         * @this wp.media.controller.Region
     6379         */
     6380        iframeContent: function( content ) {
     6381                this.$el.addClass('hide-toolbar');
     6382                content.view = new wp.media.view.Iframe({
     6383                        controller: this
     6384                });
     6385        },
     6386
     6387        iframeContentCleanup: function() {
     6388                this.$el.removeClass('hide-toolbar');
     6389        },
     6390
     6391        iframeMenu: function( view ) {
     6392                var views = {};
     6393
     6394                if ( ! view ) {
     6395                        return;
     6396                }
     6397
     6398                _.each( wp.media.view.settings.tabs, function( title, id ) {
     6399                        views[ 'iframe:' + id ] = {
     6400                                text: this.state( 'iframe:' + id ).get('title'),
     6401                                priority: 200
     6402                        };
     6403                }, this );
     6404
     6405                view.set( views );
     6406        },
     6407
     6408        hijackThickbox: function() {
     6409                var frame = this;
     6410
     6411                if ( ! window.tb_remove || this._tb_remove ) {
     6412                        return;
     6413                }
     6414
     6415                this._tb_remove = window.tb_remove;
     6416                window.tb_remove = function() {
     6417                        frame.close();
     6418                        frame.reset();
     6419                        frame.setState( frame.options.state );
     6420                        frame._tb_remove.call( window );
     6421                };
     6422        },
     6423
     6424        restoreThickbox: function() {
     6425                if ( ! this._tb_remove ) {
     6426                        return;
     6427                }
     6428
     6429                window.tb_remove = this._tb_remove;
     6430                delete this._tb_remove;
     6431        }
     6432});
     6433
     6434// Map some of the modal's methods to the frame.
     6435_.each(['open','close','attach','detach','escape'], function( method ) {
     6436        /**
     6437         * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
     6438         */
     6439        MediaFrame.prototype[ method ] = function() {
     6440                if ( this.modal ) {
     6441                        this.modal[ method ].apply( this.modal, arguments );
     6442                }
     6443                return this;
     6444        };
     6445});
     6446
     6447module.exports = MediaFrame;
     6448
     6449},{}],51:[function(require,module,exports){
     6450/**
     6451 * wp.media.view.MenuItem
     6452 *
     6453 * @class
     6454 * @augments wp.media.View
     6455 * @augments wp.Backbone.View
     6456 * @augments Backbone.View
     6457 */
     6458var $ = jQuery,
     6459        MenuItem;
     6460
     6461MenuItem = wp.media.View.extend({
     6462        tagName:   'a',
     6463        className: 'media-menu-item',
     6464
     6465        attributes: {
     6466                href: '#'
     6467        },
     6468
     6469        events: {
     6470                'click': '_click'
     6471        },
     6472        /**
     6473         * @param {Object} event
     6474         */
     6475        _click: function( event ) {
     6476                var clickOverride = this.options.click;
     6477
     6478                if ( event ) {
     6479                        event.preventDefault();
     6480                }
     6481
     6482                if ( clickOverride ) {
     6483                        clickOverride.call( this );
     6484                } else {
     6485                        this.click();
     6486                }
     6487
     6488                // When selecting a tab along the left side,
     6489                // focus should be transferred into the main panel
     6490                if ( ! wp.media.isTouchDevice ) {
     6491                        $('.media-frame-content input').first().focus();
     6492                }
     6493        },
     6494
     6495        click: function() {
     6496                var state = this.options.state;
     6497
     6498                if ( state ) {
     6499                        this.controller.setState( state );
     6500                        this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below
     6501                }
     6502        },
     6503        /**
     6504         * @returns {wp.media.view.MenuItem} returns itself to allow chaining
     6505         */
     6506        render: function() {
     6507                var options = this.options;
     6508
     6509                if ( options.text ) {
     6510                        this.$el.text( options.text );
     6511                } else if ( options.html ) {
     6512                        this.$el.html( options.html );
     6513                }
     6514
     6515                return this;
     6516        }
     6517});
     6518
     6519module.exports = MenuItem;
     6520
     6521},{}],52:[function(require,module,exports){
     6522/**
     6523 * wp.media.view.Menu
     6524 *
     6525 * @class
     6526 * @augments wp.media.view.PriorityList
     6527 * @augments wp.media.View
     6528 * @augments wp.Backbone.View
     6529 * @augments Backbone.View
     6530 */
     6531var MenuItem = wp.media.view.MenuItem,
     6532        PriorityList = wp.media.view.PriorityList,
     6533        Menu;
     6534
     6535Menu = PriorityList.extend({
     6536        tagName:   'div',
     6537        className: 'media-menu',
     6538        property:  'state',
     6539        ItemView:  MenuItem,
     6540        region:    'menu',
     6541
     6542        /* TODO: alternatively hide on any click anywhere
     6543        events: {
     6544                'click': 'click'
     6545        },
     6546
     6547        click: function() {
     6548                this.$el.removeClass( 'visible' );
     6549        },
     6550        */
     6551
     6552        /**
     6553         * @param {Object} options
     6554         * @param {string} id
     6555         * @returns {wp.media.View}
     6556         */
     6557        toView: function( options, id ) {
     6558                options = options || {};
     6559                options[ this.property ] = options[ this.property ] || id;
     6560                return new this.ItemView( options ).render();
     6561        },
     6562
     6563        ready: function() {
     6564                /**
     6565                 * call 'ready' directly on the parent class
     6566                 */
     6567                PriorityList.prototype.ready.apply( this, arguments );
     6568                this.visibility();
     6569        },
     6570
     6571        set: function() {
     6572                /**
     6573                 * call 'set' directly on the parent class
     6574                 */
     6575                PriorityList.prototype.set.apply( this, arguments );
     6576                this.visibility();
     6577        },
     6578
     6579        unset: function() {
     6580                /**
     6581                 * call 'unset' directly on the parent class
     6582                 */
     6583                PriorityList.prototype.unset.apply( this, arguments );
     6584                this.visibility();
     6585        },
     6586
     6587        visibility: function() {
     6588                var region = this.region,
     6589                        view = this.controller[ region ].get(),
     6590                        views = this.views.get(),
     6591                        hide = ! views || views.length < 2;
     6592
     6593                if ( this === view ) {
     6594                        this.controller.$el.toggleClass( 'hide-' + region, hide );
     6595                }
     6596        },
     6597        /**
     6598         * @param {string} id
     6599         */
     6600        select: function( id ) {
     6601                var view = this.get( id );
     6602
     6603                if ( ! view ) {
     6604                        return;
     6605                }
     6606
     6607                this.deselect();
     6608                view.$el.addClass('active');
     6609        },
     6610
     6611        deselect: function() {
     6612                this.$el.children().removeClass('active');
     6613        },
     6614
     6615        hide: function( id ) {
     6616                var view = this.get( id );
     6617
     6618                if ( ! view ) {
     6619                        return;
     6620                }
     6621
     6622                view.$el.addClass('hidden');
     6623        },
     6624
     6625        show: function( id ) {
     6626                var view = this.get( id );
     6627
     6628                if ( ! view ) {
     6629                        return;
     6630                }
     6631
     6632                view.$el.removeClass('hidden');
     6633        }
     6634});
     6635
     6636module.exports = Menu;
     6637
     6638},{}],53:[function(require,module,exports){
     6639/**
     6640 * wp.media.view.Modal
     6641 *
     6642 * A modal view, which the media modal uses as its default container.
     6643 *
     6644 * @class
     6645 * @augments wp.media.View
     6646 * @augments wp.Backbone.View
     6647 * @augments Backbone.View
     6648 */
     6649var $ = jQuery,
     6650        Modal;
     6651
     6652Modal = wp.media.View.extend({
     6653        tagName:  'div',
     6654        template: wp.template('media-modal'),
     6655
     6656        attributes: {
     6657                tabindex: 0
     6658        },
     6659
     6660        events: {
     6661                'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
     6662                'keydown': 'keydown'
     6663        },
     6664
     6665        initialize: function() {
     6666                _.defaults( this.options, {
     6667                        container: document.body,
     6668                        title:     '',
     6669                        propagate: true,
     6670                        freeze:    true
     6671                });
     6672
     6673                this.focusManager = new wp.media.view.FocusManager({
     6674                        el: this.el
     6675                });
     6676        },
     6677        /**
     6678         * @returns {Object}
     6679         */
     6680        prepare: function() {
     6681                return {
     6682                        title: this.options.title
     6683                };
     6684        },
     6685
     6686        /**
     6687         * @returns {wp.media.view.Modal} Returns itself to allow chaining
     6688         */
     6689        attach: function() {
     6690                if ( this.views.attached ) {
     6691                        return this;
     6692                }
     6693
     6694                if ( ! this.views.rendered ) {
     6695                        this.render();
     6696                }
     6697
     6698                this.$el.appendTo( this.options.container );
     6699
     6700                // Manually mark the view as attached and trigger ready.
     6701                this.views.attached = true;
     6702                this.views.ready();
     6703
     6704                return this.propagate('attach');
     6705        },
     6706
     6707        /**
     6708         * @returns {wp.media.view.Modal} Returns itself to allow chaining
     6709         */
     6710        detach: function() {
     6711                if ( this.$el.is(':visible') ) {
     6712                        this.close();
     6713                }
     6714
     6715                this.$el.detach();
     6716                this.views.attached = false;
     6717                return this.propagate('detach');
     6718        },
     6719
     6720        /**
     6721         * @returns {wp.media.view.Modal} Returns itself to allow chaining
     6722         */
     6723        open: function() {
     6724                var $el = this.$el,
     6725                        options = this.options,
     6726                        mceEditor;
     6727
     6728                if ( $el.is(':visible') ) {
     6729                        return this;
     6730                }
     6731
     6732                if ( ! this.views.attached ) {
     6733                        this.attach();
     6734                }
     6735
     6736                // If the `freeze` option is set, record the window's scroll position.
     6737                if ( options.freeze ) {
     6738                        this._freeze = {
     6739                                scrollTop: $( window ).scrollTop()
     6740                        };
     6741                }
     6742
     6743                // Disable page scrolling.
     6744                $( 'body' ).addClass( 'modal-open' );
     6745
     6746                $el.show();
     6747
     6748                // Try to close the onscreen keyboard
     6749                if ( 'ontouchend' in document ) {
     6750                        if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor )  && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
     6751                                mceEditor.iframeElement.focus();
     6752                                mceEditor.iframeElement.blur();
     6753
     6754                                setTimeout( function() {
     6755                                        mceEditor.iframeElement.blur();
     6756                                }, 100 );
     6757                        }
     6758                }
     6759
     6760                this.$el.focus();
     6761
     6762                return this.propagate('open');
     6763        },
     6764
     6765        /**
     6766         * @param {Object} options
     6767         * @returns {wp.media.view.Modal} Returns itself to allow chaining
     6768         */
     6769        close: function( options ) {
     6770                var freeze = this._freeze;
     6771
     6772                if ( ! this.views.attached || ! this.$el.is(':visible') ) {
     6773                        return this;
     6774                }
     6775
     6776                // Enable page scrolling.
     6777                $( 'body' ).removeClass( 'modal-open' );
     6778
     6779                // Hide modal and remove restricted media modal tab focus once it's closed
     6780                this.$el.hide().undelegate( 'keydown' );
     6781
     6782                // Put focus back in useful location once modal is closed
     6783                $('#wpbody-content').focus();
     6784
     6785                this.propagate('close');
     6786
     6787                // If the `freeze` option is set, restore the container's scroll position.
     6788                if ( freeze ) {
     6789                        $( window ).scrollTop( freeze.scrollTop );
     6790                }
     6791
     6792                if ( options && options.escape ) {
     6793                        this.propagate('escape');
     6794                }
     6795
     6796                return this;
     6797        },
     6798        /**
     6799         * @returns {wp.media.view.Modal} Returns itself to allow chaining
     6800         */
     6801        escape: function() {
     6802                return this.close({ escape: true });
     6803        },
     6804        /**
     6805         * @param {Object} event
     6806         */
     6807        escapeHandler: function( event ) {
     6808                event.preventDefault();
     6809                this.escape();
     6810        },
     6811
     6812        /**
     6813         * @param {Array|Object} content Views to register to '.media-modal-content'
     6814         * @returns {wp.media.view.Modal} Returns itself to allow chaining
     6815         */
     6816        content: function( content ) {
     6817                this.views.set( '.media-modal-content', content );
     6818                return this;
     6819        },
     6820
     6821        /**
     6822         * Triggers a modal event and if the `propagate` option is set,
     6823         * forwards events to the modal's controller.
     6824         *
     6825         * @param {string} id
     6826         * @returns {wp.media.view.Modal} Returns itself to allow chaining
     6827         */
     6828        propagate: function( id ) {
     6829                this.trigger( id );
     6830
     6831                if ( this.options.propagate ) {
     6832                        this.controller.trigger( id );
     6833                }
     6834
     6835                return this;
     6836        },
     6837        /**
     6838         * @param {Object} event
     6839         */
     6840        keydown: function( event ) {
     6841                // Close the modal when escape is pressed.
     6842                if ( 27 === event.which && this.$el.is(':visible') ) {
     6843                        this.escape();
     6844                        event.stopImmediatePropagation();
     6845                }
     6846        }
     6847});
     6848
     6849module.exports = Modal;
     6850
     6851},{}],54:[function(require,module,exports){
     6852/**
     6853 * wp.media.view.PriorityList
     6854 *
     6855 * @class
     6856 * @augments wp.media.View
     6857 * @augments wp.Backbone.View
     6858 * @augments Backbone.View
     6859 */
     6860var PriorityList = wp.media.View.extend({
     6861        tagName:   'div',
     6862
     6863        initialize: function() {
     6864                this._views = {};
     6865
     6866                this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
     6867                delete this.options.views;
     6868
     6869                if ( ! this.options.silent ) {
     6870                        this.render();
     6871                }
     6872        },
     6873        /**
     6874         * @param {string} id
     6875         * @param {wp.media.View|Object} view
     6876         * @param {Object} options
     6877         * @returns {wp.media.view.PriorityList} Returns itself to allow chaining
     6878         */
     6879        set: function( id, view, options ) {
     6880                var priority, views, index;
     6881
     6882                options = options || {};
     6883
     6884                // Accept an object with an `id` : `view` mapping.
     6885                if ( _.isObject( id ) ) {
     6886                        _.each( id, function( view, id ) {
     6887                                this.set( id, view );
     6888                        }, this );
     6889                        return this;
     6890                }
     6891
     6892                if ( ! (view instanceof Backbone.View) ) {
     6893                        view = this.toView( view, id, options );
     6894                }
     6895                view.controller = view.controller || this.controller;
     6896
     6897                this.unset( id );
     6898
     6899                priority = view.options.priority || 10;
     6900                views = this.views.get() || [];
     6901
     6902                _.find( views, function( existing, i ) {
     6903                        if ( existing.options.priority > priority ) {
     6904                                index = i;
     6905                                return true;
     6906                        }
     6907                });
     6908
     6909                this._views[ id ] = view;
     6910                this.views.add( view, {
     6911                        at: _.isNumber( index ) ? index : views.length || 0
     6912                });
     6913
     6914                return this;
     6915        },
     6916        /**
     6917         * @param {string} id
     6918         * @returns {wp.media.View}
     6919         */
     6920        get: function( id ) {
     6921                return this._views[ id ];
     6922        },
     6923        /**
     6924         * @param {string} id
     6925         * @returns {wp.media.view.PriorityList}
     6926         */
     6927        unset: function( id ) {
     6928                var view = this.get( id );
     6929
     6930                if ( view ) {
     6931                        view.remove();
     6932                }
     6933
     6934                delete this._views[ id ];
     6935                return this;
     6936        },
     6937        /**
     6938         * @param {Object} options
     6939         * @returns {wp.media.View}
     6940         */
     6941        toView: function( options ) {
     6942                return new wp.media.View( options );
     6943        }
     6944});
     6945
     6946module.exports = PriorityList;
     6947
     6948},{}],55:[function(require,module,exports){
     6949/**
     6950 * wp.media.view.RouterItem
     6951 *
     6952 * @class
     6953 * @augments wp.media.view.MenuItem
     6954 * @augments wp.media.View
     6955 * @augments wp.Backbone.View
     6956 * @augments Backbone.View
     6957 */
     6958var RouterItem = wp.media.view.MenuItem.extend({
     6959        /**
     6960         * On click handler to activate the content region's corresponding mode.
     6961         */
     6962        click: function() {
     6963                var contentMode = this.options.contentMode;
     6964                if ( contentMode ) {
     6965                        this.controller.content.mode( contentMode );
     6966                }
     6967        }
     6968});
     6969
     6970module.exports = RouterItem;
     6971
     6972},{}],56:[function(require,module,exports){
     6973/**
     6974 * wp.media.view.Router
     6975 *
     6976 * @class
     6977 * @augments wp.media.view.Menu
     6978 * @augments wp.media.view.PriorityList
     6979 * @augments wp.media.View
     6980 * @augments wp.Backbone.View
     6981 * @augments Backbone.View
     6982 */
     6983var Menu = wp.media.view.Menu,
     6984        Router;
     6985
     6986Router = Menu.extend({
     6987        tagName:   'div',
     6988        className: 'media-router',
     6989        property:  'contentMode',
     6990        ItemView:  wp.media.view.RouterItem,
     6991        region:    'router',
     6992
     6993        initialize: function() {
     6994                this.controller.on( 'content:render', this.update, this );
     6995                // Call 'initialize' directly on the parent class.
     6996                Menu.prototype.initialize.apply( this, arguments );
     6997        },
     6998
     6999        update: function() {
     7000                var mode = this.controller.content.mode();
     7001                if ( mode ) {
     7002                        this.select( mode );
     7003                }
     7004        }
     7005});
     7006
     7007module.exports = Router;
     7008
     7009},{}],57:[function(require,module,exports){
     7010/**
     7011 * wp.media.view.Search
     7012 *
     7013 * @class
     7014 * @augments wp.media.View
     7015 * @augments wp.Backbone.View
     7016 * @augments Backbone.View
     7017 */
     7018var l10n = wp.media.view.l10n,
     7019        Search;
     7020
     7021Search = wp.media.View.extend({
     7022        tagName:   'input',
     7023        className: 'search',
     7024        id:        'media-search-input',
     7025
     7026        attributes: {
     7027                type:        'search',
     7028                placeholder: l10n.search
     7029        },
     7030
     7031        events: {
     7032                'input':  'search',
     7033                'keyup':  'search',
     7034                'change': 'search',
     7035                'search': 'search'
     7036        },
     7037
     7038        /**
     7039         * @returns {wp.media.view.Search} Returns itself to allow chaining
     7040         */
     7041        render: function() {
     7042                this.el.value = this.model.escape('search');
     7043                return this;
     7044        },
     7045
     7046        search: function( event ) {
     7047                if ( event.target.value ) {
     7048                        this.model.set( 'search', event.target.value );
     7049                } else {
     7050                        this.model.unset('search');
     7051                }
     7052        }
     7053});
     7054
     7055module.exports = Search;
     7056
     7057},{}],58:[function(require,module,exports){
     7058/**
     7059 * wp.media.view.Selection
     7060 *
     7061 * @class
     7062 * @augments wp.media.View
     7063 * @augments wp.Backbone.View
     7064 * @augments Backbone.View
     7065 */
     7066var l10n = wp.media.view.l10n,
     7067        Selection;
     7068
     7069Selection = wp.media.View.extend({
     7070        tagName:   'div',
     7071        className: 'media-selection',
     7072        template:  wp.template('media-selection'),
     7073
     7074        events: {
     7075                'click .edit-selection':  'edit',
     7076                'click .clear-selection': 'clear'
     7077        },
     7078
     7079        initialize: function() {
     7080                _.defaults( this.options, {
     7081                        editable:  false,
     7082                        clearable: true
     7083                });
     7084
     7085                /**
     7086                 * @member {wp.media.view.Attachments.Selection}
     7087                 */
     7088                this.attachments = new wp.media.view.Attachments.Selection({
     7089                        controller: this.controller,
     7090                        collection: this.collection,
     7091                        selection:  this.collection,
     7092                        model:      new Backbone.Model()
     7093                });
     7094
     7095                this.views.set( '.selection-view', this.attachments );
     7096                this.collection.on( 'add remove reset', this.refresh, this );
     7097                this.controller.on( 'content:activate', this.refresh, this );
     7098        },
     7099
     7100        ready: function() {
     7101                this.refresh();
     7102        },
     7103
     7104        refresh: function() {
     7105                // If the selection hasn't been rendered, bail.
     7106                if ( ! this.$el.children().length ) {
     7107                        return;
     7108                }
     7109
     7110                var collection = this.collection,
     7111                        editing = 'edit-selection' === this.controller.content.mode();
     7112
     7113                // If nothing is selected, display nothing.
     7114                this.$el.toggleClass( 'empty', ! collection.length );
     7115                this.$el.toggleClass( 'one', 1 === collection.length );
     7116                this.$el.toggleClass( 'editing', editing );
     7117
     7118                this.$('.count').text( l10n.selected.replace('%d', collection.length) );
     7119        },
     7120
     7121        edit: function( event ) {
     7122                event.preventDefault();
     7123                if ( this.options.editable ) {
     7124                        this.options.editable.call( this, this.collection );
     7125                }
     7126        },
     7127
     7128        clear: function( event ) {
     7129                event.preventDefault();
     7130                this.collection.reset();
     7131
     7132                // Keep focus inside media modal
     7133                // after clear link is selected
     7134                this.controller.modal.focusManager.focus();
     7135        }
     7136});
     7137
     7138module.exports = Selection;
     7139
     7140},{}],59:[function(require,module,exports){
     7141/**
     7142 * wp.media.view.Settings
    85897143 *
    85907144 * @class
    … …  
    85947148 */
    85957149var View = wp.media.View,
    8596         UploaderStatus = wp.media.view.UploaderStatus,
    8597         l10n = wp.media.view.l10n,
    8598         $ = jQuery,
    8599         Cropper;
    8600 
    8601 Cropper = View.extend({
    8602         className: 'crop-content',
    8603         template: wp.template('crop-content'),
     7150        $ = Backbone.$,
     7151        Settings;
     7152
     7153Settings = View.extend({
     7154        events: {
     7155                'click button':    'updateHandler',
     7156                'change input':    'updateHandler',
     7157                'change select':   'updateHandler',
     7158                'change textarea': 'updateHandler'
     7159        },
     7160
    86047161        initialize: function() {
    8605                 _.bindAll(this, 'onImageLoad');
    8606         },
    8607         ready: function() {
    8608                 this.controller.frame.on('content:error:crop', this.onError, this);
    8609                 this.$image = this.$el.find('.crop-image');
    8610                 this.$image.on('load', this.onImageLoad);
    8611                 $(window).on('resize.cropper', _.debounce(this.onImageLoad, 250));
    8612         },
    8613         remove: function() {
    8614                 $(window).off('resize.cropper');
    8615                 this.$el.remove();
    8616                 this.$el.off();
    8617                 View.prototype.remove.apply(this, arguments);
    8618         },
     7162                this.model = this.model || new Backbone.Model();
     7163                this.listenTo( this.model, 'change', this.updateChanges );
     7164        },
     7165
    86197166        prepare: function() {
    8620                 return {
    8621                         title: l10n.cropYourImage,
    8622                         url: this.options.attachment.get('url')
    8623                 };
    8624         },
    8625         onImageLoad: function() {
    8626                 var imgOptions = this.controller.get('imgSelectOptions');
    8627                 if (typeof imgOptions === 'function') {
    8628                         imgOptions = imgOptions(this.options.attachment, this.controller);
    8629                 }
    8630 
    8631                 imgOptions = _.extend(imgOptions, {parent: this.$el});
    8632                 this.trigger('image-loaded');
    8633                 this.controller.imgSelect = this.$image.imgAreaSelect(imgOptions);
    8634         },
    8635         onError: function() {
    8636                 var filename = this.options.attachment.get('filename');
    8637 
    8638                 this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({
    8639                         filename: UploaderStatus.prototype.filename(filename),
    8640                         message: window._wpMediaViewsL10n.cropError
    8641                 }), { at: 0 });
     7167                return _.defaults({
     7168                        model: this.model.toJSON()
     7169                }, this.options );
     7170        },
     7171        /**
     7172         * @returns {wp.media.view.Settings} Returns itself to allow chaining
     7173         */
     7174        render: function() {
     7175                View.prototype.render.apply( this, arguments );
     7176                // Select the correct values.
     7177                _( this.model.attributes ).chain().keys().each( this.update, this );
     7178                return this;
     7179        },
     7180        /**
     7181         * @param {string} key
     7182         */
     7183        update: function( key ) {
     7184                var value = this.model.get( key ),
     7185                        $setting = this.$('[data-setting="' + key + '"]'),
     7186                        $buttons, $value;
     7187
     7188                // Bail if we didn't find a matching setting.
     7189                if ( ! $setting.length ) {
     7190                        return;
     7191                }
     7192
     7193                // Attempt to determine how the setting is rendered and update
     7194                // the selected value.
     7195
     7196                // Handle dropdowns.
     7197                if ( $setting.is('select') ) {
     7198                        $value = $setting.find('[value="' + value + '"]');
     7199
     7200                        if ( $value.length ) {
     7201                                $setting.find('option').prop( 'selected', false );
     7202                                $value.prop( 'selected', true );
     7203                        } else {
     7204                                // If we can't find the desired value, record what *is* selected.
     7205                                this.model.set( key, $setting.find(':selected').val() );
     7206                        }
     7207
     7208                // Handle button groups.
     7209                } else if ( $setting.hasClass('button-group') ) {
     7210                        $buttons = $setting.find('button').removeClass('active');
     7211                        $buttons.filter( '[value="' + value + '"]' ).addClass('active');
     7212
     7213                // Handle text inputs and textareas.
     7214                } else if ( $setting.is('input[type="text"], textarea') ) {
     7215                        if ( ! $setting.is(':focus') ) {
     7216                                $setting.val( value );
     7217                        }
     7218                // Handle checkboxes.
     7219                } else if ( $setting.is('input[type="checkbox"]') ) {
     7220                        $setting.prop( 'checked', !! value && 'false' !== value );
     7221                }
     7222        },
     7223        /**
     7224         * @param {Object} event
     7225         */
     7226        updateHandler: function( event ) {
     7227                var $setting = $( event.target ).closest('[data-setting]'),
     7228                        value = event.target.value,
     7229                        userSetting;
     7230
     7231                event.preventDefault();
     7232
     7233                if ( ! $setting.length ) {
     7234                        return;
     7235                }
     7236
     7237                // Use the correct value for checkboxes.
     7238                if ( $setting.is('input[type="checkbox"]') ) {
     7239                        value = $setting[0].checked;
     7240                }
     7241
     7242                // Update the corresponding setting.
     7243                this.model.set( $setting.data('setting'), value );
     7244
     7245                // If the setting has a corresponding user setting,
     7246                // update that as well.
     7247                if ( userSetting = $setting.data('userSetting') ) {
     7248                        window.setUserSetting( userSetting, value );
     7249                }
     7250        },
     7251
     7252        updateChanges: function( model ) {
     7253                if ( model.hasChanged() ) {
     7254                        _( model.changed ).chain().keys().each( this.update, this );
     7255                }
    86427256        }
    86437257});
    86447258
    8645 module.exports = Cropper;
    8646 
    8647 
    8648 /***/ }),
    8649 /* 97 */
    8650 /***/ (function(module, exports) {
    8651 
     7259module.exports = Settings;
     7260
     7261},{}],60:[function(require,module,exports){
     7262/**
     7263 * wp.media.view.Settings.AttachmentDisplay
     7264 *
     7265 * @class
     7266 * @augments wp.media.view.Settings
     7267 * @augments wp.media.View
     7268 * @augments wp.Backbone.View
     7269 * @augments Backbone.View
     7270 */
     7271var Settings = wp.media.view.Settings,
     7272        AttachmentDisplay;
     7273
     7274AttachmentDisplay = Settings.extend({
     7275        className: 'attachment-display-settings',
     7276        template:  wp.template('attachment-display-settings'),
     7277
     7278        initialize: function() {
     7279                var attachment = this.options.attachment;
     7280
     7281                _.defaults( this.options, {
     7282                        userSettings: false
     7283                });
     7284                // Call 'initialize' directly on the parent class.
     7285                Settings.prototype.initialize.apply( this, arguments );
     7286                this.listenTo( this.model, 'change:link', this.updateLinkTo );
     7287
     7288                if ( attachment ) {
     7289                        attachment.on( 'change:uploading', this.render, this );
     7290                }
     7291        },
     7292
     7293        dispose: function() {
     7294                var attachment = this.options.attachment;
     7295                if ( attachment ) {
     7296                        attachment.off( null, null, this );
     7297                }
     7298                /**
     7299                 * call 'dispose' directly on the parent class
     7300                 */
     7301                Settings.prototype.dispose.apply( this, arguments );
     7302        },
     7303        /**
     7304         * @returns {wp.media.view.AttachmentDisplay} Returns itself to allow chaining
     7305         */
     7306        render: function() {
     7307                var attachment = this.options.attachment;
     7308                if ( attachment ) {
     7309                        _.extend( this.options, {
     7310                                sizes: attachment.get('sizes'),
     7311                                type:  attachment.get('type')
     7312                        });
     7313                }
     7314                /**
     7315                 * call 'render' directly on the parent class
     7316                 */
     7317                Settings.prototype.render.call( this );
     7318                this.updateLinkTo();
     7319                return this;
     7320        },
     7321
     7322        updateLinkTo: function() {
     7323                var linkTo = this.model.get('link'),
     7324                        $input = this.$('.link-to-custom'),
     7325                        attachment = this.options.attachment;
     7326
     7327                if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
     7328                        $input.addClass( 'hidden' );
     7329                        return;
     7330                }
     7331
     7332                if ( attachment ) {
     7333                        if ( 'post' === linkTo ) {
     7334                                $input.val( attachment.get('link') );
     7335                        } else if ( 'file' === linkTo ) {
     7336                                $input.val( attachment.get('url') );
     7337                        } else if ( ! this.model.get('linkUrl') ) {
     7338                                $input.val('http://');
     7339                        }
     7340
     7341                        $input.prop( 'readonly', 'custom' !== linkTo );
     7342                }
     7343
     7344                $input.removeClass( 'hidden' );
     7345
     7346                // If the input is visible, focus and select its contents.
     7347                if ( ! wp.media.isTouchDevice && $input.is(':visible') ) {
     7348                        $input.focus()[0].select();
     7349                }
     7350        }
     7351});
     7352
     7353module.exports = AttachmentDisplay;
     7354
     7355},{}],61:[function(require,module,exports){
     7356/**
     7357 * wp.media.view.Settings.Gallery
     7358 *
     7359 * @class
     7360 * @augments wp.media.view.Settings
     7361 * @augments wp.media.View
     7362 * @augments wp.Backbone.View
     7363 * @augments Backbone.View
     7364 */
     7365var Gallery = wp.media.view.Settings.extend({
     7366        className: 'collection-settings gallery-settings',
     7367        template:  wp.template('gallery-settings')
     7368});
     7369
     7370module.exports = Gallery;
     7371
     7372},{}],62:[function(require,module,exports){
     7373/**
     7374 * wp.media.view.Settings.Playlist
     7375 *
     7376 * @class
     7377 * @augments wp.media.view.Settings
     7378 * @augments wp.media.View
     7379 * @augments wp.Backbone.View
     7380 * @augments Backbone.View
     7381 */
     7382var Playlist = wp.media.view.Settings.extend({
     7383        className: 'collection-settings playlist-settings',
     7384        template:  wp.template('playlist-settings')
     7385});
     7386
     7387module.exports = Playlist;
     7388
     7389},{}],63:[function(require,module,exports){
     7390/**
     7391 * wp.media.view.Sidebar
     7392 *
     7393 * @class
     7394 * @augments wp.media.view.PriorityList
     7395 * @augments wp.media.View
     7396 * @augments wp.Backbone.View
     7397 * @augments Backbone.View
     7398 */
     7399var Sidebar = wp.media.view.PriorityList.extend({
     7400        className: 'media-sidebar'
     7401});
     7402
     7403module.exports = Sidebar;
     7404
     7405},{}],64:[function(require,module,exports){
    86527406/**
    86537407 * wp.media.view.SiteIconCropper
    … …  
    86927446module.exports = SiteIconCropper;
    86937447
    8694 
    8695 /***/ }),
    8696 /* 98 */
    8697 /***/ (function(module, exports) {
    8698 
     7448},{}],65:[function(require,module,exports){
    86997449/**
    87007450 * wp.media.view.SiteIconPreview
    … …  
    87527502module.exports = SiteIconPreview;
    87537503
    8754 
    8755 /***/ }),
    8756 /* 99 */
    8757 /***/ (function(module, exports) {
    8758 
    8759 /**
    8760  * wp.media.view.EditImage
    8761  *
    8762  * @class
    8763  * @augments wp.media.View
    8764  * @augments wp.Backbone.View
    8765  * @augments Backbone.View
    8766  */
    8767 var View = wp.media.View,
    8768         EditImage;
    8769 
    8770 EditImage = View.extend({
    8771         className: 'image-editor',
    8772         template: wp.template('image-editor'),
    8773 
    8774         initialize: function( options ) {
    8775                 this.editor = window.imageEdit;
    8776                 this.controller = options.controller;
    8777                 View.prototype.initialize.apply( this, arguments );
    8778         },
    8779 
    8780         prepare: function() {
    8781                 return this.model.toJSON();
    8782         },
    8783 
    8784         loadEditor: function() {
    8785                 var dfd = this.editor.open( this.model.get('id'), this.model.get('nonces').edit, this );
    8786                 dfd.done( _.bind( this.focus, this ) );
    8787         },
    8788 
    8789         focus: function() {
    8790                 this.$( '.imgedit-submit .button' ).eq( 0 ).focus();
    8791         },
    8792 
    8793         back: function() {
    8794                 var lastState = this.controller.lastState();
    8795                 this.controller.setState( lastState );
    8796         },
    8797 
    8798         refresh: function() {
    8799                 this.model.fetch();
    8800         },
    8801 
    8802         save: function() {
    8803                 var lastState = this.controller.lastState();
    8804 
    8805                 this.model.fetch().done( _.bind( function() {
    8806                         this.controller.setState( lastState );
    8807                 }, this ) );
    8808         }
    8809 
    8810 });
    8811 
    8812 module.exports = EditImage;
    8813 
    8814 
    8815 /***/ }),
    8816 /* 100 */
    8817 /***/ (function(module, exports) {
    8818 
     7504},{}],66:[function(require,module,exports){
    88197505/**
    88207506 * wp.media.view.Spinner
    … …  
    88517537module.exports = Spinner;
    88527538
    8853 
    8854 /***/ })
    8855 /******/ ]));
     7539},{}],67:[function(require,module,exports){
     7540/**
     7541 * wp.media.view.Toolbar
     7542 *
     7543 * A toolbar which consists of a primary and a secondary section. Each sections
     7544 * can be filled with views.
     7545 *
     7546 * @class
     7547 * @augments wp.media.View
     7548 * @augments wp.Backbone.View
     7549 * @augments Backbone.View
     7550 */
     7551var View = wp.media.View,
     7552        Toolbar;
     7553
     7554Toolbar = View.extend({
     7555        tagName:   'div',
     7556        className: 'media-toolbar',
     7557
     7558        initialize: function() {
     7559                var state = this.controller.state(),
     7560                        selection = this.selection = state.get('selection'),
     7561                        library = this.library = state.get('library');
     7562
     7563                this._views = {};
     7564
     7565                // The toolbar is composed of two `PriorityList` views.
     7566                this.primary   = new wp.media.view.PriorityList();
     7567                this.secondary = new wp.media.view.PriorityList();
     7568                this.primary.$el.addClass('media-toolbar-primary search-form');
     7569                this.secondary.$el.addClass('media-toolbar-secondary');
     7570
     7571                this.views.set([ this.secondary, this.primary ]);
     7572
     7573                if ( this.options.items ) {
     7574                        this.set( this.options.items, { silent: true });
     7575                }
     7576
     7577                if ( ! this.options.silent ) {
     7578                        this.render();
     7579                }
     7580
     7581                if ( selection ) {
     7582                        selection.on( 'add remove reset', this.refresh, this );
     7583                }
     7584
     7585                if ( library ) {
     7586                        library.on( 'add remove reset', this.refresh, this );
     7587                }
     7588        },
     7589        /**
     7590         * @returns {wp.media.view.Toolbar} Returns itsef to allow chaining
     7591         */
     7592        dispose: function() {
     7593                if ( this.selection ) {
     7594                        this.selection.off( null, null, this );
     7595                }
     7596
     7597                if ( this.library ) {
     7598                        this.library.off( null, null, this );
     7599                }
     7600                /**
     7601                 * call 'dispose' directly on the parent class
     7602                 */
     7603                return View.prototype.dispose.apply( this, arguments );
     7604        },
     7605
     7606        ready: function() {
     7607                this.refresh();
     7608        },
     7609
     7610        /**
     7611         * @param {string} id
     7612         * @param {Backbone.View|Object} view
     7613         * @param {Object} [options={}]
     7614         * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
     7615         */
     7616        set: function( id, view, options ) {
     7617                var list;
     7618                options = options || {};
     7619
     7620                // Accept an object with an `id` : `view` mapping.
     7621                if ( _.isObject( id ) ) {
     7622                        _.each( id, function( view, id ) {
     7623                                this.set( id, view, { silent: true });
     7624                        }, this );
     7625
     7626                } else {
     7627                        if ( ! ( view instanceof Backbone.View ) ) {
     7628                                view.classes = [ 'media-button-' + id ].concat( view.classes || [] );
     7629                                view = new wp.media.view.Button( view ).render();
     7630                        }
     7631
     7632                        view.controller = view.controller || this.controller;
     7633
     7634                        this._views[ id ] = view;
     7635
     7636                        list = view.options.priority < 0 ? 'secondary' : 'primary';
     7637                        this[ list ].set( id, view, options );
     7638                }
     7639
     7640                if ( ! options.silent ) {
     7641                        this.refresh();
     7642                }
     7643
     7644                return this;
     7645        },
     7646        /**
     7647         * @param {string} id
     7648         * @returns {wp.media.view.Button}
     7649         */
     7650        get: function( id ) {
     7651                return this._views[ id ];
     7652        },
     7653        /**
     7654         * @param {string} id
     7655         * @param {Object} options
     7656         * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
     7657         */
     7658        unset: function( id, options ) {
     7659                delete this._views[ id ];
     7660                this.primary.unset( id, options );
     7661                this.secondary.unset( id, options );
     7662
     7663                if ( ! options || ! options.silent ) {
     7664                        this.refresh();
     7665                }
     7666                return this;
     7667        },
     7668
     7669        refresh: function() {
     7670                var state = this.controller.state(),
     7671                        library = state.get('library'),
     7672                        selection = state.get('selection');
     7673
     7674                _.each( this._views, function( button ) {
     7675                        if ( ! button.model || ! button.options || ! button.options.requires ) {
     7676                                return;
     7677                        }
     7678
     7679                        var requires = button.options.requires,
     7680                                disabled = false;
     7681
     7682                        // Prevent insertion of attachments if any of them are still uploading
     7683                        disabled = _.some( selection.models, function( attachment ) {
     7684                                return attachment.get('uploading') === true;
     7685                        });
     7686
     7687                        if ( requires.selection && selection && ! selection.length ) {
     7688                                disabled = true;
     7689                        } else if ( requires.library && library && ! library.length ) {
     7690                                disabled = true;
     7691                        }
     7692                        button.model.set( 'disabled', disabled );
     7693                });
     7694        }
     7695});
     7696
     7697module.exports = Toolbar;
     7698
     7699},{}],68:[function(require,module,exports){
     7700/**
     7701 * wp.media.view.Toolbar.Embed
     7702 *
     7703 * @class
     7704 * @augments wp.media.view.Toolbar.Select
     7705 * @augments wp.media.view.Toolbar
     7706 * @augments wp.media.View
     7707 * @augments wp.Backbone.View
     7708 * @augments Backbone.View
     7709 */
     7710var Select = wp.media.view.Toolbar.Select,
     7711        l10n = wp.media.view.l10n,
     7712        Embed;
     7713
     7714Embed = Select.extend({
     7715        initialize: function() {
     7716                _.defaults( this.options, {
     7717                        text: l10n.insertIntoPost,
     7718                        requires: false
     7719                });
     7720                // Call 'initialize' directly on the parent class.
     7721                Select.prototype.initialize.apply( this, arguments );
     7722        },
     7723
     7724        refresh: function() {
     7725                var url = this.controller.state().props.get('url');
     7726                this.get('select').model.set( 'disabled', ! url || url === 'http://' );
     7727                /**
     7728                 * call 'refresh' directly on the parent class
     7729                 */
     7730                Select.prototype.refresh.apply( this, arguments );
     7731        }
     7732});
     7733
     7734module.exports = Embed;
     7735
     7736},{}],69:[function(require,module,exports){
     7737/**
     7738 * wp.media.view.Toolbar.Select
     7739 *
     7740 * @class
     7741 * @augments wp.media.view.Toolbar
     7742 * @augments wp.media.View
     7743 * @augments wp.Backbone.View
     7744 * @augments Backbone.View
     7745 */
     7746var Toolbar = wp.media.view.Toolbar,
     7747        l10n = wp.media.view.l10n,
     7748        Select;
     7749
     7750Select = Toolbar.extend({
     7751        initialize: function() {
     7752                var options = this.options;
     7753
     7754                _.bindAll( this, 'clickSelect' );
     7755
     7756                _.defaults( options, {
     7757                        event: 'select',
     7758                        state: false,
     7759                        reset: true,
     7760                        close: true,
     7761                        text:  l10n.select,
     7762
     7763                        // Does the button rely on the selection?
     7764                        requires: {
     7765                                selection: true
     7766                        }
     7767                });
     7768
     7769                options.items = _.defaults( options.items || {}, {
     7770                        select: {
     7771                                style:    'primary',
     7772                                text:     options.text,
     7773                                priority: 80,
     7774                                click:    this.clickSelect,
     7775                                requires: options.requires
     7776                        }
     7777                });
     7778                // Call 'initialize' directly on the parent class.
     7779                Toolbar.prototype.initialize.apply( this, arguments );
     7780        },
     7781
     7782        clickSelect: function() {
     7783                var options = this.options,
     7784                        controller = this.controller;
     7785
     7786                if ( options.close ) {
     7787                        controller.close();
     7788                }
     7789
     7790                if ( options.event ) {
     7791                        controller.state().trigger( options.event );
     7792                }
     7793
     7794                if ( options.state ) {
     7795                        controller.setState( options.state );
     7796                }
     7797
     7798                if ( options.reset ) {
     7799                        controller.reset();
     7800                }
     7801        }
     7802});
     7803
     7804module.exports = Select;
     7805
     7806},{}],70:[function(require,module,exports){
     7807/**
     7808 * Creates a dropzone on WP editor instances (elements with .wp-editor-wrap)
     7809 * and relays drag'n'dropped files to a media workflow.
     7810 *
     7811 * wp.media.view.EditorUploader
     7812 *
     7813 * @class
     7814 * @augments wp.media.View
     7815 * @augments wp.Backbone.View
     7816 * @augments Backbone.View
     7817 */
     7818var View = wp.media.View,
     7819        l10n = wp.media.view.l10n,
     7820        $ = jQuery,
     7821        EditorUploader;
     7822
     7823EditorUploader = View.extend({
     7824        tagName:   'div',
     7825        className: 'uploader-editor',
     7826        template:  wp.template( 'uploader-editor' ),
     7827
     7828        localDrag: false,
     7829        overContainer: false,
     7830        overDropzone: false,
     7831        draggingFile: null,
     7832
     7833        /**
     7834         * Bind drag'n'drop events to callbacks.
     7835         */
     7836        initialize: function() {
     7837                this.initialized = false;
     7838
     7839                // Bail if not enabled or UA does not support drag'n'drop or File API.
     7840                if ( ! window.tinyMCEPreInit || ! window.tinyMCEPreInit.dragDropUpload || ! this.browserSupport() ) {
     7841                        return this;
     7842                }
     7843
     7844                this.$document = $(document);
     7845                this.dropzones = [];
     7846                this.files = [];
     7847
     7848                this.$document.on( 'drop', '.uploader-editor', _.bind( this.drop, this ) );
     7849                this.$document.on( 'dragover', '.uploader-editor', _.bind( this.dropzoneDragover, this ) );
     7850                this.$document.on( 'dragleave', '.uploader-editor', _.bind( this.dropzoneDragleave, this ) );
     7851                this.$document.on( 'click', '.uploader-editor', _.bind( this.click, this ) );
     7852
     7853                this.$document.on( 'dragover', _.bind( this.containerDragover, this ) );
     7854                this.$document.on( 'dragleave', _.bind( this.containerDragleave, this ) );
     7855
     7856                this.$document.on( 'dragstart dragend drop', _.bind( function( event ) {
     7857                        this.localDrag = event.type === 'dragstart';
     7858                }, this ) );
     7859
     7860                this.initialized = true;
     7861                return this;
     7862        },
     7863
     7864        /**
     7865         * Check browser support for drag'n'drop.
     7866         *
     7867         * @return Boolean
     7868         */
     7869        browserSupport: function() {
     7870                var supports = false, div = document.createElement('div');
     7871
     7872                supports = ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div );
     7873                supports = supports && !! ( window.File && window.FileList && window.FileReader );
     7874                return supports;
     7875        },
     7876
     7877        isDraggingFile: function( event ) {
     7878                if ( this.draggingFile !== null ) {
     7879                        return this.draggingFile;
     7880                }
     7881
     7882                if ( _.isUndefined( event.originalEvent ) || _.isUndefined( event.originalEvent.dataTransfer ) ) {
     7883                        return false;
     7884                }
     7885
     7886                this.draggingFile = _.indexOf( event.originalEvent.dataTransfer.types, 'Files' ) > -1 &&
     7887                        _.indexOf( event.originalEvent.dataTransfer.types, 'text/plain' ) === -1;
     7888
     7889                return this.draggingFile;
     7890        },
     7891
     7892        refresh: function( e ) {
     7893                var dropzone_id;
     7894                for ( dropzone_id in this.dropzones ) {
     7895                        // Hide the dropzones only if dragging has left the screen.
     7896                        this.dropzones[ dropzone_id ].toggle( this.overContainer || this.overDropzone );
     7897                }
     7898
     7899                if ( ! _.isUndefined( e ) ) {
     7900                        $( e.target ).closest( '.uploader-editor' ).toggleClass( 'droppable', this.overDropzone );
     7901                }
     7902
     7903                if ( ! this.overContainer && ! this.overDropzone ) {
     7904                        this.draggingFile = null;
     7905                }
     7906
     7907                return this;
     7908        },
     7909
     7910        render: function() {
     7911                if ( ! this.initialized ) {
     7912                        return this;
     7913                }
     7914
     7915                View.prototype.render.apply( this, arguments );
     7916                $( '.wp-editor-wrap' ).each( _.bind( this.attach, this ) );
     7917                return this;
     7918        },
     7919
     7920        attach: function( index, editor ) {
     7921                // Attach a dropzone to an editor.
     7922                var dropzone = this.$el.clone();
     7923                this.dropzones.push( dropzone );
     7924                $( editor ).append( dropzone );
     7925                return this;
     7926        },
     7927
     7928        /**
     7929         * When a file is dropped on the editor uploader, open up an editor media workflow
     7930         * and upload the file immediately.
     7931         *
     7932         * @param  {jQuery.Event} event The 'drop' event.
     7933         */
     7934        drop: function( event ) {
     7935                var $wrap, uploadView;
     7936
     7937                this.containerDragleave( event );
     7938                this.dropzoneDragleave( event );
     7939
     7940                this.files = event.originalEvent.dataTransfer.files;
     7941                if ( this.files.length < 1 ) {
     7942                        return;
     7943                }
     7944
     7945                // Set the active editor to the drop target.
     7946                $wrap = $( event.target ).parents( '.wp-editor-wrap' );
     7947                if ( $wrap.length > 0 && $wrap[0].id ) {
     7948                        window.wpActiveEditor = $wrap[0].id.slice( 3, -5 );
     7949                }
     7950
     7951                if ( ! this.workflow ) {
     7952                        this.workflow = wp.media.editor.open( window.wpActiveEditor, {
     7953                                frame:    'post',
     7954                                state:    'insert',
     7955                                title:    l10n.addMedia,
     7956                                multiple: true
     7957                        });
     7958
     7959                        uploadView = this.workflow.uploader;
     7960
     7961                        if ( uploadView.uploader && uploadView.uploader.ready ) {
     7962                                this.addFiles.apply( this );
     7963                        } else {
     7964                                this.workflow.on( 'uploader:ready', this.addFiles, this );
     7965                        }
     7966                } else {
     7967                        this.workflow.state().reset();
     7968                        this.addFiles.apply( this );
     7969                        this.workflow.open();
     7970                }
     7971
     7972                return false;
     7973        },
     7974
     7975        /**
     7976         * Add the files to the uploader.
     7977         */
     7978        addFiles: function() {
     7979                if ( this.files.length ) {
     7980                        this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) );
     7981                        this.files = [];
     7982                }
     7983                return this;
     7984        },
     7985
     7986        containerDragover: function( event ) {
     7987                if ( this.localDrag || ! this.isDraggingFile( event ) ) {
     7988                        return;
     7989                }
     7990
     7991                this.overContainer = true;
     7992                this.refresh();
     7993        },
     7994
     7995        containerDragleave: function() {
     7996                this.overContainer = false;
     7997
     7998                // Throttle dragleave because it's called when bouncing from some elements to others.
     7999                _.delay( _.bind( this.refresh, this ), 50 );
     8000        },
     8001
     8002        dropzoneDragover: function( event ) {
     8003                if ( this.localDrag || ! this.isDraggingFile( event ) ) {
     8004                        return;
     8005                }
     8006
     8007                this.overDropzone = true;
     8008                this.refresh( event );
     8009                return false;
     8010        },
     8011
     8012        dropzoneDragleave: function( e ) {
     8013                this.overDropzone = false;
     8014                _.delay( _.bind( this.refresh, this, e ), 50 );
     8015        },
     8016
     8017        click: function( e ) {
     8018                // In the rare case where the dropzone gets stuck, hide it on click.
     8019                this.containerDragleave( e );
     8020                this.dropzoneDragleave( e );
     8021                this.localDrag = false;
     8022        }
     8023});
     8024
     8025module.exports = EditorUploader;
     8026
     8027},{}],71:[function(require,module,exports){
     8028/**
     8029 * wp.media.view.UploaderInline
     8030 *
     8031 * The inline uploader that shows up in the 'Upload Files' tab.
     8032 *
     8033 * @class
     8034 * @augments wp.media.View
     8035 * @augments wp.Backbone.View
     8036 * @augments Backbone.View
     8037 */
     8038var View = wp.media.View,
     8039        UploaderInline;
     8040
     8041UploaderInline = View.extend({
     8042        tagName:   'div',
     8043        className: 'uploader-inline',
     8044        template:  wp.template('uploader-inline'),
     8045
     8046        events: {
     8047                'click .close': 'hide'
     8048        },
     8049
     8050        initialize: function() {
     8051                _.defaults( this.options, {
     8052                        message: '',
     8053                        status:  true,
     8054                        canClose: false
     8055                });
     8056
     8057                if ( ! this.options.$browser && this.controller.uploader ) {
     8058                        this.options.$browser = this.controller.uploader.$browser;
     8059                }
     8060
     8061                if ( _.isUndefined( this.options.postId ) ) {
     8062                        this.options.postId = wp.media.view.settings.post.id;
     8063                }
     8064
     8065                if ( this.options.status ) {
     8066                        this.views.set( '.upload-inline-status', new wp.media.view.UploaderStatus({
     8067                                controller: this.controller
     8068                        }) );
     8069                }
     8070        },
     8071
     8072        prepare: function() {
     8073                var suggestedWidth = this.controller.state().get('suggestedWidth'),
     8074                        suggestedHeight = this.controller.state().get('suggestedHeight'),
     8075                        data = {};
     8076
     8077                data.message = this.options.message;
     8078                data.canClose = this.options.canClose;
     8079
     8080                if ( suggestedWidth && suggestedHeight ) {
     8081                        data.suggestedWidth = suggestedWidth;
     8082                        data.suggestedHeight = suggestedHeight;
     8083                }
     8084
     8085                return data;
     8086        },
     8087        /**
     8088         * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
     8089         */
     8090        dispose: function() {
     8091                if ( this.disposing ) {
     8092                        /**
     8093                         * call 'dispose' directly on the parent class
     8094                         */
     8095                        return View.prototype.dispose.apply( this, arguments );
     8096                }
     8097
     8098                // Run remove on `dispose`, so we can be sure to refresh the
     8099                // uploader with a view-less DOM. Track whether we're disposing
     8100                // so we don't trigger an infinite loop.
     8101                this.disposing = true;
     8102                return this.remove();
     8103        },
     8104        /**
     8105         * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
     8106         */
     8107        remove: function() {
     8108                /**
     8109                 * call 'remove' directly on the parent class
     8110                 */
     8111                var result = View.prototype.remove.apply( this, arguments );
     8112
     8113                _.defer( _.bind( this.refresh, this ) );
     8114                return result;
     8115        },
     8116
     8117        refresh: function() {
     8118                var uploader = this.controller.uploader;
     8119
     8120                if ( uploader ) {
     8121                        uploader.refresh();
     8122                }
     8123        },
     8124        /**
     8125         * @returns {wp.media.view.UploaderInline}
     8126         */
     8127        ready: function() {
     8128                var $browser = this.options.$browser,
     8129                        $placeholder;
     8130
     8131                if ( this.controller.uploader ) {
     8132                        $placeholder = this.$('.browser');
     8133
     8134                        // Check if we've already replaced the placeholder.
     8135                        if ( $placeholder[0] === $browser[0] ) {
     8136                                return;
     8137                        }
     8138
     8139                        $browser.detach().text( $placeholder.text() );
     8140                        $browser[0].className = $placeholder[0].className;
     8141                        $placeholder.replaceWith( $browser.show() );
     8142                }
     8143
     8144                this.refresh();
     8145                return this;
     8146        },
     8147        show: function() {
     8148                this.$el.removeClass( 'hidden' );
     8149        },
     8150        hide: function() {
     8151                this.$el.addClass( 'hidden' );
     8152        }
     8153
     8154});
     8155
     8156module.exports = UploaderInline;
     8157
     8158},{}],72:[function(require,module,exports){
     8159/**
     8160 * wp.media.view.UploaderStatusError
     8161 *
     8162 * @class
     8163 * @augments wp.media.View
     8164 * @augments wp.Backbone.View
     8165 * @augments Backbone.View
     8166 */
     8167var UploaderStatusError = wp.media.View.extend({
     8168        className: 'upload-error',
     8169        template:  wp.template('uploader-status-error')
     8170});
     8171
     8172module.exports = UploaderStatusError;
     8173
     8174},{}],73:[function(require,module,exports){
     8175/**
     8176 * wp.media.view.UploaderStatus
     8177 *
     8178 * An uploader status for on-going uploads.
     8179 *
     8180 * @class
     8181 * @augments wp.media.View
     8182 * @augments wp.Backbone.View
     8183 * @augments Backbone.View
     8184 */
     8185var View = wp.media.View,
     8186        UploaderStatus;
     8187
     8188UploaderStatus = View.extend({
     8189        className: 'media-uploader-status',
     8190        template:  wp.template('uploader-status'),
     8191
     8192        events: {
     8193                'click .upload-dismiss-errors': 'dismiss'
     8194        },
     8195
     8196        initialize: function() {
     8197                this.queue = wp.Uploader.queue;
     8198                this.queue.on( 'add remove reset', this.visibility, this );
     8199                this.queue.on( 'add remove reset change:percent', this.progress, this );
     8200                this.queue.on( 'add remove reset change:uploading', this.info, this );
     8201
     8202                this.errors = wp.Uploader.errors;
     8203                this.errors.reset();
     8204                this.errors.on( 'add remove reset', this.visibility, this );
     8205                this.errors.on( 'add', this.error, this );
     8206        },
     8207        /**
     8208         * @global wp.Uploader
     8209         * @returns {wp.media.view.UploaderStatus}
     8210         */
     8211        dispose: function() {
     8212                wp.Uploader.queue.off( null, null, this );
     8213                /**
     8214                 * call 'dispose' directly on the parent class
     8215                 */
     8216                View.prototype.dispose.apply( this, arguments );
     8217                return this;
     8218        },
     8219
     8220        visibility: function() {
     8221                this.$el.toggleClass( 'uploading', !! this.queue.length );
     8222                this.$el.toggleClass( 'errors', !! this.errors.length );
     8223                this.$el.toggle( !! this.queue.length || !! this.errors.length );
     8224        },
     8225
     8226        ready: function() {
     8227                _.each({
     8228                        '$bar':      '.media-progress-bar div',
     8229                        '$index':    '.upload-index',
     8230                        '$total':    '.upload-total',
     8231                        '$filename': '.upload-filename'
     8232                }, function( selector, key ) {
     8233                        this[ key ] = this.$( selector );
     8234                }, this );
     8235
     8236                this.visibility();
     8237                this.progress();
     8238                this.info();
     8239        },
     8240
     8241        progress: function() {
     8242                var queue = this.queue,
     8243                        $bar = this.$bar;
     8244
     8245                if ( ! $bar || ! queue.length ) {
     8246                        return;
     8247                }
     8248
     8249                $bar.width( ( queue.reduce( function( memo, attachment ) {
     8250                        if ( ! attachment.get('uploading') ) {
     8251                                return memo + 100;
     8252                        }
     8253
     8254                        var percent = attachment.get('percent');
     8255                        return memo + ( _.isNumber( percent ) ? percent : 100 );
     8256                }, 0 ) / queue.length ) + '%' );
     8257        },
     8258
     8259        info: function() {
     8260                var queue = this.queue,
     8261                        index = 0, active;
     8262
     8263                if ( ! queue.length ) {
     8264                        return;
     8265                }
     8266
     8267                active = this.queue.find( function( attachment, i ) {
     8268                        index = i;
     8269                        return attachment.get('uploading');
     8270                });
     8271
     8272                this.$index.text( index + 1 );
     8273                this.$total.text( queue.length );
     8274                this.$filename.html( active ? this.filename( active.get('filename') ) : '' );
     8275        },
     8276        /**
     8277         * @param {string} filename
     8278         * @returns {string}
     8279         */
     8280        filename: function( filename ) {
     8281                return _.escape( filename );
     8282        },
     8283        /**
     8284         * @param {Backbone.Model} error
     8285         */
     8286        error: function( error ) {
     8287                this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({
     8288                        filename: this.filename( error.get('file').name ),
     8289                        message:  error.get('message')
     8290                }), { at: 0 });
     8291        },
     8292
     8293        /**
     8294         * @global wp.Uploader
     8295         *
     8296         * @param {Object} event
     8297         */
     8298        dismiss: function( event ) {
     8299                var errors = this.views.get('.upload-errors');
     8300
     8301                event.preventDefault();
     8302
     8303                if ( errors ) {
     8304                        _.invoke( errors, 'remove' );
     8305                }
     8306                wp.Uploader.errors.reset();
     8307        }
     8308});
     8309
     8310module.exports = UploaderStatus;
     8311
     8312},{}],74:[function(require,module,exports){
     8313/**
     8314 * wp.media.view.UploaderWindow
     8315 *
     8316 * An uploader window that allows for dragging and dropping media.
     8317 *
     8318 * @class
     8319 * @augments wp.media.View
     8320 * @augments wp.Backbone.View
     8321 * @augments Backbone.View
     8322 *
     8323 * @param {object} [options]                   Options hash passed to the view.
     8324 * @param {object} [options.uploader]          Uploader properties.
     8325 * @param {jQuery} [options.uploader.browser]
     8326 * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.
     8327 * @param {object} [options.uploader.params]
     8328 */
     8329var $ = jQuery,
     8330        UploaderWindow;
     8331
     8332UploaderWindow = wp.media.View.extend({
     8333        tagName:   'div',
     8334        className: 'uploader-window',
     8335        template:  wp.template('uploader-window'),
     8336
     8337        initialize: function() {
     8338                var uploader;
     8339
     8340                this.$browser = $('<a href="#" class="browser" />').hide().appendTo('body');
     8341
     8342                uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
     8343                        dropzone:  this.$el,
     8344                        browser:   this.$browser,
     8345                        params:    {}
     8346                });
     8347
     8348                // Ensure the dropzone is a jQuery collection.
     8349                if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) {
     8350                        uploader.dropzone = $( uploader.dropzone );
     8351                }
     8352
     8353                this.controller.on( 'activate', this.refresh, this );
     8354
     8355                this.controller.on( 'detach', function() {
     8356                        this.$browser.remove();
     8357                }, this );
     8358        },
     8359
     8360        refresh: function() {
     8361                if ( this.uploader ) {
     8362                        this.uploader.refresh();
     8363                }
     8364        },
     8365
     8366        ready: function() {
     8367                var postId = wp.media.view.settings.post.id,
     8368                        dropzone;
     8369
     8370                // If the uploader already exists, bail.
     8371                if ( this.uploader ) {
     8372                        return;
     8373                }
     8374
     8375                if ( postId ) {
     8376                        this.options.uploader.params.post_id = postId;
     8377                }
     8378                this.uploader = new wp.Uploader( this.options.uploader );
     8379
     8380                dropzone = this.uploader.dropzone;
     8381                dropzone.on( 'dropzone:enter', _.bind( this.show, this ) );
     8382                dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) );
     8383
     8384                $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) );
     8385        },
     8386
     8387        _ready: function() {
     8388                this.controller.trigger( 'uploader:ready' );
     8389        },
     8390
     8391        show: function() {
     8392                var $el = this.$el.show();
     8393
     8394                // Ensure that the animation is triggered by waiting until
     8395                // the transparent element is painted into the DOM.
     8396                _.defer( function() {
     8397                        $el.css({ opacity: 1 });
     8398                });
     8399        },
     8400
     8401        hide: function() {
     8402                var $el = this.$el.css({ opacity: 0 });
     8403
     8404                wp.media.transition( $el ).done( function() {
     8405                        // Transition end events are subject to race conditions.
     8406                        // Make sure that the value is set as intended.
     8407                        if ( '0' === $el.css('opacity') ) {
     8408                                $el.hide();
     8409                        }
     8410                });
     8411
     8412                // https://core.trac.wordpress.org/ticket/27341
     8413                _.delay( function() {
     8414                        if ( '0' === $el.css('opacity') && $el.is(':visible') ) {
     8415                                $el.hide();
     8416                        }
     8417                }, 500 );
     8418        }
     8419});
     8420
     8421module.exports = UploaderWindow;
     8422
     8423},{}],75:[function(require,module,exports){
     8424/**
     8425 * wp.media.View
     8426 *
     8427 * The base view class for media.
     8428 *
     8429 * Undelegating events, removing events from the model, and
     8430 * removing events from the controller mirror the code for
     8431 * `Backbone.View.dispose` in Backbone 0.9.8 development.
     8432 *
     8433 * This behavior has since been removed, and should not be used
     8434 * outside of the media manager.
     8435 *
     8436 * @class
     8437 * @augments wp.Backbone.View
     8438 * @augments Backbone.View
     8439 */
     8440var View = wp.Backbone.View.extend({
     8441        constructor: function( options ) {
     8442                if ( options && options.controller ) {
     8443                        this.controller = options.controller;
     8444                }
     8445                wp.Backbone.View.apply( this, arguments );
     8446        },
     8447        /**
     8448         * @todo The internal comment mentions this might have been a stop-gap
     8449         *       before Backbone 0.9.8 came out. Figure out if Backbone core takes
     8450         *       care of this in Backbone.View now.
     8451         *
     8452         * @returns {wp.media.View} Returns itself to allow chaining
     8453         */
     8454        dispose: function() {
     8455                // Undelegating events, removing events from the model, and
     8456                // removing events from the controller mirror the code for
     8457                // `Backbone.View.dispose` in Backbone 0.9.8 development.
     8458                this.undelegateEvents();
     8459
     8460                if ( this.model && this.model.off ) {
     8461                        this.model.off( null, null, this );
     8462                }
     8463
     8464                if ( this.collection && this.collection.off ) {
     8465                        this.collection.off( null, null, this );
     8466                }
     8467
     8468                // Unbind controller events.
     8469                if ( this.controller && this.controller.off ) {
     8470                        this.controller.off( null, null, this );
     8471                }
     8472
     8473                return this;
     8474        },
     8475        /**
     8476         * @returns {wp.media.View} Returns itself to allow chaining
     8477         */
     8478        remove: function() {
     8479                this.dispose();
     8480                /**
     8481                 * call 'remove' directly on the parent class
     8482                 */
     8483                return wp.Backbone.View.prototype.remove.apply( this, arguments );
     8484        }
     8485});
     8486
     8487module.exports = View;
     8488
     8489},{}]},{},[19]);
Note: See TracChangeset for help on using the changeset viewer.