[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/ -> media-views.js (source)

   1  /******/ (() => { // webpackBootstrap
   2  /******/     var __webpack_modules__ = ({
   3  
   4  /***/ 7145:
   5  /***/ ((module) => {
   6  
   7  var Selection = wp.media.model.Selection,
   8      Library = wp.media.controller.Library,
   9      CollectionAdd;
  10  
  11  /**
  12   * wp.media.controller.CollectionAdd
  13   *
  14   * A state for adding attachments to a collection (e.g. video playlist).
  15   *
  16   * @memberOf wp.media.controller
  17   *
  18   * @class
  19   * @augments wp.media.controller.Library
  20   * @augments wp.media.controller.State
  21   * @augments Backbone.Model
  22   *
  23   * @param {object}                     [attributes]                         The attributes hash passed to the state.
  24   * @param {string}                     [attributes.id=library]              Unique identifier.
  25   * @param {string}                     attributes.title                     Title for the state. Displays in the frame's title region.
  26   * @param {boolean}                    [attributes.multiple=add]            Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
  27   * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
  28   *                                                                          If one is not supplied, a collection of attachments of the specified type will be created.
  29   * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
  30   *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
  31   * @param {string}                     [attributes.menu=gallery]            Initial mode for the menu region.
  32   * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
  33   *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
  34   * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
  35   * @param {string}                     [attributes.toolbar=gallery-add]     Initial mode for the toolbar region.
  36   * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
  37   * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  38   * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
  39   * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  40   * @param {int}                        [attributes.priority=100]            The priority for the state link in the media menu.
  41   * @param {boolean}                    [attributes.syncSelection=false]     Whether the Attachments selection should be persisted from the last state.
  42   *                                                                          Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
  43   * @param {string}                     attributes.type                      The collection's media type. (e.g. 'video').
  44   * @param {string}                     attributes.collectionType            The collection type. (e.g. 'playlist').
  45   */
  46  CollectionAdd = Library.extend(/** @lends wp.media.controller.CollectionAdd.prototype */{
  47      defaults: _.defaults( {
  48          // Selection defaults. @see media.model.Selection
  49          multiple:      'add',
  50          // Attachments browser defaults. @see media.view.AttachmentsBrowser
  51          filterable:    'uploaded',
  52  
  53          priority:      100,
  54          syncSelection: false
  55      }, Library.prototype.defaults ),
  56  
  57      /**
  58       * @since 3.9.0
  59       */
  60      initialize: function() {
  61          var collectionType = this.get('collectionType');
  62  
  63          if ( 'video' === this.get( 'type' ) ) {
  64              collectionType = 'video-' + collectionType;
  65          }
  66  
  67          this.set( 'id', collectionType + '-library' );
  68          this.set( 'toolbar', collectionType + '-add' );
  69          this.set( 'menu', collectionType );
  70  
  71          // If we haven't been provided a `library`, create a `Selection`.
  72          if ( ! this.get('library') ) {
  73              this.set( 'library', wp.media.query({ type: this.get('type') }) );
  74          }
  75          Library.prototype.initialize.apply( this, arguments );
  76      },
  77  
  78      /**
  79       * @since 3.9.0
  80       */
  81      activate: function() {
  82          var library = this.get('library'),
  83              editLibrary = this.get('editLibrary'),
  84              edit = this.frame.state( this.get('collectionType') + '-edit' ).get('library');
  85  
  86          if ( editLibrary && editLibrary !== edit ) {
  87              library.unobserve( editLibrary );
  88          }
  89  
  90          // Accepts attachments that exist in the original library and
  91          // that do not exist in gallery's library.
  92          library.validator = function( attachment ) {
  93              return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
  94          };
  95  
  96          /*
  97           * Reset the library to ensure that all attachments are re-added
  98           * to the collection. Do so silently, as calling `observe` will
  99           * trigger the `reset` event.
 100           */
 101          library.reset( library.mirroring.models, { silent: true });
 102          library.observe( edit );
 103          this.set('editLibrary', edit);
 104  
 105          Library.prototype.activate.apply( this, arguments );
 106      }
 107  });
 108  
 109  module.exports = CollectionAdd;
 110  
 111  
 112  /***/ }),
 113  
 114  /***/ 8612:
 115  /***/ ((module) => {
 116  
 117  var Library = wp.media.controller.Library,
 118      l10n = wp.media.view.l10n,
 119      $ = jQuery,
 120      CollectionEdit;
 121  
 122  /**
 123   * wp.media.controller.CollectionEdit
 124   *
 125   * A state for editing a collection, which is used by audio and video playlists,
 126   * and can be used for other collections.
 127   *
 128   * @memberOf wp.media.controller
 129   *
 130   * @class
 131   * @augments wp.media.controller.Library
 132   * @augments wp.media.controller.State
 133   * @augments Backbone.Model
 134   *
 135   * @param {object}                     [attributes]                      The attributes hash passed to the state.
 136   * @param {string}                     attributes.title                  Title for the state. Displays in the media menu and the frame's title region.
 137   * @param {wp.media.model.Attachments} [attributes.library]              The attachments collection to edit.
 138   *                                                                       If one is not supplied, an empty media.model.Selection collection is created.
 139   * @param {boolean}                    [attributes.multiple=false]       Whether multi-select is enabled.
 140   * @param {string}                     [attributes.content=browse]       Initial mode for the content region.
 141   * @param {string}                     attributes.menu                   Initial mode for the menu region. @todo this needs a better explanation.
 142   * @param {boolean}                    [attributes.searchable=false]     Whether the library is searchable.
 143   * @param {boolean}                    [attributes.sortable=true]        Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
 144   * @param {boolean}                    [attributes.date=true]            Whether to show the date filter in the browser's toolbar.
 145   * @param {boolean}                    [attributes.describe=true]        Whether to offer UI to describe the attachments - e.g. captioning images in a gallery.
 146   * @param {boolean}                    [attributes.dragInfo=true]        Whether to show instructional text about the attachments being sortable.
 147   * @param {boolean}                    [attributes.dragInfoText]         Instructional text about the attachments being sortable.
 148   * @param {int}                        [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
 149   * @param {boolean}                    [attributes.editing=false]        Whether the gallery is being created, or editing an existing instance.
 150   * @param {int}                        [attributes.priority=60]          The priority for the state link in the media menu.
 151   * @param {boolean}                    [attributes.syncSelection=false]  Whether the Attachments selection should be persisted from the last state.
 152   *                                                                       Defaults to false for this state, because the library passed in  *is* the selection.
 153   * @param {view}                       [attributes.SettingsView]         The view to edit the collection instance settings (e.g. Playlist settings with "Show tracklist" checkbox).
 154   * @param {view}                       [attributes.AttachmentView]       The single `Attachment` view to be used in the `Attachments`.
 155   *                                                                       If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
 156   * @param {string}                     attributes.type                   The collection's media type. (e.g. 'video').
 157   * @param {string}                     attributes.collectionType         The collection type. (e.g. 'playlist').
 158   */
 159  CollectionEdit = Library.extend(/** @lends wp.media.controller.CollectionEdit.prototype */{
 160      defaults: {
 161          multiple:         false,
 162          sortable:         true,
 163          date:             false,
 164          searchable:       false,
 165          content:          'browse',
 166          describe:         true,
 167          dragInfo:         true,
 168          idealColumnWidth: 170,
 169          editing:          false,
 170          priority:         60,
 171          SettingsView:     false,
 172          syncSelection:    false
 173      },
 174  
 175      /**
 176       * @since 3.9.0
 177       */
 178      initialize: function() {
 179          var collectionType = this.get('collectionType');
 180  
 181          if ( 'video' === this.get( 'type' ) ) {
 182              collectionType = 'video-' + collectionType;
 183          }
 184  
 185          this.set( 'id', collectionType + '-edit' );
 186          this.set( 'toolbar', collectionType + '-edit' );
 187  
 188          // If we haven't been provided a `library`, create a `Selection`.
 189          if ( ! this.get('library') ) {
 190              this.set( 'library', new wp.media.model.Selection() );
 191          }
 192          // The single `Attachment` view to be used in the `Attachments` view.
 193          if ( ! this.get('AttachmentView') ) {
 194              this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
 195          }
 196          Library.prototype.initialize.apply( this, arguments );
 197      },
 198  
 199      /**
 200       * @since 3.9.0
 201       */
 202      activate: function() {
 203          var library = this.get('library');
 204  
 205          // Limit the library to images only.
 206          library.props.set( 'type', this.get( 'type' ) );
 207  
 208          // Watch for uploaded attachments.
 209          this.get('library').observe( wp.Uploader.queue );
 210  
 211          this.frame.on( 'content:render:browse', this.renderSettings, this );
 212  
 213          Library.prototype.activate.apply( this, arguments );
 214      },
 215  
 216      /**
 217       * @since 3.9.0
 218       */
 219      deactivate: function() {
 220          // Stop watching for uploaded attachments.
 221          this.get('library').unobserve( wp.Uploader.queue );
 222  
 223          this.frame.off( 'content:render:browse', this.renderSettings, this );
 224  
 225          Library.prototype.deactivate.apply( this, arguments );
 226      },
 227  
 228      /**
 229       * Render the collection embed settings view in the browser sidebar.
 230       *
 231       * @todo This is against the pattern elsewhere in media. Typically the frame
 232       *       is responsible for adding region mode callbacks. Explain.
 233       *
 234       * @since 3.9.0
 235       *
 236       * @param {wp.media.view.attachmentsBrowser} The attachments browser view.
 237       */
 238      renderSettings: function( attachmentsBrowserView ) {
 239          var library = this.get('library'),
 240              collectionType = this.get('collectionType'),
 241              dragInfoText = this.get('dragInfoText'),
 242              SettingsView = this.get('SettingsView'),
 243              obj = {};
 244  
 245          if ( ! library || ! attachmentsBrowserView ) {
 246              return;
 247          }
 248  
 249          library[ collectionType ] = library[ collectionType ] || new Backbone.Model();
 250  
 251          obj[ collectionType ] = new SettingsView({
 252              controller: this,
 253              model:      library[ collectionType ],
 254              priority:   40
 255          });
 256  
 257          attachmentsBrowserView.sidebar.set( obj );
 258  
 259          if ( dragInfoText ) {
 260              attachmentsBrowserView.toolbar.set( 'dragInfo', new wp.media.View({
 261                  el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0],
 262                  priority: -40
 263              }) );
 264          }
 265  
 266          // Add the 'Reverse order' button to the toolbar.
 267          attachmentsBrowserView.toolbar.set( 'reverse', {
 268              text:     l10n.reverseOrder,
 269              priority: 80,
 270  
 271              click: function() {
 272                  library.reset( library.toArray().reverse() );
 273              }
 274          });
 275      }
 276  });
 277  
 278  module.exports = CollectionEdit;
 279  
 280  
 281  /***/ }),
 282  
 283  /***/ 5422:
 284  /***/ ((module) => {
 285  
 286  var l10n = wp.media.view.l10n,
 287      Cropper;
 288  
 289  /**
 290   * wp.media.controller.Cropper
 291   *
 292   * A class for cropping an image when called from the header media customization panel.
 293   *
 294   * @memberOf wp.media.controller
 295   *
 296   * @class
 297   * @augments wp.media.controller.State
 298   * @augments Backbone.Model
 299   */
 300  Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Cropper.prototype */{
 301      defaults: {
 302          id:          'cropper',
 303          title:       l10n.cropImage,
 304          // Region mode defaults.
 305          toolbar:     'crop',
 306          content:     'crop',
 307          router:      false,
 308          canSkipCrop: false,
 309  
 310          // Default doCrop Ajax arguments to allow the Customizer (for example) to inject state.
 311          doCropArgs: {}
 312      },
 313  
 314      /**
 315       * Shows the crop image window when called from the Add new image button.
 316       *
 317       * @since 4.2.0
 318       *
 319       * @return {void}
 320       */
 321      activate: function() {
 322          this.frame.on( 'content:create:crop', this.createCropContent, this );
 323          this.frame.on( 'close', this.removeCropper, this );
 324          this.set('selection', new Backbone.Collection(this.frame._selection.single));
 325      },
 326  
 327      /**
 328       * Changes the state of the toolbar window to browse mode.
 329       *
 330       * @since 4.2.0
 331       *
 332       * @return {void}
 333       */
 334      deactivate: function() {
 335          this.frame.toolbar.mode('browse');
 336      },
 337  
 338      /**
 339       * Creates the crop image window.
 340       *
 341       * Initialized when clicking on the Select and Crop button.
 342       *
 343       * @since 4.2.0
 344       *
 345       * @fires crop window
 346       *
 347       * @return {void}
 348       */
 349      createCropContent: function() {
 350          this.cropperView = new wp.media.view.Cropper({
 351              controller: this,
 352              attachment: this.get('selection').first()
 353          });
 354          this.cropperView.on('image-loaded', this.createCropToolbar, this);
 355          this.frame.content.set(this.cropperView);
 356  
 357      },
 358  
 359      /**
 360       * Removes the image selection and closes the cropping window.
 361       *
 362       * @since 4.2.0
 363       *
 364       * @return {void}
 365       */
 366      removeCropper: function() {
 367          this.imgSelect.cancelSelection();
 368          this.imgSelect.setOptions({remove: true});
 369          this.imgSelect.update();
 370          this.cropperView.remove();
 371      },
 372  
 373      /**
 374       * Checks if cropping can be skipped and creates crop toolbar accordingly.
 375       *
 376       * @since 4.2.0
 377       *
 378       * @return {void}
 379       */
 380      createCropToolbar: function() {
 381          var canSkipCrop, hasRequiredAspectRatio, suggestedCropSize, toolbarOptions;
 382  
 383          suggestedCropSize      = this.get( 'suggestedCropSize' );
 384          hasRequiredAspectRatio = this.get( 'hasRequiredAspectRatio' );
 385          canSkipCrop            = this.get( 'canSkipCrop' ) || false;
 386  
 387          toolbarOptions = {
 388              controller: this.frame,
 389              items: {
 390                  insert: {
 391                      style:    'primary',
 392                      text:     l10n.cropImage,
 393                      priority: 80,
 394                      requires: { library: false, selection: false },
 395  
 396                      click: function() {
 397                          var controller = this.controller,
 398                              selection;
 399  
 400                          selection = controller.state().get('selection').first();
 401                          selection.set({cropDetails: controller.state().imgSelect.getSelection()});
 402  
 403                          this.$el.text(l10n.cropping);
 404                          this.$el.attr('disabled', true);
 405  
 406                          controller.state().doCrop( selection ).done( function( croppedImage ) {
 407                              controller.trigger('cropped', croppedImage );
 408                              controller.close();
 409                          }).fail( function() {
 410                              controller.trigger('content:error:crop');
 411                          });
 412                      }
 413                  }
 414              }
 415          };
 416  
 417          if ( canSkipCrop || hasRequiredAspectRatio ) {
 418              _.extend( toolbarOptions.items, {
 419                  skip: {
 420                      style:      'secondary',
 421                      text:       l10n.skipCropping,
 422                      priority:   70,
 423                      requires:   { library: false, selection: false },
 424                      click:      function() {
 425                          var controller = this.controller,
 426                              selection = controller.state().get( 'selection' ).first();
 427  
 428                          controller.state().cropperView.remove();
 429  
 430                          // Apply the suggested crop size.
 431                          if ( hasRequiredAspectRatio && !canSkipCrop ) {
 432                              selection.set({cropDetails: suggestedCropSize});
 433                              controller.state().doCrop( selection ).done( function( croppedImage ) {
 434                                  controller.trigger( 'cropped', croppedImage );
 435                                  controller.close();
 436                              }).fail( function() {
 437                                  controller.trigger( 'content:error:crop' );
 438                              });
 439                              return;
 440                          }
 441  
 442                          // Skip the cropping process.
 443                          controller.trigger( 'skippedcrop', selection );
 444                          controller.close();
 445                      }
 446                  }
 447              });
 448          }
 449  
 450          this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) );
 451      },
 452  
 453      /**
 454       * Creates an object with the image attachment and crop properties.
 455       *
 456       * @since 4.2.0
 457       *
 458       * @return {$.promise} A jQuery promise with the custom header crop details.
 459       */
 460      doCrop: function( attachment ) {
 461          return wp.ajax.post( 'custom-header-crop', _.extend(
 462              {},
 463              this.defaults.doCropArgs,
 464              {
 465                  nonce: attachment.get( 'nonces' ).edit,
 466                  id: attachment.get( 'id' ),
 467                  cropDetails: attachment.get( 'cropDetails' )
 468              }
 469          ) );
 470      }
 471  });
 472  
 473  module.exports = Cropper;
 474  
 475  
 476  /***/ }),
 477  
 478  /***/ 9660:
 479  /***/ ((module) => {
 480  
 481  var Controller = wp.media.controller,
 482      CustomizeImageCropper;
 483  
 484  /**
 485   * A state for cropping an image in the customizer.
 486   *
 487   * @since 4.3.0
 488   *
 489   * @constructs wp.media.controller.CustomizeImageCropper
 490   * @memberOf wp.media.controller
 491   * @augments wp.media.controller.CustomizeImageCropper.Cropper
 492   * @inheritDoc
 493   */
 494  CustomizeImageCropper = Controller.Cropper.extend(/** @lends wp.media.controller.CustomizeImageCropper.prototype */{
 495      /**
 496       * Posts the crop details to the admin.
 497       *
 498       * Uses crop measurements when flexible in both directions.
 499       * Constrains flexible side based on image ratio and size of the fixed side.
 500       *
 501       * @since 4.3.0
 502       *
 503       * @param {Object} attachment The attachment to crop.
 504       *
 505       * @return {$.promise} A jQuery promise that represents the crop image request.
 506       */
 507      doCrop: function( attachment ) {
 508          var cropDetails = attachment.get( 'cropDetails' ),
 509              control = this.get( 'control' ),
 510              ratio = cropDetails.width / cropDetails.height;
 511  
 512          // Use crop measurements when flexible in both directions.
 513          if ( control.params.flex_width && control.params.flex_height ) {
 514              cropDetails.dst_width  = cropDetails.width;
 515              cropDetails.dst_height = cropDetails.height;
 516  
 517          // Constrain flexible side based on image ratio and size of the fixed side.
 518          } else {
 519              cropDetails.dst_width  = control.params.flex_width  ? control.params.height * ratio : control.params.width;
 520              cropDetails.dst_height = control.params.flex_height ? control.params.width  / ratio : control.params.height;
 521          }
 522  
 523          return wp.ajax.post( 'crop-image', {
 524              wp_customize: 'on',
 525              nonce: attachment.get( 'nonces' ).edit,
 526              id: attachment.get( 'id' ),
 527              context: control.id,
 528              cropDetails: cropDetails
 529          } );
 530      }
 531  });
 532  
 533  module.exports = CustomizeImageCropper;
 534  
 535  
 536  /***/ }),
 537  
 538  /***/ 5663:
 539  /***/ ((module) => {
 540  
 541  var l10n = wp.media.view.l10n,
 542      EditImage;
 543  
 544  /**
 545   * wp.media.controller.EditImage
 546   *
 547   * A state for editing (cropping, etc.) an image.
 548   *
 549   * @memberOf wp.media.controller
 550   *
 551   * @class
 552   * @augments wp.media.controller.State
 553   * @augments Backbone.Model
 554   *
 555   * @param {object}                    attributes                      The attributes hash passed to the state.
 556   * @param {wp.media.model.Attachment} attributes.model                The attachment.
 557   * @param {string}                    [attributes.id=edit-image]      Unique identifier.
 558   * @param {string}                    [attributes.title=Edit Image]   Title for the state. Displays in the media menu and the frame's title region.
 559   * @param {string}                    [attributes.content=edit-image] Initial mode for the content region.
 560   * @param {string}                    [attributes.toolbar=edit-image] Initial mode for the toolbar region.
 561   * @param {string}                    [attributes.menu=false]         Initial mode for the menu region.
 562   * @param {string}                    [attributes.url]                Unused. @todo Consider removal.
 563   */
 564  EditImage = wp.media.controller.State.extend(/** @lends wp.media.controller.EditImage.prototype */{
 565      defaults: {
 566          id:      'edit-image',
 567          title:   l10n.editImage,
 568          menu:    false,
 569          toolbar: 'edit-image',
 570          content: 'edit-image',
 571          url:     ''
 572      },
 573  
 574      /**
 575       * Activates a frame for editing a featured image.
 576       *
 577       * @since 3.9.0
 578       *
 579       * @return {void}
 580       */
 581      activate: function() {
 582          this.frame.on( 'toolbar:render:edit-image', _.bind( this.toolbar, this ) );
 583      },
 584  
 585      /**
 586       * Deactivates a frame for editing a featured image.
 587       *
 588       * @since 3.9.0
 589       *
 590       * @return {void}
 591       */
 592      deactivate: function() {
 593          this.frame.off( 'toolbar:render:edit-image' );
 594      },
 595  
 596      /**
 597       * Adds a toolbar with a back button.
 598       *
 599       * When the back button is pressed it checks whether there is a previous state.
 600       * In case there is a previous state it sets that previous state otherwise it
 601       * closes the frame.
 602       *
 603       * @since 3.9.0
 604       *
 605       * @return {void}
 606       */
 607      toolbar: function() {
 608          var frame = this.frame,
 609              lastState = frame.lastState(),
 610              previous = lastState && lastState.id;
 611  
 612          frame.toolbar.set( new wp.media.view.Toolbar({
 613              controller: frame,
 614              items: {
 615                  back: {
 616                      style: 'primary',
 617                      text:     l10n.back,
 618                      priority: 20,
 619                      click:    function() {
 620                          if ( previous ) {
 621                              frame.setState( previous );
 622                          } else {
 623                              frame.close();
 624                          }
 625                      }
 626                  }
 627              }
 628          }) );
 629      }
 630  });
 631  
 632  module.exports = EditImage;
 633  
 634  
 635  /***/ }),
 636  
 637  /***/ 4910:
 638  /***/ ((module) => {
 639  
 640  var l10n = wp.media.view.l10n,
 641      $ = Backbone.$,
 642      Embed;
 643  
 644  /**
 645   * wp.media.controller.Embed
 646   *
 647   * A state for embedding media from a URL.
 648   *
 649   * @memberOf wp.media.controller
 650   *
 651   * @class
 652   * @augments wp.media.controller.State
 653   * @augments Backbone.Model
 654   *
 655   * @param {object} attributes                         The attributes hash passed to the state.
 656   * @param {string} [attributes.id=embed]              Unique identifier.
 657   * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region.
 658   * @param {string} [attributes.content=embed]         Initial mode for the content region.
 659   * @param {string} [attributes.menu=default]          Initial mode for the menu region.
 660   * @param {string} [attributes.toolbar=main-embed]    Initial mode for the toolbar region.
 661   * @param {string} [attributes.menu=false]            Initial mode for the menu region.
 662   * @param {int}    [attributes.priority=120]          The priority for the state link in the media menu.
 663   * @param {string} [attributes.type=link]             The type of embed. Currently only link is supported.
 664   * @param {string} [attributes.url]                   The embed URL.
 665   * @param {object} [attributes.metadata={}]           Properties of the embed, which will override attributes.url if set.
 666   */
 667  Embed = wp.media.controller.State.extend(/** @lends wp.media.controller.Embed.prototype */{
 668      defaults: {
 669          id:       'embed',
 670          title:    l10n.insertFromUrlTitle,
 671          content:  'embed',
 672          menu:     'default',
 673          toolbar:  'main-embed',
 674          priority: 120,
 675          type:     'link',
 676          url:      '',
 677          metadata: {}
 678      },
 679  
 680      // The amount of time used when debouncing the scan.
 681      sensitivity: 400,
 682  
 683      initialize: function(options) {
 684          this.metadata = options.metadata;
 685          this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity );
 686          this.props = new Backbone.Model( this.metadata || { url: '' });
 687          this.props.on( 'change:url', this.debouncedScan, this );
 688          this.props.on( 'change:url', this.refresh, this );
 689          this.on( 'scan', this.scanImage, this );
 690      },
 691  
 692      /**
 693       * Trigger a scan of the embedded URL's content for metadata required to embed.
 694       *
 695       * @fires wp.media.controller.Embed#scan
 696       */
 697      scan: function() {
 698          var scanners,
 699              embed = this,
 700              attributes = {
 701                  type: 'link',
 702                  scanners: []
 703              };
 704  
 705          /*
 706           * Scan is triggered with the list of `attributes` to set on the
 707           * state, useful for the 'type' attribute and 'scanners' attribute,
 708           * an array of promise objects for asynchronous scan operations.
 709           */
 710          if ( this.props.get('url') ) {
 711              this.trigger( 'scan', attributes );
 712          }
 713  
 714          if ( attributes.scanners.length ) {
 715              scanners = attributes.scanners = $.when.apply( $, attributes.scanners );
 716              scanners.always( function() {
 717                  if ( embed.get('scanners') === scanners ) {
 718                      embed.set( 'loading', false );
 719                  }
 720              });
 721          } else {
 722              attributes.scanners = null;
 723          }
 724  
 725          attributes.loading = !! attributes.scanners;
 726          this.set( attributes );
 727      },
 728      /**
 729       * Try scanning the embed as an image to discover its dimensions.
 730       *
 731       * @param {Object} attributes
 732       */
 733      scanImage: function( attributes ) {
 734          var frame = this.frame,
 735              state = this,
 736              url = this.props.get('url'),
 737              image = new Image(),
 738              deferred = $.Deferred();
 739  
 740          attributes.scanners.push( deferred.promise() );
 741  
 742          // Try to load the image and find its width/height.
 743          image.onload = function() {
 744              deferred.resolve();
 745  
 746              if ( state !== frame.state() || url !== state.props.get('url') ) {
 747                  return;
 748              }
 749  
 750              state.set({
 751                  type: 'image'
 752              });
 753  
 754              state.props.set({
 755                  width:  image.width,
 756                  height: image.height
 757              });
 758          };
 759  
 760          image.onerror = deferred.reject;
 761          image.src = url;
 762      },
 763  
 764      refresh: function() {
 765          this.frame.toolbar.get().refresh();
 766      },
 767  
 768      reset: function() {
 769          this.props.clear().set({ url: '' });
 770  
 771          if ( this.active ) {
 772              this.refresh();
 773          }
 774      }
 775  });
 776  
 777  module.exports = Embed;
 778  
 779  
 780  /***/ }),
 781  
 782  /***/ 1169:
 783  /***/ ((module) => {
 784  
 785  var Attachment = wp.media.model.Attachment,
 786      Library = wp.media.controller.Library,
 787      l10n = wp.media.view.l10n,
 788      FeaturedImage;
 789  
 790  /**
 791   * wp.media.controller.FeaturedImage
 792   *
 793   * A state for selecting a featured image for a post.
 794   *
 795   * @memberOf wp.media.controller
 796   *
 797   * @class
 798   * @augments wp.media.controller.Library
 799   * @augments wp.media.controller.State
 800   * @augments Backbone.Model
 801   *
 802   * @param {object}                     [attributes]                          The attributes hash passed to the state.
 803   * @param {string}                     [attributes.id=featured-image]        Unique identifier.
 804   * @param {string}                     [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region.
 805   * @param {wp.media.model.Attachments} [attributes.library]                  The attachments collection to browse.
 806   *                                                                           If one is not supplied, a collection of all images will be created.
 807   * @param {boolean}                    [attributes.multiple=false]           Whether multi-select is enabled.
 808   * @param {string}                     [attributes.content=upload]           Initial mode for the content region.
 809   *                                                                           Overridden by persistent user setting if 'contentUserSetting' is true.
 810   * @param {string}                     [attributes.menu=default]             Initial mode for the menu region.
 811   * @param {string}                     [attributes.router=browse]            Initial mode for the router region.
 812   * @param {string}                     [attributes.toolbar=featured-image]   Initial mode for the toolbar region.
 813   * @param {int}                        [attributes.priority=60]              The priority for the state link in the media menu.
 814   * @param {boolean}                    [attributes.searchable=true]          Whether the library is searchable.
 815   * @param {boolean|string}             [attributes.filterable=false]         Whether the library is filterable, and if so what filters should be shown.
 816   *                                                                           Accepts 'all', 'uploaded', or 'unattached'.
 817   * @param {boolean}                    [attributes.sortable=true]            Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
 818   * @param {boolean}                    [attributes.autoSelect=true]          Whether an uploaded attachment should be automatically added to the selection.
 819   * @param {boolean}                    [attributes.describe=false]           Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
 820   * @param {boolean}                    [attributes.contentUserSetting=true]  Whether the content region's mode should be set and persisted per user.
 821   * @param {boolean}                    [attributes.syncSelection=true]       Whether the Attachments selection should be persisted from the last state.
 822   */
 823  FeaturedImage = Library.extend(/** @lends wp.media.controller.FeaturedImage.prototype */{
 824      defaults: _.defaults({
 825          id:            'featured-image',
 826          title:         l10n.setFeaturedImageTitle,
 827          multiple:      false,
 828          filterable:    'uploaded',
 829          toolbar:       'featured-image',
 830          priority:      60,
 831          syncSelection: true
 832      }, Library.prototype.defaults ),
 833  
 834      /**
 835       * @since 3.5.0
 836       */
 837      initialize: function() {
 838          var library, comparator;
 839  
 840          // If we haven't been provided a `library`, create a `Selection`.
 841          if ( ! this.get('library') ) {
 842              this.set( 'library', wp.media.query({ type: 'image' }) );
 843          }
 844  
 845          Library.prototype.initialize.apply( this, arguments );
 846  
 847          library    = this.get('library');
 848          comparator = library.comparator;
 849  
 850          // Overload the library's comparator to push items that are not in
 851          // the mirrored query to the front of the aggregate collection.
 852          library.comparator = function( a, b ) {
 853              var aInQuery = !! this.mirroring.get( a.cid ),
 854                  bInQuery = !! this.mirroring.get( b.cid );
 855  
 856              if ( ! aInQuery && bInQuery ) {
 857                  return -1;
 858              } else if ( aInQuery && ! bInQuery ) {
 859                  return 1;
 860              } else {
 861                  return comparator.apply( this, arguments );
 862              }
 863          };
 864  
 865          // Add all items in the selection to the library, so any featured
 866          // images that are not initially loaded still appear.
 867          library.observe( this.get('selection') );
 868      },
 869  
 870      /**
 871       * @since 3.5.0
 872       */
 873      activate: function() {
 874          this.frame.on( 'open', this.updateSelection, this );
 875  
 876          Library.prototype.activate.apply( this, arguments );
 877      },
 878  
 879      /**
 880       * @since 3.5.0
 881       */
 882      deactivate: function() {
 883          this.frame.off( 'open', this.updateSelection, this );
 884  
 885          Library.prototype.deactivate.apply( this, arguments );
 886      },
 887  
 888      /**
 889       * @since 3.5.0
 890       */
 891      updateSelection: function() {
 892          var selection = this.get('selection'),
 893              id = wp.media.view.settings.post.featuredImageId,
 894              attachment;
 895  
 896          if ( '' !== id && -1 !== id ) {
 897              attachment = Attachment.get( id );
 898              attachment.fetch();
 899          }
 900  
 901          selection.reset( attachment ? [ attachment ] : [] );
 902      }
 903  });
 904  
 905  module.exports = FeaturedImage;
 906  
 907  
 908  /***/ }),
 909  
 910  /***/ 7127:
 911  /***/ ((module) => {
 912  
 913  var Selection = wp.media.model.Selection,
 914      Library = wp.media.controller.Library,
 915      l10n = wp.media.view.l10n,
 916      GalleryAdd;
 917  
 918  /**
 919   * wp.media.controller.GalleryAdd
 920   *
 921   * A state for selecting more images to add to a gallery.
 922   *
 923   * @since 3.5.0
 924   *
 925   * @class
 926   * @augments wp.media.controller.Library
 927   * @augments wp.media.controller.State
 928   * @augments Backbone.Model
 929   *
 930   * @memberof wp.media.controller
 931   *
 932   * @param {Object}                     [attributes]                         The attributes hash passed to the state.
 933   * @param {string}                     [attributes.id=gallery-library]      Unique identifier.
 934   * @param {string}                     [attributes.title=Add to Gallery]    Title for the state. Displays in the frame's title region.
 935   * @param {boolean}                    [attributes.multiple=add]            Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
 936   * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
 937   *                                                                          If one is not supplied, a collection of all images will be created.
 938   * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
 939   *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
 940   * @param {string}                     [attributes.menu=gallery]            Initial mode for the menu region.
 941   * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
 942   *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
 943   * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
 944   * @param {string}                     [attributes.toolbar=gallery-add]     Initial mode for the toolbar region.
 945   * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
 946   * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
 947   * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
 948   * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
 949   * @param {number}                     [attributes.priority=100]            The priority for the state link in the media menu.
 950   * @param {boolean}                    [attributes.syncSelection=false]     Whether the Attachments selection should be persisted from the last state.
 951   *                                                                          Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
 952   */
 953  GalleryAdd = Library.extend(/** @lends wp.media.controller.GalleryAdd.prototype */{
 954      defaults: _.defaults({
 955          id:            'gallery-library',
 956          title:         l10n.addToGalleryTitle,
 957          multiple:      'add',
 958          filterable:    'uploaded',
 959          menu:          'gallery',
 960          toolbar:       'gallery-add',
 961          priority:      100,
 962          syncSelection: false
 963      }, Library.prototype.defaults ),
 964  
 965      /**
 966       * Initializes the library. Creates a library of images if a library isn't supplied.
 967       *
 968       * @since 3.5.0
 969       *
 970       * @return {void}
 971       */
 972      initialize: function() {
 973          if ( ! this.get('library') ) {
 974              this.set( 'library', wp.media.query({ type: 'image' }) );
 975          }
 976  
 977          Library.prototype.initialize.apply( this, arguments );
 978      },
 979  
 980      /**
 981       * Activates the library.
 982       *
 983       * Removes all event listeners if in edit mode. Creates a validator to check an attachment.
 984       * Resets library and re-enables event listeners. Activates edit mode. Calls the parent's activate method.
 985       *
 986       * @since 3.5.0
 987       *
 988       * @return {void}
 989       */
 990      activate: function() {
 991          var library = this.get('library'),
 992              edit    = this.frame.state('gallery-edit').get('library');
 993  
 994          if ( this.editLibrary && this.editLibrary !== edit ) {
 995              library.unobserve( this.editLibrary );
 996          }
 997  
 998          /*
 999           * Accept attachments that exist in the original library but
1000           * that do not exist in gallery's library yet.
1001           */
1002          library.validator = function( attachment ) {
1003              return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
1004          };
1005  
1006          /*
1007           * Reset the library to ensure that all attachments are re-added
1008           * to the collection. Do so silently, as calling `observe` will
1009           * trigger the `reset` event.
1010           */
1011          library.reset( library.mirroring.models, { silent: true });
1012          library.observe( edit );
1013          this.editLibrary = edit;
1014  
1015          Library.prototype.activate.apply( this, arguments );
1016      }
1017  });
1018  
1019  module.exports = GalleryAdd;
1020  
1021  
1022  /***/ }),
1023  
1024  /***/ 2038:
1025  /***/ ((module) => {
1026  
1027  var Library = wp.media.controller.Library,
1028      l10n = wp.media.view.l10n,
1029      GalleryEdit;
1030  
1031  /**
1032   * wp.media.controller.GalleryEdit
1033   *
1034   * A state for editing a gallery's images and settings.
1035   *
1036   * @since 3.5.0
1037   *
1038   * @class
1039   * @augments wp.media.controller.Library
1040   * @augments wp.media.controller.State
1041   * @augments Backbone.Model
1042   *
1043   * @memberOf wp.media.controller
1044   *
1045   * @param {Object}                     [attributes]                       The attributes hash passed to the state.
1046   * @param {string}                     [attributes.id=gallery-edit]       Unique identifier.
1047   * @param {string}                     [attributes.title=Edit Gallery]    Title for the state. Displays in the frame's title region.
1048   * @param {wp.media.model.Attachments} [attributes.library]               The collection of attachments in the gallery.
1049   *                                                                        If one is not supplied, an empty media.model.Selection collection is created.
1050   * @param {boolean}                    [attributes.multiple=false]        Whether multi-select is enabled.
1051   * @param {boolean}                    [attributes.searchable=false]      Whether the library is searchable.
1052   * @param {boolean}                    [attributes.sortable=true]         Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
1053   * @param {boolean}                    [attributes.date=true]             Whether to show the date filter in the browser's toolbar.
1054   * @param {string|false}               [attributes.content=browse]        Initial mode for the content region.
1055   * @param {string|false}               [attributes.toolbar=image-details] Initial mode for the toolbar region.
1056   * @param {boolean}                    [attributes.describe=true]         Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
1057   * @param {boolean}                    [attributes.displaySettings=true]  Whether to show the attachment display settings interface.
1058   * @param {boolean}                    [attributes.dragInfo=true]         Whether to show instructional text about the attachments being sortable.
1059   * @param {number}                     [attributes.idealColumnWidth=170]  The ideal column width in pixels for attachments.
1060   * @param {boolean}                    [attributes.editing=false]         Whether the gallery is being created, or editing an existing instance.
1061   * @param {number}                     [attributes.priority=60]           The priority for the state link in the media menu.
1062   * @param {boolean}                    [attributes.syncSelection=false]   Whether the Attachments selection should be persisted from the last state.
1063   *                                                                        Defaults to false for this state, because the library passed in  *is* the selection.
1064   * @param {view}                       [attributes.AttachmentView]        The single `Attachment` view to be used in the `Attachments`.
1065   *                                                                        If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
1066   */
1067  GalleryEdit = Library.extend(/** @lends wp.media.controller.GalleryEdit.prototype */{
1068      defaults: {
1069          id:               'gallery-edit',
1070          title:            l10n.editGalleryTitle,
1071          multiple:         false,
1072          searchable:       false,
1073          sortable:         true,
1074          date:             false,
1075          display:          false,
1076          content:          'browse',
1077          toolbar:          'gallery-edit',
1078          describe:         true,
1079          displaySettings:  true,
1080          dragInfo:         true,
1081          idealColumnWidth: 170,
1082          editing:          false,
1083          priority:         60,
1084          syncSelection:    false
1085      },
1086  
1087      /**
1088       * Initializes the library.
1089       *
1090       * Creates a selection if a library isn't supplied and creates an attachment
1091       * view if no attachment view is supplied.
1092       *
1093       * @since 3.5.0
1094       *
1095       * @return {void}
1096       */
1097      initialize: function() {
1098          // If we haven't been provided a `library`, create a `Selection`.
1099          if ( ! this.get('library') ) {
1100              this.set( 'library', new wp.media.model.Selection() );
1101          }
1102  
1103          // The single `Attachment` view to be used in the `Attachments` view.
1104          if ( ! this.get('AttachmentView') ) {
1105              this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
1106          }
1107  
1108          Library.prototype.initialize.apply( this, arguments );
1109      },
1110  
1111      /**
1112       * Activates the library.
1113       *
1114       * Limits the library to images, watches for uploaded attachments. Watches for
1115       * the browse event on the frame and binds it to gallerySettings.
1116       *
1117       * @since 3.5.0
1118       *
1119       * @return {void}
1120       */
1121      activate: function() {
1122          var library = this.get('library');
1123  
1124          // Limit the library to images only.
1125          library.props.set( 'type', 'image' );
1126  
1127          // Watch for uploaded attachments.
1128          this.get('library').observe( wp.Uploader.queue );
1129  
1130          this.frame.on( 'content:render:browse', this.gallerySettings, this );
1131  
1132          Library.prototype.activate.apply( this, arguments );
1133      },
1134  
1135      /**
1136       * Deactivates the library.
1137       *
1138       * Stops watching for uploaded attachments and browse events.
1139       *
1140       * @since 3.5.0
1141       *
1142       * @return {void}
1143       */
1144      deactivate: function() {
1145          // Stop watching for uploaded attachments.
1146          this.get('library').unobserve( wp.Uploader.queue );
1147  
1148          this.frame.off( 'content:render:browse', this.gallerySettings, this );
1149  
1150          Library.prototype.deactivate.apply( this, arguments );
1151      },
1152  
1153      /**
1154       * Adds the gallery settings to the sidebar and adds a reverse button to the
1155       * toolbar.
1156       *
1157       * @since 3.5.0
1158       *
1159       * @param {wp.media.view.Frame} browser The file browser.
1160       *
1161       * @return {void}
1162       */
1163      gallerySettings: function( browser ) {
1164          if ( ! this.get('displaySettings') ) {
1165              return;
1166          }
1167  
1168          var library = this.get('library');
1169  
1170          if ( ! library || ! browser ) {
1171              return;
1172          }
1173  
1174          library.gallery = library.gallery || new Backbone.Model();
1175  
1176          browser.sidebar.set({
1177              gallery: new wp.media.view.Settings.Gallery({
1178                  controller: this,
1179                  model:      library.gallery,
1180                  priority:   40
1181              })
1182          });
1183  
1184          browser.toolbar.set( 'reverse', {
1185              text:     l10n.reverseOrder,
1186              priority: 80,
1187  
1188              click: function() {
1189                  library.reset( library.toArray().reverse() );
1190              }
1191          });
1192      }
1193  });
1194  
1195  module.exports = GalleryEdit;
1196  
1197  
1198  /***/ }),
1199  
1200  /***/ 705:
1201  /***/ ((module) => {
1202  
1203  var State = wp.media.controller.State,
1204      Library = wp.media.controller.Library,
1205      l10n = wp.media.view.l10n,
1206      ImageDetails;
1207  
1208  /**
1209   * wp.media.controller.ImageDetails
1210   *
1211   * A state for editing the attachment display settings of an image that's been
1212   * inserted into the editor.
1213   *
1214   * @memberOf wp.media.controller
1215   *
1216   * @class
1217   * @augments wp.media.controller.State
1218   * @augments Backbone.Model
1219   *
1220   * @param {object}                    [attributes]                       The attributes hash passed to the state.
1221   * @param {string}                    [attributes.id=image-details]      Unique identifier.
1222   * @param {string}                    [attributes.title=Image Details]   Title for the state. Displays in the frame's title region.
1223   * @param {wp.media.model.Attachment} attributes.image                   The image's model.
1224   * @param {string|false}              [attributes.content=image-details] Initial mode for the content region.
1225   * @param {string|false}              [attributes.menu=false]            Initial mode for the menu region.
1226   * @param {string|false}              [attributes.router=false]          Initial mode for the router region.
1227   * @param {string|false}              [attributes.toolbar=image-details] Initial mode for the toolbar region.
1228   * @param {boolean}                   [attributes.editing=false]         Unused.
1229   * @param {int}                       [attributes.priority=60]           Unused.
1230   *
1231   * @todo This state inherits some defaults from media.controller.Library.prototype.defaults,
1232   *       however this may not do anything.
1233   */
1234  ImageDetails = State.extend(/** @lends wp.media.controller.ImageDetails.prototype */{
1235      defaults: _.defaults({
1236          id:       'image-details',
1237          title:    l10n.imageDetailsTitle,
1238          content:  'image-details',
1239          menu:     false,
1240          router:   false,
1241          toolbar:  'image-details',
1242          editing:  false,
1243          priority: 60
1244      }, Library.prototype.defaults ),
1245  
1246      /**
1247       * @since 3.9.0
1248       *
1249       * @param options Attributes
1250       */
1251      initialize: function( options ) {
1252          this.image = options.image;
1253          State.prototype.initialize.apply( this, arguments );
1254      },
1255  
1256      /**
1257       * @since 3.9.0
1258       */
1259      activate: function() {
1260          this.frame.modal.$el.addClass('image-details');
1261      }
1262  });
1263  
1264  module.exports = ImageDetails;
1265  
1266  
1267  /***/ }),
1268  
1269  /***/ 472:
1270  /***/ ((module) => {
1271  
1272  var l10n = wp.media.view.l10n,
1273      getUserSetting = window.getUserSetting,
1274      setUserSetting = window.setUserSetting,
1275      Library;
1276  
1277  /**
1278   * wp.media.controller.Library
1279   *
1280   * A state for choosing an attachment or group of attachments from the media library.
1281   *
1282   * @memberOf wp.media.controller
1283   *
1284   * @class
1285   * @augments wp.media.controller.State
1286   * @augments Backbone.Model
1287   * @mixes media.selectionSync
1288   *
1289   * @param {object}                          [attributes]                         The attributes hash passed to the state.
1290   * @param {string}                          [attributes.id=library]              Unique identifier.
1291   * @param {string}                          [attributes.title=Media library]     Title for the state. Displays in the media menu and the frame's title region.
1292   * @param {wp.media.model.Attachments}      [attributes.library]                 The attachments collection to browse.
1293   *                                                                               If one is not supplied, a collection of all attachments will be created.
1294   * @param {wp.media.model.Selection|object} [attributes.selection]               A collection to contain attachment selections within the state.
1295   *                                                                               If the 'selection' attribute is a plain JS object,
1296   *                                                                               a Selection will be created using its values as the selection instance's `props` model.
1297   *                                                                               Otherwise, it will copy the library's `props` model.
1298   * @param {boolean}                         [attributes.multiple=false]          Whether multi-select is enabled.
1299   * @param {string}                          [attributes.content=upload]          Initial mode for the content region.
1300   *                                                                               Overridden by persistent user setting if 'contentUserSetting' is true.
1301   * @param {string}                          [attributes.menu=default]            Initial mode for the menu region.
1302   * @param {string}                          [attributes.router=browse]           Initial mode for the router region.
1303   * @param {string}                          [attributes.toolbar=select]          Initial mode for the toolbar region.
1304   * @param {boolean}                         [attributes.searchable=true]         Whether the library is searchable.
1305   * @param {boolean|string}                  [attributes.filterable=false]        Whether the library is filterable, and if so what filters should be shown.
1306   *                                                                               Accepts 'all', 'uploaded', or 'unattached'.
1307   * @param {boolean}                         [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
1308   * @param {boolean}                         [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
1309   * @param {boolean}                         [attributes.describe=false]          Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
1310   * @param {boolean}                         [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
1311   * @param {boolean}                         [attributes.syncSelection=true]      Whether the Attachments selection should be persisted from the last state.
1312   */
1313  Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Library.prototype */{
1314      defaults: {
1315          id:                 'library',
1316          title:              l10n.mediaLibraryTitle,
1317          multiple:           false,
1318          content:            'upload',
1319          menu:               'default',
1320          router:             'browse',
1321          toolbar:            'select',
1322          searchable:         true,
1323          filterable:         false,
1324          sortable:           true,
1325          autoSelect:         true,
1326          describe:           false,
1327          contentUserSetting: true,
1328          syncSelection:      true
1329      },
1330  
1331      /**
1332       * If a library isn't provided, query all media items.
1333       * If a selection instance isn't provided, create one.
1334       *
1335       * @since 3.5.0
1336       */
1337      initialize: function() {
1338          var selection = this.get('selection'),
1339              props;
1340  
1341          if ( ! this.get('library') ) {
1342              this.set( 'library', wp.media.query() );
1343          }
1344  
1345          if ( ! ( selection instanceof wp.media.model.Selection ) ) {
1346              props = selection;
1347  
1348              if ( ! props ) {
1349                  props = this.get('library').props.toJSON();
1350                  props = _.omit( props, 'orderby', 'query' );
1351              }
1352  
1353              this.set( 'selection', new wp.media.model.Selection( null, {
1354                  multiple: this.get('multiple'),
1355                  props: props
1356              }) );
1357          }
1358  
1359          this.resetDisplays();
1360      },
1361  
1362      /**
1363       * @since 3.5.0
1364       */
1365      activate: function() {
1366          this.syncSelection();
1367  
1368          wp.Uploader.queue.on( 'add', this.uploading, this );
1369  
1370          this.get('selection').on( 'add remove reset', this.refreshContent, this );
1371  
1372          if ( this.get( 'router' ) && this.get('contentUserSetting') ) {
1373              this.frame.on( 'content:activate', this.saveContentMode, this );
1374              this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) );
1375          }
1376      },
1377  
1378      /**
1379       * @since 3.5.0
1380       */
1381      deactivate: function() {
1382          this.recordSelection();
1383  
1384          this.frame.off( 'content:activate', this.saveContentMode, this );
1385  
1386          // Unbind all event handlers that use this state as the context
1387          // from the selection.
1388          this.get('selection').off( null, null, this );
1389  
1390          wp.Uploader.queue.off( null, null, this );
1391      },
1392  
1393      /**
1394       * Reset the library to its initial state.
1395       *
1396       * @since 3.5.0
1397       */
1398      reset: function() {
1399          this.get('selection').reset();
1400          this.resetDisplays();
1401          this.refreshContent();
1402      },
1403  
1404      /**
1405       * Reset the attachment display settings defaults to the site options.
1406       *
1407       * If site options don't define them, fall back to a persistent user setting.
1408       *
1409       * @since 3.5.0
1410       */
1411      resetDisplays: function() {
1412          var defaultProps = wp.media.view.settings.defaultProps;
1413          this._displays = [];
1414          this._defaultDisplaySettings = {
1415              align: getUserSetting( 'align', defaultProps.align ) || 'none',
1416              size:  getUserSetting( 'imgsize', defaultProps.size ) || 'medium',
1417              link:  getUserSetting( 'urlbutton', defaultProps.link ) || 'none'
1418          };
1419      },
1420  
1421      /**
1422       * Create a model to represent display settings (alignment, etc.) for an attachment.
1423       *
1424       * @since 3.5.0
1425       *
1426       * @param {wp.media.model.Attachment} attachment
1427       * @return {Backbone.Model}
1428       */
1429      display: function( attachment ) {
1430          var displays = this._displays;
1431  
1432          if ( ! displays[ attachment.cid ] ) {
1433              displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) );
1434          }
1435          return displays[ attachment.cid ];
1436      },
1437  
1438      /**
1439       * Given an attachment, create attachment display settings properties.
1440       *
1441       * @since 3.6.0
1442       *
1443       * @param {wp.media.model.Attachment} attachment
1444       * @return {Object}
1445       */
1446      defaultDisplaySettings: function( attachment ) {
1447          var settings = _.clone( this._defaultDisplaySettings );
1448  
1449          settings.canEmbed = this.canEmbed( attachment );
1450          if ( settings.canEmbed ) {
1451              settings.link = 'embed';
1452          } else if ( ! this.isImageAttachment( attachment ) && settings.link === 'none' ) {
1453              settings.link = 'file';
1454          }
1455  
1456          return settings;
1457      },
1458  
1459      /**
1460       * Whether an attachment is image.
1461       *
1462       * @since 4.4.1
1463       *
1464       * @param {wp.media.model.Attachment} attachment
1465       * @return {boolean}
1466       */
1467      isImageAttachment: function( attachment ) {
1468          // If uploading, we know the filename but not the mime type.
1469          if ( attachment.get('uploading') ) {
1470              return /\.(jpe?g|png|gif|webp|avif|heic|heif)$/i.test( attachment.get('filename') );
1471          }
1472  
1473          return attachment.get('type') === 'image';
1474      },
1475  
1476      /**
1477       * Whether an attachment can be embedded (audio or video).
1478       *
1479       * @since 3.6.0
1480       *
1481       * @param {wp.media.model.Attachment} attachment
1482       * @return {boolean}
1483       */
1484      canEmbed: function( attachment ) {
1485          // If uploading, we know the filename but not the mime type.
1486          if ( ! attachment.get('uploading') ) {
1487              var type = attachment.get('type');
1488              if ( type !== 'audio' && type !== 'video' ) {
1489                  return false;
1490              }
1491          }
1492  
1493          return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() );
1494      },
1495  
1496  
1497      /**
1498       * If the state is active, no items are selected, and the current
1499       * content mode is not an option in the state's router (provided
1500       * the state has a router), reset the content mode to the default.
1501       *
1502       * @since 3.5.0
1503       */
1504      refreshContent: function() {
1505          var selection = this.get('selection'),
1506              frame = this.frame,
1507              router = frame.router.get(),
1508              mode = frame.content.mode();
1509  
1510          if ( this.active && ! selection.length && router && ! router.get( mode ) ) {
1511              this.frame.content.render( this.get('content') );
1512          }
1513      },
1514  
1515      /**
1516       * Callback handler when an attachment is uploaded.
1517       *
1518       * Switch to the Media Library if uploaded from the 'Upload Files' tab.
1519       *
1520       * Adds any uploading attachments to the selection.
1521       *
1522       * If the state only supports one attachment to be selected and multiple
1523       * attachments are uploaded, the last attachment in the upload queue will
1524       * be selected.
1525       *
1526       * @since 3.5.0
1527       *
1528       * @param {wp.media.model.Attachment} attachment
1529       */
1530      uploading: function( attachment ) {
1531          var content = this.frame.content;
1532  
1533          if ( 'upload' === content.mode() ) {
1534              this.frame.content.mode('browse');
1535          }
1536  
1537          if ( this.get( 'autoSelect' ) ) {
1538              this.get('selection').add( attachment );
1539              this.frame.trigger( 'library:selection:add' );
1540          }
1541      },
1542  
1543      /**
1544       * Persist the mode of the content region as a user setting.
1545       *
1546       * @since 3.5.0
1547       */
1548      saveContentMode: function() {
1549          if ( 'browse' !== this.get('router') ) {
1550              return;
1551          }
1552  
1553          var mode = this.frame.content.mode(),
1554              view = this.frame.router.get();
1555  
1556          if ( view && view.get( mode ) ) {
1557              setUserSetting( 'libraryContent', mode );
1558          }
1559      }
1560  
1561  });
1562  
1563  // Make selectionSync available on any Media Library state.
1564  _.extend( Library.prototype, wp.media.selectionSync );
1565  
1566  module.exports = Library;
1567  
1568  
1569  /***/ }),
1570  
1571  /***/ 8065:
1572  /***/ ((module) => {
1573  
1574  /**
1575   * wp.media.controller.MediaLibrary
1576   *
1577   * @memberOf wp.media.controller
1578   *
1579   * @class
1580   * @augments wp.media.controller.Library
1581   * @augments wp.media.controller.State
1582   * @augments Backbone.Model
1583   */
1584  var Library = wp.media.controller.Library,
1585      MediaLibrary;
1586  
1587  MediaLibrary = Library.extend(/** @lends wp.media.controller.MediaLibrary.prototype */{
1588      defaults: _.defaults({
1589          // Attachments browser defaults. @see media.view.AttachmentsBrowser
1590          filterable:      'uploaded',
1591  
1592          displaySettings: false,
1593          priority:        80,
1594          syncSelection:   false
1595      }, Library.prototype.defaults ),
1596  
1597      /**
1598       * @since 3.9.0
1599       *
1600       * @param options
1601       */
1602      initialize: function( options ) {
1603          this.media = options.media;
1604          this.type = options.type;
1605          this.set( 'library', wp.media.query({ type: this.type }) );
1606  
1607          Library.prototype.initialize.apply( this, arguments );
1608      },
1609  
1610      /**
1611       * @since 3.9.0
1612       */
1613      activate: function() {
1614          // @todo this should use this.frame.
1615          if ( wp.media.frame.lastMime ) {
1616              this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) );
1617              delete wp.media.frame.lastMime;
1618          }
1619          Library.prototype.activate.apply( this, arguments );
1620      }
1621  });
1622  
1623  module.exports = MediaLibrary;
1624  
1625  
1626  /***/ }),
1627  
1628  /***/ 9875:
1629  /***/ ((module) => {
1630  
1631  /**
1632   * wp.media.controller.Region
1633   *
1634   * A region is a persistent application layout area.
1635   *
1636   * A region assumes one mode at any time, and can be switched to another.
1637   *
1638   * When mode changes, events are triggered on the region's parent view.
1639   * The parent view will listen to specific events and fill the region with an
1640   * appropriate view depending on mode. For example, a frame listens for the
1641   * 'browse' mode t be activated on the 'content' view and then fills the region
1642   * with an AttachmentsBrowser view.
1643   *
1644   * @memberOf wp.media.controller
1645   *
1646   * @class
1647   *
1648   * @param {Object}        options          Options hash for the region.
1649   * @param {string}        options.id       Unique identifier for the region.
1650   * @param {Backbone.View} options.view     A parent view the region exists within.
1651   * @param {string}        options.selector jQuery selector for the region within the parent view.
1652   */
1653  var Region = function( options ) {
1654      _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
1655  };
1656  
1657  // Use Backbone's self-propagating `extend` inheritance method.
1658  Region.extend = Backbone.Model.extend;
1659  
1660  _.extend( Region.prototype,/** @lends wp.media.controller.Region.prototype */{
1661      /**
1662       * Activate a mode.
1663       *
1664       * @since 3.5.0
1665       *
1666       * @param {string} mode
1667       *
1668       * @fires Region#activate
1669       * @fires Region#deactivate
1670       *
1671       * @return {wp.media.controller.Region} Returns itself to allow chaining.
1672       */
1673      mode: function( mode ) {
1674          if ( ! mode ) {
1675              return this._mode;
1676          }
1677          // Bail if we're trying to change to the current mode.
1678          if ( mode === this._mode ) {
1679              return this;
1680          }
1681  
1682          /**
1683           * Region mode deactivation event.
1684           *
1685           * @event wp.media.controller.Region#deactivate
1686           */
1687          this.trigger('deactivate');
1688  
1689          this._mode = mode;
1690          this.render( mode );
1691  
1692          /**
1693           * Region mode activation event.
1694           *
1695           * @event wp.media.controller.Region#activate
1696           */
1697          this.trigger('activate');
1698          return this;
1699      },
1700      /**
1701       * Render a mode.
1702       *
1703       * @since 3.5.0
1704       *
1705       * @param {string} mode
1706       *
1707       * @fires Region#create
1708       * @fires Region#render
1709       *
1710       * @return {wp.media.controller.Region} Returns itself to allow chaining.
1711       */
1712      render: function( mode ) {
1713          // If the mode isn't active, activate it.
1714          if ( mode && mode !== this._mode ) {
1715              return this.mode( mode );
1716          }
1717  
1718          var set = { view: null },
1719              view;
1720  
1721          /**
1722           * Create region view event.
1723           *
1724           * Region view creation takes place in an event callback on the frame.
1725           *
1726           * @event wp.media.controller.Region#create
1727           * @type {object}
1728           * @property {object} view
1729           */
1730          this.trigger( 'create', set );
1731          view = set.view;
1732  
1733          /**
1734           * Render region view event.
1735           *
1736           * Region view creation takes place in an event callback on the frame.
1737           *
1738           * @event wp.media.controller.Region#render
1739           * @type {object}
1740           */
1741          this.trigger( 'render', view );
1742          if ( view ) {
1743              this.set( view );
1744          }
1745          return this;
1746      },
1747  
1748      /**
1749       * Get the region's view.
1750       *
1751       * @since 3.5.0
1752       *
1753       * @return {wp.media.View}
1754       */
1755      get: function() {
1756          return this.view.views.first( this.selector );
1757      },
1758  
1759      /**
1760       * Set the region's view as a subview of the frame.
1761       *
1762       * @since 3.5.0
1763       *
1764       * @param {Array|Object} views
1765       * @param {Object} [options={}]
1766       * @return {wp.Backbone.Subviews} Subviews is returned to allow chaining.
1767       */
1768      set: function( views, options ) {
1769          if ( options ) {
1770              options.add = false;
1771          }
1772          return this.view.views.set( this.selector, views, options );
1773      },
1774  
1775      /**
1776       * Trigger regional view events on the frame.
1777       *
1778       * @since 3.5.0
1779       *
1780       * @param {string} event
1781       * @return {undefined|wp.media.controller.Region} Returns itself to allow chaining.
1782       */
1783      trigger: function( event ) {
1784          var base, args;
1785  
1786          if ( ! this._mode ) {
1787              return;
1788          }
1789  
1790          args = _.toArray( arguments );
1791          base = this.id + ':' + event;
1792  
1793          // Trigger `{this.id}:{event}:{this._mode}` event on the frame.
1794          args[0] = base + ':' + this._mode;
1795          this.view.trigger.apply( this.view, args );
1796  
1797          // Trigger `{this.id}:{event}` event on the frame.
1798          args[0] = base;
1799          this.view.trigger.apply( this.view, args );
1800          return this;
1801      }
1802  });
1803  
1804  module.exports = Region;
1805  
1806  
1807  /***/ }),
1808  
1809  /***/ 2275:
1810  /***/ ((module) => {
1811  
1812  var Library = wp.media.controller.Library,
1813      l10n = wp.media.view.l10n,
1814      ReplaceImage;
1815  
1816  /**
1817   * wp.media.controller.ReplaceImage
1818   *
1819   * A state for replacing an image.
1820   *
1821   * @memberOf wp.media.controller
1822   *
1823   * @class
1824   * @augments wp.media.controller.Library
1825   * @augments wp.media.controller.State
1826   * @augments Backbone.Model
1827   *
1828   * @param {object}                     [attributes]                         The attributes hash passed to the state.
1829   * @param {string}                     [attributes.id=replace-image]        Unique identifier.
1830   * @param {string}                     [attributes.title=Replace Image]     Title for the state. Displays in the media menu and the frame's title region.
1831   * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
1832   *                                                                          If one is not supplied, a collection of all images will be created.
1833   * @param {boolean}                    [attributes.multiple=false]          Whether multi-select is enabled.
1834   * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
1835   *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
1836   * @param {string}                     [attributes.menu=default]            Initial mode for the menu region.
1837   * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
1838   * @param {string}                     [attributes.toolbar=replace]         Initial mode for the toolbar region.
1839   * @param {int}                        [attributes.priority=60]             The priority for the state link in the media menu.
1840   * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
1841   * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
1842   *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
1843   * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
1844   * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
1845   * @param {boolean}                    [attributes.describe=false]          Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
1846   * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
1847   * @param {boolean}                    [attributes.syncSelection=true]      Whether the Attachments selection should be persisted from the last state.
1848   */
1849  ReplaceImage = Library.extend(/** @lends wp.media.controller.ReplaceImage.prototype */{
1850      defaults: _.defaults({
1851          id:            'replace-image',
1852          title:         l10n.replaceImageTitle,
1853          multiple:      false,
1854          filterable:    'uploaded',
1855          toolbar:       'replace',
1856          menu:          false,
1857          priority:      60,
1858          syncSelection: true
1859      }, Library.prototype.defaults ),
1860  
1861      /**
1862       * @since 3.9.0
1863       *
1864       * @param options
1865       */
1866      initialize: function( options ) {
1867          var library, comparator;
1868  
1869          this.image = options.image;
1870          // If we haven't been provided a `library`, create a `Selection`.
1871          if ( ! this.get('library') ) {
1872              this.set( 'library', wp.media.query({ type: 'image' }) );
1873          }
1874  
1875          Library.prototype.initialize.apply( this, arguments );
1876  
1877          library    = this.get('library');
1878          comparator = library.comparator;
1879  
1880          // Overload the library's comparator to push items that are not in
1881          // the mirrored query to the front of the aggregate collection.
1882          library.comparator = function( a, b ) {
1883              var aInQuery = !! this.mirroring.get( a.cid ),
1884                  bInQuery = !! this.mirroring.get( b.cid );
1885  
1886              if ( ! aInQuery && bInQuery ) {
1887                  return -1;
1888              } else if ( aInQuery && ! bInQuery ) {
1889                  return 1;
1890              } else {
1891                  return comparator.apply( this, arguments );
1892              }
1893          };
1894  
1895          // Add all items in the selection to the library, so any featured
1896          // images that are not initially loaded still appear.
1897          library.observe( this.get('selection') );
1898      },
1899  
1900      /**
1901       * @since 3.9.0
1902       */
1903      activate: function() {
1904          this.frame.on( 'content:render:browse', this.updateSelection, this );
1905  
1906          Library.prototype.activate.apply( this, arguments );
1907      },
1908  
1909      /**
1910       * @since 5.9.0
1911       */
1912      deactivate: function() {
1913          this.frame.off( 'content:render:browse', this.updateSelection, this );
1914  
1915          Library.prototype.deactivate.apply( this, arguments );
1916      },
1917  
1918      /**
1919       * @since 3.9.0
1920       */
1921      updateSelection: function() {
1922          var selection = this.get('selection'),
1923              attachment = this.image.attachment;
1924  
1925          selection.reset( attachment ? [ attachment ] : [] );
1926      }
1927  });
1928  
1929  module.exports = ReplaceImage;
1930  
1931  
1932  /***/ }),
1933  
1934  /***/ 6172:
1935  /***/ ((module) => {
1936  
1937  var Controller = wp.media.controller,
1938      SiteIconCropper;
1939  
1940  /**
1941   * wp.media.controller.SiteIconCropper
1942   *
1943   * A state for cropping a Site Icon.
1944   *
1945   * @memberOf wp.media.controller
1946   *
1947   * @class
1948   * @augments wp.media.controller.Cropper
1949   * @augments wp.media.controller.State
1950   * @augments Backbone.Model
1951   */
1952  SiteIconCropper = Controller.Cropper.extend(/** @lends wp.media.controller.SiteIconCropper.prototype */{
1953      activate: function() {
1954          this.frame.on( 'content:create:crop', this.createCropContent, this );
1955          this.frame.on( 'close', this.removeCropper, this );
1956          this.set('selection', new Backbone.Collection(this.frame._selection.single));
1957      },
1958  
1959      createCropContent: function() {
1960          this.cropperView = new wp.media.view.SiteIconCropper({
1961              controller: this,
1962              attachment: this.get('selection').first()
1963          });
1964          this.cropperView.on('image-loaded', this.createCropToolbar, this);
1965          this.frame.content.set(this.cropperView);
1966  
1967      },
1968  
1969      doCrop: function( attachment ) {
1970          var cropDetails = attachment.get( 'cropDetails' ),
1971              control = this.get( 'control' );
1972  
1973          cropDetails.dst_width  = control.params.width;
1974          cropDetails.dst_height = control.params.height;
1975  
1976          return wp.ajax.post( 'crop-image', {
1977              nonce: attachment.get( 'nonces' ).edit,
1978              id: attachment.get( 'id' ),
1979              context: 'site-icon',
1980              cropDetails: cropDetails
1981          } );
1982      }
1983  });
1984  
1985  module.exports = SiteIconCropper;
1986  
1987  
1988  /***/ }),
1989  
1990  /***/ 6150:
1991  /***/ ((module) => {
1992  
1993  /**
1994   * wp.media.controller.StateMachine
1995   *
1996   * A state machine keeps track of state. It is in one state at a time,
1997   * and can change from one state to another.
1998   *
1999   * States are stored as models in a Backbone collection.
2000   *
2001   * @memberOf wp.media.controller
2002   *
2003   * @since 3.5.0
2004   *
2005   * @class
2006   * @augments Backbone.Model
2007   * @mixin
2008   * @mixes Backbone.Events
2009   */
2010  var StateMachine = function() {
2011      return {
2012          // Use Backbone's self-propagating `extend` inheritance method.
2013          extend: Backbone.Model.extend
2014      };
2015  };
2016  
2017  _.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{
2018      /**
2019       * Fetch a state.
2020       *
2021       * If no `id` is provided, returns the active state.
2022       *
2023       * Implicitly creates states.
2024       *
2025       * Ensure that the `states` collection exists so the `StateMachine`
2026       * can be used as a mixin.
2027       *
2028       * @since 3.5.0
2029       *
2030       * @param {string} id
2031       * @return {wp.media.controller.State} Returns a State model from
2032       *                                     the StateMachine collection.
2033       */
2034      state: function( id ) {
2035          this.states = this.states || new Backbone.Collection();
2036  
2037          // Default to the active state.
2038          id = id || this._state;
2039  
2040          if ( id && ! this.states.get( id ) ) {
2041              this.states.add({ id: id });
2042          }
2043          return this.states.get( id );
2044      },
2045  
2046      /**
2047       * Sets the active state.
2048       *
2049       * Bail if we're trying to select the current state, if we haven't
2050       * created the `states` collection, or are trying to select a state
2051       * that does not exist.
2052       *
2053       * @since 3.5.0
2054       *
2055       * @param {string} id
2056       *
2057       * @fires wp.media.controller.State#deactivate
2058       * @fires wp.media.controller.State#activate
2059       *
2060       * @return {wp.media.controller.StateMachine} Returns itself to allow chaining.
2061       */
2062      setState: function( id ) {
2063          var previous = this.state();
2064  
2065          if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) {
2066              return this;
2067          }
2068  
2069          if ( previous ) {
2070              previous.trigger('deactivate');
2071              this._lastState = previous.id;
2072          }
2073  
2074          this._state = id;
2075          this.state().trigger('activate');
2076  
2077          return this;
2078      },
2079  
2080      /**
2081       * Returns the previous active state.
2082       *
2083       * Call the `state()` method with no parameters to retrieve the current
2084       * active state.
2085       *
2086       * @since 3.5.0
2087       *
2088       * @return {wp.media.controller.State} Returns a State model from
2089       *                                     the StateMachine collection.
2090       */
2091      lastState: function() {
2092          if ( this._lastState ) {
2093              return this.state( this._lastState );
2094          }
2095      }
2096  });
2097  
2098  // Map all event binding and triggering on a StateMachine to its `states` collection.
2099  _.each([ 'on', 'off', 'trigger' ], function( method ) {
2100      /**
2101       * @function on
2102       * @memberOf wp.media.controller.StateMachine
2103       * @instance
2104       * @return {wp.media.controller.StateMachine} Returns itself to allow chaining.
2105       */
2106      /**
2107       * @function off
2108       * @memberOf wp.media.controller.StateMachine
2109       * @instance
2110       * @return {wp.media.controller.StateMachine} Returns itself to allow chaining.
2111       */
2112      /**
2113       * @function trigger
2114       * @memberOf wp.media.controller.StateMachine
2115       * @instance
2116       * @return {wp.media.controller.StateMachine} Returns itself to allow chaining.
2117       */
2118      StateMachine.prototype[ method ] = function() {
2119          // Ensure that the `states` collection exists so the `StateMachine`
2120          // can be used as a mixin.
2121          this.states = this.states || new Backbone.Collection();
2122          // Forward the method to the `states` collection.
2123          this.states[ method ].apply( this.states, arguments );
2124          return this;
2125      };
2126  });
2127  
2128  module.exports = StateMachine;
2129  
2130  
2131  /***/ }),
2132  
2133  /***/ 5694:
2134  /***/ ((module) => {
2135  
2136  /**
2137   * wp.media.controller.State
2138   *
2139   * A state is a step in a workflow that when set will trigger the controllers
2140   * for the regions to be updated as specified in the frame.
2141   *
2142   * A state has an event-driven lifecycle:
2143   *
2144   *     'ready'      triggers when a state is added to a state machine's collection.
2145   *     'activate'   triggers when a state is activated by a state machine.
2146   *     'deactivate' triggers when a state is deactivated by a state machine.
2147   *     'reset'      is not triggered automatically. It should be invoked by the
2148   *                  proper controller to reset the state to its default.
2149   *
2150   * @memberOf wp.media.controller
2151   *
2152   * @class
2153   * @augments Backbone.Model
2154   */
2155  var State = Backbone.Model.extend(/** @lends wp.media.controller.State.prototype */{
2156      /**
2157       * Constructor.
2158       *
2159       * @since 3.5.0
2160       */
2161      constructor: function() {
2162          this.on( 'activate', this._preActivate, this );
2163          this.on( 'activate', this.activate, this );
2164          this.on( 'activate', this._postActivate, this );
2165          this.on( 'deactivate', this._deactivate, this );
2166          this.on( 'deactivate', this.deactivate, this );
2167          this.on( 'reset', this.reset, this );
2168          this.on( 'ready', this._ready, this );
2169          this.on( 'ready', this.ready, this );
2170          /**
2171           * Call parent constructor with passed arguments
2172           */
2173          Backbone.Model.apply( this, arguments );
2174          this.on( 'change:menu', this._updateMenu, this );
2175      },
2176      /**
2177       * Ready event callback.
2178       *
2179       * @abstract
2180       * @since 3.5.0
2181       */
2182      ready: function() {},
2183  
2184      /**
2185       * Activate event callback.
2186       *
2187       * @abstract
2188       * @since 3.5.0
2189       */
2190      activate: function() {},
2191  
2192      /**
2193       * Deactivate event callback.
2194       *
2195       * @abstract
2196       * @since 3.5.0
2197       */
2198      deactivate: function() {},
2199  
2200      /**
2201       * Reset event callback.
2202       *
2203       * @abstract
2204       * @since 3.5.0
2205       */
2206      reset: function() {},
2207  
2208      /**
2209       * @since 3.5.0
2210       * @access private
2211       */
2212      _ready: function() {
2213          this._updateMenu();
2214      },
2215  
2216      /**
2217       * @since 3.5.0
2218       * @access private
2219      */
2220      _preActivate: function() {
2221          this.active = true;
2222      },
2223  
2224      /**
2225       * @since 3.5.0
2226       * @access private
2227       */
2228      _postActivate: function() {
2229          this.on( 'change:menu', this._menu, this );
2230          this.on( 'change:titleMode', this._title, this );
2231          this.on( 'change:content', this._content, this );
2232          this.on( 'change:toolbar', this._toolbar, this );
2233  
2234          this.frame.on( 'title:render:default', this._renderTitle, this );
2235  
2236          this._title();
2237          this._menu();
2238          this._toolbar();
2239          this._content();
2240          this._router();
2241      },
2242  
2243      /**
2244       * @since 3.5.0
2245       * @access private
2246       */
2247      _deactivate: function() {
2248          this.active = false;
2249  
2250          this.frame.off( 'title:render:default', this._renderTitle, this );
2251  
2252          this.off( 'change:menu', this._menu, this );
2253          this.off( 'change:titleMode', this._title, this );
2254          this.off( 'change:content', this._content, this );
2255          this.off( 'change:toolbar', this._toolbar, this );
2256      },
2257  
2258      /**
2259       * @since 3.5.0
2260       * @access private
2261       */
2262      _title: function() {
2263          this.frame.title.render( this.get('titleMode') || 'default' );
2264      },
2265  
2266      /**
2267       * @since 3.5.0
2268       * @access private
2269       */
2270      _renderTitle: function( view ) {
2271          view.$el.text( this.get('title') || '' );
2272      },
2273  
2274      /**
2275       * @since 3.5.0
2276       * @access private
2277       */
2278      _router: function() {
2279          var router = this.frame.router,
2280              mode = this.get('router'),
2281              view;
2282  
2283          this.frame.$el.toggleClass( 'hide-router', ! mode );
2284          if ( ! mode ) {
2285              return;
2286          }
2287  
2288          this.frame.router.render( mode );
2289  
2290          view = router.get();
2291          if ( view && view.select ) {
2292              view.select( this.frame.content.mode() );
2293          }
2294      },
2295  
2296      /**
2297       * @since 3.5.0
2298       * @access private
2299       */
2300      _menu: function() {
2301          var menu = this.frame.menu,
2302              mode = this.get('menu'),
2303              actionMenuItems,
2304              actionMenuLength,
2305              view;
2306  
2307          if ( this.frame.menu ) {
2308              actionMenuItems = this.frame.menu.get('views'),
2309              actionMenuLength = actionMenuItems ? actionMenuItems.views.get().length : 0,
2310              // Show action menu only if it is active and has more than one default element.
2311              this.frame.$el.toggleClass( 'hide-menu', ! mode || actionMenuLength < 2 );
2312          }
2313          if ( ! mode ) {
2314              return;
2315          }
2316  
2317          menu.mode( mode );
2318  
2319          view = menu.get();
2320          if ( view && view.select ) {
2321              view.select( this.id );
2322          }
2323      },
2324  
2325      /**
2326       * @since 3.5.0
2327       * @access private
2328       */
2329      _updateMenu: function() {
2330          var previous = this.previous('menu'),
2331              menu = this.get('menu');
2332  
2333          if ( previous ) {
2334              this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
2335          }
2336  
2337          if ( menu ) {
2338              this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
2339          }
2340      },
2341  
2342      /**
2343       * Create a view in the media menu for the state.
2344       *
2345       * @since 3.5.0
2346       * @access private
2347       *
2348       * @param {media.view.Menu} view The menu view.
2349       */
2350      _renderMenu: function( view ) {
2351          var menuItem = this.get('menuItem'),
2352              title = this.get('title'),
2353              priority = this.get('priority');
2354  
2355          if ( ! menuItem && title ) {
2356              menuItem = { text: title };
2357  
2358              if ( priority ) {
2359                  menuItem.priority = priority;
2360              }
2361          }
2362  
2363          if ( ! menuItem ) {
2364              return;
2365          }
2366  
2367          view.set( this.id, menuItem );
2368      }
2369  });
2370  
2371  _.each(['toolbar','content'], function( region ) {
2372      /**
2373       * @access private
2374       */
2375      State.prototype[ '_' + region ] = function() {
2376          var mode = this.get( region );
2377          if ( mode ) {
2378              this.frame[ region ].render( mode );
2379          }
2380      };
2381  });
2382  
2383  module.exports = State;
2384  
2385  
2386  /***/ }),
2387  
2388  /***/ 4181:
2389  /***/ ((module) => {
2390  
2391  /**
2392   * wp.media.selectionSync
2393   *
2394   * Sync an attachments selection in a state with another state.
2395   *
2396   * Allows for selecting multiple images in the Add Media workflow, and then
2397   * switching to the Insert Gallery workflow while preserving the attachments selection.
2398   *
2399   * @memberOf wp.media
2400   *
2401   * @mixin
2402   */
2403  var selectionSync = {
2404      /**
2405       * @since 3.5.0
2406       */
2407      syncSelection: function() {
2408          var selection = this.get('selection'),
2409              manager = this.frame._selection;
2410  
2411          if ( ! this.get('syncSelection') || ! manager || ! selection ) {
2412              return;
2413          }
2414  
2415          /*
2416           * If the selection supports multiple items, validate the stored
2417           * attachments based on the new selection's conditions. Record
2418           * the attachments that are not included; we'll maintain a
2419           * reference to those. Other attachments are considered in flux.
2420           */
2421          if ( selection.multiple ) {
2422              selection.reset( [], { silent: true });
2423              selection.validateAll( manager.attachments );
2424              manager.difference = _.difference( manager.attachments.models, selection.models );
2425          }
2426  
2427          // Sync the selection's single item with the master.
2428          selection.single( manager.single );
2429      },
2430  
2431      /**
2432       * Record the currently active attachments, which is a combination
2433       * of the selection's attachments and the set of selected
2434       * attachments that this specific selection considered invalid.
2435       * Reset the difference and record the single attachment.
2436       *
2437       * @since 3.5.0
2438       */
2439      recordSelection: function() {
2440          var selection = this.get('selection'),
2441              manager = this.frame._selection;
2442  
2443          if ( ! this.get('syncSelection') || ! manager || ! selection ) {
2444              return;
2445          }
2446  
2447          if ( selection.multiple ) {
2448              manager.attachments.reset( selection.toArray().concat( manager.difference ) );
2449              manager.difference = [];
2450          } else {
2451              manager.attachments.add( selection.toArray() );
2452          }
2453  
2454          manager.single = selection._single;
2455      }
2456  };
2457  
2458  module.exports = selectionSync;
2459  
2460  
2461  /***/ }),
2462  
2463  /***/ 2982:
2464  /***/ ((module) => {
2465  
2466  var View = wp.media.View,
2467      AttachmentCompat;
2468  
2469  /**
2470   * wp.media.view.AttachmentCompat
2471   *
2472   * A view to display fields added via the `attachment_fields_to_edit` filter.
2473   *
2474   * @memberOf wp.media.view
2475   *
2476   * @class
2477   * @augments wp.media.View
2478   * @augments wp.Backbone.View
2479   * @augments Backbone.View
2480   */
2481  AttachmentCompat = View.extend(/** @lends wp.media.view.AttachmentCompat.prototype */{
2482      tagName:   'form',
2483      className: 'compat-item',
2484  
2485      events: {
2486          'submit':          'preventDefault',
2487          'change input':    'save',
2488          'change select':   'save',
2489          'change textarea': 'save'
2490      },
2491  
2492      initialize: function() {
2493          // Render the view when a new item is added.
2494          this.listenTo( this.model, 'add', this.render );
2495      },
2496  
2497      /**
2498       * @return {wp.media.view.AttachmentCompat} Returns itself to allow chaining.
2499       */
2500      dispose: function() {
2501          if ( this.$(':focus').length ) {
2502              this.save();
2503          }
2504          /**
2505           * call 'dispose' directly on the parent class
2506           */
2507          return View.prototype.dispose.apply( this, arguments );
2508      },
2509      /**
2510       * @return {wp.media.view.AttachmentCompat} Returns itself to allow chaining.
2511       */
2512      render: function() {
2513          var compat = this.model.get('compat');
2514          if ( ! compat || ! compat.item ) {
2515              return;
2516          }
2517  
2518          this.views.detach();
2519          this.$el.html( compat.item );
2520          this.views.render();
2521          return this;
2522      },
2523      /**
2524       * @param {Object} event
2525       */
2526      preventDefault: function( event ) {
2527          event.preventDefault();
2528      },
2529      /**
2530       * @param {Object} event
2531       */
2532      save: function( event ) {
2533          var data = {};
2534  
2535          if ( event ) {
2536              event.preventDefault();
2537          }
2538  
2539          _.each( this.$el.serializeArray(), function( pair ) {
2540              data[ pair.name ] = pair.value;
2541          });
2542  
2543          this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
2544          this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
2545      },
2546  
2547      postSave: function() {
2548          this.controller.trigger( 'attachment:compat:ready', ['ready'] );
2549      }
2550  });
2551  
2552  module.exports = AttachmentCompat;
2553  
2554  
2555  /***/ }),
2556  
2557  /***/ 7709:
2558  /***/ ((module) => {
2559  
2560  var $ = jQuery,
2561      AttachmentFilters;
2562  
2563  /**
2564   * wp.media.view.AttachmentFilters
2565   *
2566   * @memberOf wp.media.view
2567   *
2568   * @class
2569   * @augments wp.media.View
2570   * @augments wp.Backbone.View
2571   * @augments Backbone.View
2572   */
2573  AttachmentFilters = wp.media.View.extend(/** @lends wp.media.view.AttachmentFilters.prototype */{
2574      tagName:   'select',
2575      className: 'attachment-filters',
2576      id:        'media-attachment-filters',
2577  
2578      events: {
2579          change: 'change'
2580      },
2581  
2582      keys: [],
2583  
2584      initialize: function() {
2585          this.createFilters();
2586          _.extend( this.filters, this.options.filters );
2587  
2588          // Build `<option>` elements.
2589          this.$el.html( _.chain( this.filters ).map( function( filter, value ) {
2590              return {
2591                  el: $( '<option></option>' ).val( value ).html( filter.text )[0],
2592                  priority: filter.priority || 50
2593              };
2594          }, this ).sortBy('priority').pluck('el').value() );
2595  
2596          this.listenTo( this.model, 'change', this.select );
2597          this.select();
2598      },
2599  
2600      /**
2601       * @abstract
2602       */
2603      createFilters: function() {
2604          this.filters = {};
2605      },
2606  
2607      /**
2608       * When the selected filter changes, update the Attachment Query properties to match.
2609       */
2610      change: function() {
2611          var filter = this.filters[ this.el.value ];
2612          if ( filter ) {
2613              this.model.set( filter.props );
2614          }
2615      },
2616  
2617      select: function() {
2618          var model = this.model,
2619              value = 'all',
2620              props = model.toJSON();
2621  
2622          _.find( this.filters, function( filter, id ) {
2623              var equal = _.all( filter.props, function( prop, key ) {
2624                  return prop === ( _.isUndefined( props[ key ] ) ? null : props[ key ] );
2625              });
2626  
2627              if ( equal ) {
2628                  return value = id;
2629              }
2630          });
2631  
2632          this.$el.val( value );
2633      }
2634  });
2635  
2636  module.exports = AttachmentFilters;
2637  
2638  
2639  /***/ }),
2640  
2641  /***/ 7349:
2642  /***/ ((module) => {
2643  
2644  var l10n = wp.media.view.l10n,
2645      All;
2646  
2647  /**
2648   * wp.media.view.AttachmentFilters.All
2649   *
2650   * @memberOf wp.media.view.AttachmentFilters
2651   *
2652   * @class
2653   * @augments wp.media.view.AttachmentFilters
2654   * @augments wp.media.View
2655   * @augments wp.Backbone.View
2656   * @augments Backbone.View
2657   */
2658  All = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.All.prototype */{
2659      createFilters: function() {
2660          var filters = {},
2661              uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0;
2662  
2663          _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) {
2664              filters[ key ] = {
2665                  text: text,
2666                  props: {
2667                      status:  null,
2668                      type:    key,
2669                      uploadedTo: null,
2670                      orderby: 'date',
2671                      order:   'DESC',
2672                      author:  null
2673                  }
2674              };
2675          });
2676  
2677          filters.all = {
2678              text:  l10n.allMediaItems,
2679              props: {
2680                  status:  null,
2681                  type:    null,
2682                  uploadedTo: null,
2683                  orderby: 'date',
2684                  order:   'DESC',
2685                  author:  null
2686              },
2687              priority: 10
2688          };
2689  
2690          if ( wp.media.view.settings.post.id ) {
2691              filters.uploaded = {
2692                  text:  l10n.uploadedToThisPost,
2693                  props: {
2694                      status:  null,
2695                      type:    null,
2696                      uploadedTo: wp.media.view.settings.post.id,
2697                      orderby: 'menuOrder',
2698                      order:   'ASC',
2699                      author:  null
2700                  },
2701                  priority: 20
2702              };
2703          }
2704  
2705          filters.unattached = {
2706              text:  l10n.unattached,
2707              props: {
2708                  status:     null,
2709                  uploadedTo: 0,
2710                  type:       null,
2711                  orderby:    'menuOrder',
2712                  order:      'ASC',
2713                  author:     null
2714              },
2715              priority: 50
2716          };
2717  
2718          if ( uid ) {
2719              filters.mine = {
2720                  text:  l10n.mine,
2721                  props: {
2722                      status:        null,
2723                      type:        null,
2724                      uploadedTo:    null,
2725                      orderby:    'date',
2726                      order:        'DESC',
2727                      author:        uid
2728                  },
2729                  priority: 50
2730              };
2731          }
2732  
2733          if ( wp.media.view.settings.mediaTrash &&
2734              this.controller.isModeActive( 'grid' ) ) {
2735  
2736              filters.trash = {
2737                  text:  l10n.trash,
2738                  props: {
2739                      uploadedTo: null,
2740                      status:     'trash',
2741                      type:       null,
2742                      orderby:    'date',
2743                      order:      'DESC',
2744                      author:     null
2745                  },
2746                  priority: 50
2747              };
2748          }
2749  
2750          this.filters = filters;
2751      }
2752  });
2753  
2754  module.exports = All;
2755  
2756  
2757  /***/ }),
2758  
2759  /***/ 6472:
2760  /***/ ((module) => {
2761  
2762  var l10n = wp.media.view.l10n,
2763      DateFilter;
2764  
2765  /**
2766   * A filter dropdown for month/dates.
2767   *
2768   * @memberOf wp.media.view.AttachmentFilters
2769   *
2770   * @class
2771   * @augments wp.media.view.AttachmentFilters
2772   * @augments wp.media.View
2773   * @augments wp.Backbone.View
2774   * @augments Backbone.View
2775   */
2776  DateFilter = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Date.prototype */{
2777      id: 'media-attachment-date-filters',
2778  
2779      createFilters: function() {
2780          var filters = {};
2781          _.each( wp.media.view.settings.months || {}, function( value, index ) {
2782              filters[ index ] = {
2783                  text: value.text,
2784                  props: {
2785                      year: value.year,
2786                      monthnum: value.month
2787                  }
2788              };
2789          });
2790          filters.all = {
2791              text:  l10n.allDates,
2792              props: {
2793                  monthnum: false,
2794                  year:  false
2795              },
2796              priority: 10
2797          };
2798          this.filters = filters;
2799      }
2800  });
2801  
2802  module.exports = DateFilter;
2803  
2804  
2805  /***/ }),
2806  
2807  /***/ 1368:
2808  /***/ ((module) => {
2809  
2810  var l10n = wp.media.view.l10n,
2811      Uploaded;
2812  
2813  /**
2814   * wp.media.view.AttachmentFilters.Uploaded
2815   *
2816   * @memberOf wp.media.view.AttachmentFilters
2817   *
2818   * @class
2819   * @augments wp.media.view.AttachmentFilters
2820   * @augments wp.media.View
2821   * @augments wp.Backbone.View
2822   * @augments Backbone.View
2823   */
2824  Uploaded = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Uploaded.prototype */{
2825      createFilters: function() {
2826          var type = this.model.get('type'),
2827              types = wp.media.view.settings.mimeTypes,
2828              uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0,
2829              text;
2830  
2831          if ( types && type ) {
2832              text = types[ type ];
2833          }
2834  
2835          this.filters = {
2836              all: {
2837                  text:  text || l10n.allMediaItems,
2838                  props: {
2839                      uploadedTo: null,
2840                      orderby: 'date',
2841                      order:   'DESC',
2842                      author:     null
2843                  },
2844                  priority: 10
2845              },
2846  
2847              uploaded: {
2848                  text:  l10n.uploadedToThisPost,
2849                  props: {
2850                      uploadedTo: wp.media.view.settings.post.id,
2851                      orderby: 'menuOrder',
2852                      order:   'ASC',
2853                      author:     null
2854                  },
2855                  priority: 20
2856              },
2857  
2858              unattached: {
2859                  text:  l10n.unattached,
2860                  props: {
2861                      uploadedTo: 0,
2862                      orderby: 'menuOrder',
2863                      order:   'ASC',
2864                      author:     null
2865                  },
2866                  priority: 50
2867              }
2868          };
2869  
2870          if ( uid ) {
2871              this.filters.mine = {
2872                  text:  l10n.mine,
2873                  props: {
2874                      orderby: 'date',
2875                      order:   'DESC',
2876                      author:  uid
2877                  },
2878                  priority: 50
2879              };
2880          }
2881      }
2882  });
2883  
2884  module.exports = Uploaded;
2885  
2886  
2887  /***/ }),
2888  
2889  /***/ 4075:
2890  /***/ ((module) => {
2891  
2892  var View = wp.media.View,
2893      $ = jQuery,
2894      Attachment;
2895  
2896  /**
2897   * wp.media.view.Attachment
2898   *
2899   * @memberOf wp.media.view
2900   *
2901   * @class
2902   * @augments wp.media.View
2903   * @augments wp.Backbone.View
2904   * @augments Backbone.View
2905   */
2906  Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{
2907      tagName:   'li',
2908      className: 'attachment',
2909      template:  wp.template('attachment'),
2910  
2911      attributes: function() {
2912          return {
2913              'tabIndex':     0,
2914              'role':         'checkbox',
2915              'aria-label':   this.model.get( 'title' ),
2916              'aria-checked': false,
2917              'data-id':      this.model.get( 'id' )
2918          };
2919      },
2920  
2921      events: {
2922          'click':                          'toggleSelectionHandler',
2923          'change [data-setting]':          'updateSetting',
2924          'change [data-setting] input':    'updateSetting',
2925          'change [data-setting] select':   'updateSetting',
2926          'change [data-setting] textarea': 'updateSetting',
2927          'click .attachment-close':        'removeFromLibrary',
2928          'click .check':                   'checkClickHandler',
2929          'keydown':                        'toggleSelectionHandler'
2930      },
2931  
2932      buttons: {},
2933  
2934      initialize: function() {
2935          var selection = this.options.selection,
2936              options = _.defaults( this.options, {
2937                  rerenderOnModelChange: true
2938              } );
2939  
2940          if ( options.rerenderOnModelChange ) {
2941              this.listenTo( this.model, 'change', this.render );
2942          } else {
2943              this.listenTo( this.model, 'change:percent', this.progress );
2944          }
2945          this.listenTo( this.model, 'change:title', this._syncTitle );
2946          this.listenTo( this.model, 'change:caption', this._syncCaption );
2947          this.listenTo( this.model, 'change:artist', this._syncArtist );
2948          this.listenTo( this.model, 'change:album', this._syncAlbum );
2949  
2950          // Update the selection.
2951          this.listenTo( this.model, 'add', this.select );
2952          this.listenTo( this.model, 'remove', this.deselect );
2953          if ( selection ) {
2954              selection.on( 'reset', this.updateSelect, this );
2955              // Update the model's details view.
2956              this.listenTo( this.model, 'selection:single selection:unsingle', this.details );
2957              this.details( this.model, this.controller.state().get('selection') );
2958          }
2959  
2960          this.listenTo( this.controller.states, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
2961      },
2962      /**
2963       * @return {wp.media.view.Attachment} Returns itself to allow chaining.
2964       */
2965      dispose: function() {
2966          var selection = this.options.selection;
2967  
2968          // Make sure all settings are saved before removing the view.
2969          this.updateAll();
2970  
2971          if ( selection ) {
2972              selection.off( null, null, this );
2973          }
2974          /**
2975           * call 'dispose' directly on the parent class
2976           */
2977          View.prototype.dispose.apply( this, arguments );
2978          return this;
2979      },
2980      /**
2981       * @return {wp.media.view.Attachment} Returns itself to allow chaining.
2982       */
2983      render: function() {
2984          var options = _.defaults( this.model.toJSON(), {
2985                  orientation:   'landscape',
2986                  uploading:     false,
2987                  type:          '',
2988                  subtype:       '',
2989                  icon:          '',
2990                  filename:      '',
2991                  caption:       '',
2992                  title:         '',
2993                  dateFormatted: '',
2994                  width:         '',
2995                  height:        '',
2996                  compat:        false,
2997                  alt:           '',
2998                  description:   ''
2999              }, this.options );
3000  
3001          options.buttons  = this.buttons;
3002          options.describe = this.controller.state().get('describe');
3003  
3004          if ( 'image' === options.type ) {
3005              options.size = this.imageSize();
3006          }
3007  
3008          options.can = {};
3009          if ( options.nonces ) {
3010              options.can.remove = !! options.nonces['delete'];
3011              options.can.save = !! options.nonces.update;
3012          }
3013  
3014          if ( this.controller.state().get('allowLocalEdits') && ! options.uploading ) {
3015              options.allowLocalEdits = true;
3016          }
3017  
3018          if ( options.uploading && ! options.percent ) {
3019              options.percent = 0;
3020          }
3021  
3022          this.views.detach();
3023          this.$el.html( this.template( options ) );
3024  
3025          this.$el.toggleClass( 'uploading', options.uploading );
3026  
3027          if ( options.uploading ) {
3028              this.$bar = this.$('.media-progress-bar div');
3029          } else {
3030              delete this.$bar;
3031          }
3032  
3033          // Check if the model is selected.
3034          this.updateSelect();
3035  
3036          // Update the save status.
3037          this.updateSave();
3038  
3039          this.views.render();
3040  
3041          return this;
3042      },
3043  
3044      progress: function() {
3045          if ( this.$bar && this.$bar.length ) {
3046              this.$bar.width( this.model.get('percent') + '%' );
3047          }
3048      },
3049  
3050      /**
3051       * @param {Object} event
3052       */
3053      toggleSelectionHandler: function( event ) {
3054          var method;
3055  
3056          // Don't do anything inside inputs and on the attachment check and remove buttons.
3057          if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName ) {
3058              return;
3059          }
3060  
3061          // Catch arrow events.
3062          if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
3063              this.controller.trigger( 'attachment:keydown:arrow', event );
3064              return;
3065          }
3066  
3067          // Catch enter and space events.
3068          if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
3069              return;
3070          }
3071  
3072          event.preventDefault();
3073  
3074          // In the grid view, bubble up an edit:attachment event to the controller.
3075          if ( this.controller.isModeActive( 'grid' ) ) {
3076              if ( this.controller.isModeActive( 'edit' ) ) {
3077                  // Pass the current target to restore focus when closing.
3078                  this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
3079                  return;
3080              }
3081  
3082              if ( this.controller.isModeActive( 'select' ) ) {
3083                  method = 'toggle';
3084              }
3085          }
3086  
3087          if ( event.shiftKey ) {
3088              method = 'between';
3089          } else if ( event.ctrlKey || event.metaKey ) {
3090              method = 'toggle';
3091          }
3092  
3093          // Avoid toggles when the command or control key is pressed with the enter key to prevent deselecting the last selected attachment.
3094          if ( ( event.metaKey || event.ctrlKey ) && ( 13 === event.keyCode || 10 === event.keyCode ) ) {
3095              return;
3096          }
3097  
3098          this.toggleSelection({
3099              method: method
3100          });
3101  
3102          this.controller.trigger( 'selection:toggle' );
3103      },
3104      /**
3105       * @param {Object} options
3106       */
3107      toggleSelection: function( options ) {
3108          var collection = this.collection,
3109              selection = this.options.selection,
3110              model = this.model,
3111              method = options && options.method,
3112              single, models, singleIndex, modelIndex;
3113  
3114          if ( ! selection ) {
3115              return;
3116          }
3117  
3118          single = selection.single();
3119          method = _.isUndefined( method ) ? selection.multiple : method;
3120  
3121          // If the `method` is set to `between`, select all models that
3122          // exist between the current and the selected model.
3123          if ( 'between' === method && single && selection.multiple ) {
3124              // If the models are the same, short-circuit.
3125              if ( single === model ) {
3126                  return;
3127              }
3128  
3129              singleIndex = collection.indexOf( single );
3130              modelIndex  = collection.indexOf( this.model );
3131  
3132              if ( singleIndex < modelIndex ) {
3133                  models = collection.models.slice( singleIndex, modelIndex + 1 );
3134              } else {
3135                  models = collection.models.slice( modelIndex, singleIndex + 1 );
3136              }
3137  
3138              selection.add( models );
3139              selection.single( model );
3140              return;
3141  
3142          // If the `method` is set to `toggle`, just flip the selection
3143          // status, regardless of whether the model is the single model.
3144          } else if ( 'toggle' === method ) {
3145              selection[ this.selected() ? 'remove' : 'add' ]( model );
3146              selection.single( model );
3147              return;
3148          } else if ( 'add' === method ) {
3149              selection.add( model );
3150              selection.single( model );
3151              return;
3152          }
3153  
3154          // Fixes bug that loses focus when selecting a featured image.
3155          if ( ! method ) {
3156              method = 'add';
3157          }
3158  
3159          if ( method !== 'add' ) {
3160              method = 'reset';
3161          }
3162  
3163          if ( this.selected() ) {
3164              /*
3165               * If the model is the single model, remove it.
3166               * If it is not the same as the single model,
3167               * it now becomes the single model.
3168               */
3169              selection[ single === model ? 'remove' : 'single' ]( model );
3170          } else {
3171              /*
3172               * If the model is not selected, run the `method` on the
3173               * selection. By default, we `reset` the selection, but the
3174               * `method` can be set to `add` the model to the selection.
3175               */
3176              selection[ method ]( model );
3177              selection.single( model );
3178          }
3179      },
3180  
3181      updateSelect: function() {
3182          this[ this.selected() ? 'select' : 'deselect' ]();
3183      },
3184      /**
3185       * @return {unresolved|boolean}
3186       */
3187      selected: function() {
3188          var selection = this.options.selection;
3189          if ( selection ) {
3190              return !! selection.get( this.model.cid );
3191          }
3192      },
3193      /**
3194       * @param {Backbone.Model} model
3195       * @param {Backbone.Collection} collection
3196       */
3197      select: function( model, collection ) {
3198          var selection = this.options.selection,
3199              controller = this.controller;
3200  
3201          /*
3202           * Check if a selection exists and if it's the collection provided.
3203           * If they're not the same collection, bail; we're in another
3204           * selection's event loop.
3205           */
3206          if ( ! selection || ( collection && collection !== selection ) ) {
3207              return;
3208          }
3209  
3210          // Bail if the model is already selected.
3211          if ( this.$el.hasClass( 'selected' ) ) {
3212              return;
3213          }
3214  
3215          // Add 'selected' class to model, set aria-checked to true.
3216          this.$el.addClass( 'selected' ).attr( 'aria-checked', true );
3217          //  Make the checkbox tabable, except in media grid (bulk select mode).
3218          if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) {
3219              this.$( '.check' ).attr( 'tabindex', '0' );
3220          }
3221      },
3222      /**
3223       * @param {Backbone.Model} model
3224       * @param {Backbone.Collection} collection
3225       */
3226      deselect: function( model, collection ) {
3227          var selection = this.options.selection;
3228  
3229          /*
3230           * Check if a selection exists and if it's the collection provided.
3231           * If they're not the same collection, bail; we're in another
3232           * selection's event loop.
3233           */
3234          if ( ! selection || ( collection && collection !== selection ) ) {
3235              return;
3236          }
3237          this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )
3238              .find( '.check' ).attr( 'tabindex', '-1' );
3239      },
3240      /**
3241       * @param {Backbone.Model} model
3242       * @param {Backbone.Collection} collection
3243       */
3244      details: function( model, collection ) {
3245          var selection = this.options.selection,
3246              details;
3247  
3248          if ( selection !== collection ) {
3249              return;
3250          }
3251  
3252          details = selection.single();
3253          this.$el.toggleClass( 'details', details === this.model );
3254      },
3255      /**
3256       * @param {string} size
3257       * @return {Object}
3258       */
3259      imageSize: function( size ) {
3260          var sizes = this.model.get('sizes'), matched = false;
3261  
3262          size = size || 'medium';
3263  
3264          // Use the provided image size if possible.
3265          if ( sizes ) {
3266              if ( sizes[ size ] ) {
3267                  matched = sizes[ size ];
3268              } else if ( sizes.large ) {
3269                  matched = sizes.large;
3270              } else if ( sizes.thumbnail ) {
3271                  matched = sizes.thumbnail;
3272              } else if ( sizes.full ) {
3273                  matched = sizes.full;
3274              }
3275  
3276              if ( matched ) {
3277                  return _.clone( matched );
3278              }
3279          }
3280  
3281          return {
3282              url:         this.model.get('url'),
3283              width:       this.model.get('width'),
3284              height:      this.model.get('height'),
3285              orientation: this.model.get('orientation')
3286          };
3287      },
3288      /**
3289       * @param {Object} event
3290       */
3291      updateSetting: function( event ) {
3292          var $setting = $( event.target ).closest('[data-setting]'),
3293              setting, value;
3294  
3295          if ( ! $setting.length ) {
3296              return;
3297          }
3298  
3299          setting = $setting.data('setting');
3300          value   = event.target.value;
3301  
3302          if ( this.model.get( setting ) !== value ) {
3303              this.save( setting, value );
3304          }
3305      },
3306  
3307      /**
3308       * Pass all the arguments to the model's save method.
3309       *
3310       * Records the aggregate status of all save requests and updates the
3311       * view's classes accordingly.
3312       */
3313      save: function() {
3314          var view = this,
3315              save = this._save = this._save || { status: 'ready' },
3316              request = this.model.save.apply( this.model, arguments ),
3317              requests = save.requests ? $.when( request, save.requests ) : request;
3318  
3319          // If we're waiting to remove 'Saved.', stop.
3320          if ( save.savedTimer ) {
3321              clearTimeout( save.savedTimer );
3322          }
3323  
3324          this.updateSave('waiting');
3325          save.requests = requests;
3326          requests.always( function() {
3327              // If we've performed another request since this one, bail.
3328              if ( save.requests !== requests ) {
3329                  return;
3330              }
3331  
3332              view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' );
3333              save.savedTimer = setTimeout( function() {
3334                  view.updateSave('ready');
3335                  delete save.savedTimer;
3336              }, 2000 );
3337          });
3338      },
3339      /**
3340       * @param {string} status
3341       * @return {wp.media.view.Attachment} Returns itself to allow chaining.
3342       */
3343      updateSave: function( status ) {
3344          var save = this._save = this._save || { status: 'ready' };
3345  
3346          if ( status && status !== save.status ) {
3347              this.$el.removeClass( 'save-' + save.status );
3348              save.status = status;
3349          }
3350  
3351          this.$el.addClass( 'save-' + save.status );
3352          return this;
3353      },
3354  
3355      updateAll: function() {
3356          var $settings = this.$('[data-setting]'),
3357              model = this.model,
3358              changed;
3359  
3360          changed = _.chain( $settings ).map( function( el ) {
3361              var $input = $('input, textarea, select, [value]', el ),
3362                  setting, value;
3363  
3364              if ( ! $input.length ) {
3365                  return;
3366              }
3367  
3368              setting = $(el).data('setting');
3369              value = $input.val();
3370  
3371              // Record the value if it changed.
3372              if ( model.get( setting ) !== value ) {
3373                  return [ setting, value ];
3374              }
3375          }).compact().object().value();
3376  
3377          if ( ! _.isEmpty( changed ) ) {
3378              model.save( changed );
3379          }
3380      },
3381      /**
3382       * @param {Object} event
3383       */
3384      removeFromLibrary: function( event ) {
3385          // Catch enter and space events.
3386          if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
3387              return;
3388          }
3389  
3390          // Stop propagation so the model isn't selected.
3391          event.stopPropagation();
3392  
3393          this.collection.remove( this.model );
3394      },
3395  
3396      /**
3397       * Add the model if it isn't in the selection, if it is in the selection,
3398       * remove it.
3399       *
3400       * @param {[type]} event [description]
3401       * @return {[type]} [description]
3402       */
3403      checkClickHandler: function ( event ) {
3404          var selection = this.options.selection;
3405          if ( ! selection ) {
3406              return;
3407          }
3408          event.stopPropagation();
3409          if ( selection.where( { id: this.model.get( 'id' ) } ).length ) {
3410              selection.remove( this.model );
3411              // Move focus back to the attachment tile (from the check).
3412              this.$el.focus();
3413          } else {
3414              selection.add( this.model );
3415          }
3416  
3417          // Trigger an action button update.
3418          this.controller.trigger( 'selection:toggle' );
3419      }
3420  });
3421  
3422  // Ensure settings remain in sync between attachment views.
3423  _.each({
3424      caption: '_syncCaption',
3425      title:   '_syncTitle',
3426      artist:  '_syncArtist',
3427      album:   '_syncAlbum'
3428  }, function( method, setting ) {
3429      /**
3430       * @function _syncCaption
3431       * @memberOf wp.media.view.Attachment
3432       * @instance
3433       *
3434       * @param {Backbone.Model} model
3435       * @param {string} value
3436       * @return {wp.media.view.Attachment} Returns itself to allow chaining.
3437       */
3438      /**
3439       * @function _syncTitle
3440       * @memberOf wp.media.view.Attachment
3441       * @instance
3442       *
3443       * @param {Backbone.Model} model
3444       * @param {string} value
3445       * @return {wp.media.view.Attachment} Returns itself to allow chaining.
3446       */
3447      /**
3448       * @function _syncArtist
3449       * @memberOf wp.media.view.Attachment
3450       * @instance
3451       *
3452       * @param {Backbone.Model} model
3453       * @param {string} value
3454       * @return {wp.media.view.Attachment} Returns itself to allow chaining.
3455       */
3456      /**
3457       * @function _syncAlbum
3458       * @memberOf wp.media.view.Attachment
3459       * @instance
3460       *
3461       * @param {Backbone.Model} model
3462       * @param {string} value
3463       * @return {wp.media.view.Attachment} Returns itself to allow chaining.
3464       */
3465      Attachment.prototype[ method ] = function( model, value ) {
3466          var $setting = this.$('[data-setting="' + setting + '"]');
3467  
3468          if ( ! $setting.length ) {
3469              return this;
3470          }
3471  
3472          /*
3473           * If the updated value is in sync with the value in the DOM, there
3474           * is no need to re-render. If we're currently editing the value,
3475           * it will automatically be in sync, suppressing the re-render for
3476           * the view we're editing, while updating any others.
3477           */
3478          if ( value === $setting.find('input, textarea, select, [value]').val() ) {
3479              return this;
3480          }
3481  
3482          return this.render();
3483      };
3484  });
3485  
3486  module.exports = Attachment;
3487  
3488  
3489  /***/ }),
3490  
3491  /***/ 6090:
3492  /***/ ((module) => {
3493  
3494  /* global ClipboardJS */
3495  var Attachment = wp.media.view.Attachment,
3496      l10n = wp.media.view.l10n,
3497      $ = jQuery,
3498      Details,
3499      __ = wp.i18n.__;
3500  
3501  Details = Attachment.extend(/** @lends wp.media.view.Attachment.Details.prototype */{
3502      tagName:   'div',
3503      className: 'attachment-details',
3504      template:  wp.template('attachment-details'),
3505  
3506      /*
3507       * Reset all the attributes inherited from Attachment including role=checkbox,
3508       * tabindex, etc., as they are inappropriate for this view. See #47458 and [30483] / #30390.
3509       */
3510      attributes: {},
3511  
3512      events: {
3513          'change [data-setting]':          'updateSetting',
3514          'change [data-setting] input':    'updateSetting',
3515          'change [data-setting] select':   'updateSetting',
3516          'change [data-setting] textarea': 'updateSetting',
3517          'click .delete-attachment':       'deleteAttachment',
3518          'click .trash-attachment':        'trashAttachment',
3519          'click .untrash-attachment':      'untrashAttachment',
3520          'click .edit-attachment':         'editAttachment',
3521          'keydown':                        'toggleSelectionHandler'
3522      },
3523  
3524      /**
3525       * Copies the attachment URL to the clipboard.
3526       *
3527       * @since 5.5.0
3528       *
3529       * @param {MouseEvent} event A click event.
3530       *
3531       * @return {void}
3532       */
3533       copyAttachmentDetailsURLClipboard: function() {
3534          var clipboard = new ClipboardJS( '.copy-attachment-url' ),
3535              successTimeout;
3536  
3537          clipboard.on( 'success', function( event ) {
3538              var triggerElement = $( event.trigger ),
3539                  successElement = $( '.success', triggerElement.closest( '.copy-to-clipboard-container' ) );
3540  
3541              // Clear the selection and move focus back to the trigger.
3542              event.clearSelection();
3543  
3544              // Show success visual feedback.
3545              clearTimeout( successTimeout );
3546              successElement.removeClass( 'hidden' );
3547  
3548              // Hide success visual feedback after 3 seconds since last success.
3549              successTimeout = setTimeout( function() {
3550                  successElement.addClass( 'hidden' );
3551              }, 3000 );
3552  
3553              // Handle success audible feedback.
3554              wp.a11y.speak( __( 'The file URL has been copied to your clipboard' ) );
3555          } );
3556       },
3557  
3558      /**
3559       * Shows the details of an attachment.
3560       *
3561       * @since 3.5.0
3562       *
3563       * @constructs wp.media.view.Attachment.Details
3564       * @augments wp.media.view.Attachment
3565       *
3566       * @return {void}
3567       */
3568      initialize: function() {
3569          this.options = _.defaults( this.options, {
3570              rerenderOnModelChange: false
3571          });
3572  
3573          // Call 'initialize' directly on the parent class.
3574          Attachment.prototype.initialize.apply( this, arguments );
3575  
3576          this.copyAttachmentDetailsURLClipboard();
3577      },
3578  
3579      /**
3580       * Gets the focusable elements to move focus to.
3581       *
3582       * @since 5.3.0
3583       */
3584      getFocusableElements: function() {
3585          var editedAttachment = $( 'li[data-id="' + this.model.id + '"]' );
3586  
3587          this.previousAttachment = editedAttachment.prev();
3588          this.nextAttachment = editedAttachment.next();
3589      },
3590  
3591      /**
3592       * Moves focus to the previous or next attachment in the grid.
3593       * Fallbacks to the upload button or media frame when there are no attachments.
3594       *
3595       * @since 5.3.0
3596       */
3597      moveFocus: function() {
3598          if ( this.previousAttachment.length ) {
3599              this.previousAttachment.trigger( 'focus' );
3600              return;
3601          }
3602  
3603          if ( this.nextAttachment.length ) {
3604              this.nextAttachment.trigger( 'focus' );
3605              return;
3606          }
3607  
3608          // Fallback: move focus to the "Select Files" button in the media modal.
3609          if ( this.controller.uploader && this.controller.uploader.$browser ) {
3610              this.controller.uploader.$browser.trigger( 'focus' );
3611              return;
3612          }
3613  
3614          // Last fallback.
3615          this.moveFocusToLastFallback();
3616      },
3617  
3618      /**
3619       * Moves focus to the media frame as last fallback.
3620       *
3621       * @since 5.3.0
3622       */
3623      moveFocusToLastFallback: function() {
3624          // Last fallback: make the frame focusable and move focus to it.
3625          $( '.media-frame' )
3626              .attr( 'tabindex', '-1' )
3627              .trigger( 'focus' );
3628      },
3629  
3630      /**
3631       * Deletes an attachment.
3632       *
3633       * Deletes an attachment after asking for confirmation. After deletion,
3634       * keeps focus in the modal.
3635       *
3636       * @since 3.5.0
3637       *
3638       * @param {MouseEvent} event A click event.
3639       *
3640       * @return {void}
3641       */
3642      deleteAttachment: function( event ) {
3643          event.preventDefault();
3644  
3645          this.getFocusableElements();
3646  
3647          if ( window.confirm( l10n.warnDelete ) ) {
3648              this.model.destroy( {
3649                  wait: true,
3650                  error: function() {
3651                      window.alert( l10n.errorDeleting );
3652                  }
3653              } );
3654  
3655              this.moveFocus();
3656          }
3657      },
3658  
3659      /**
3660       * Sets the Trash state on an attachment, or destroys the model itself.
3661       *
3662       * If the mediaTrash setting is set to true, trashes the attachment.
3663       * Otherwise, the model itself is destroyed.
3664       *
3665       * @since 3.9.0
3666       *
3667       * @param {MouseEvent} event A click event.
3668       *
3669       * @return {void}
3670       */
3671      trashAttachment: function( event ) {
3672          var library = this.controller.library,
3673              self = this;
3674          event.preventDefault();
3675  
3676          this.getFocusableElements();
3677  
3678          // When in the Media Library and the Media Trash is enabled.
3679          if ( wp.media.view.settings.mediaTrash &&
3680              'edit-metadata' === this.controller.content.mode() ) {
3681  
3682              this.model.set( 'status', 'trash' );
3683              this.model.save().done( function() {
3684                  library._requery( true );
3685                  /*
3686                   * @todo We need to move focus back to the previous, next, or first
3687                   * attachment but the library gets re-queried and refreshed.
3688                   * Thus, the references to the previous attachments are lost.
3689                   * We need an alternate method.
3690                   */
3691                  self.moveFocusToLastFallback();
3692              } );
3693          } else {
3694              this.model.destroy();
3695              this.moveFocus();
3696          }
3697      },
3698  
3699      /**
3700       * Untrashes an attachment.
3701       *
3702       * @since 4.0.0
3703       *
3704       * @param {MouseEvent} event A click event.
3705       *
3706       * @return {void}
3707       */
3708      untrashAttachment: function( event ) {
3709          var library = this.controller.library;
3710          event.preventDefault();
3711  
3712          this.model.set( 'status', 'inherit' );
3713          this.model.save().done( function() {
3714              library._requery( true );
3715          } );
3716      },
3717  
3718      /**
3719       * Opens the edit page for a specific attachment.
3720       *
3721       * @since 3.5.0
3722       *
3723       * @param {MouseEvent} event A click event.
3724       *
3725       * @return {void}
3726       */
3727      editAttachment: function( event ) {
3728          var editState = this.controller.states.get( 'edit-image' );
3729          if ( window.imageEdit && editState ) {
3730              event.preventDefault();
3731  
3732              editState.set( 'image', this.model );
3733              this.controller.setState( 'edit-image' );
3734          } else {
3735              this.$el.addClass('needs-refresh');
3736          }
3737      },
3738  
3739      /**
3740       * Triggers an event on the controller when reverse tabbing (shift+tab).
3741       *
3742       * This event can be used to make sure to move the focus correctly.
3743       *
3744       * @since 4.0.0
3745       *
3746       * @fires wp.media.controller.MediaLibrary#attachment:details:shift-tab
3747       * @fires wp.media.controller.MediaLibrary#attachment:keydown:arrow
3748       *
3749       * @param {KeyboardEvent} event A keyboard event.
3750       *
3751       * @return {boolean|void} Returns false or undefined.
3752       */
3753      toggleSelectionHandler: function( event ) {
3754          if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) {
3755              this.controller.trigger( 'attachment:details:shift-tab', event );
3756              return false;
3757          }
3758      },
3759  
3760      render: function() {
3761          Attachment.prototype.render.apply( this, arguments );
3762  
3763          wp.media.mixin.removeAllPlayers();
3764          this.$( 'audio, video' ).each( function (i, elem) {
3765              var el = wp.media.view.MediaDetails.prepareSrc( elem );
3766              new window.MediaElementPlayer( el, wp.media.mixin.mejsSettings );
3767          } );
3768      }
3769  });
3770  
3771  module.exports = Details;
3772  
3773  
3774  /***/ }),
3775  
3776  /***/ 5232:
3777  /***/ ((module) => {
3778  
3779  /**
3780   * wp.media.view.Attachment.EditLibrary
3781   *
3782   * @memberOf wp.media.view.Attachment
3783   *
3784   * @class
3785   * @augments wp.media.view.Attachment
3786   * @augments wp.media.View
3787   * @augments wp.Backbone.View
3788   * @augments Backbone.View
3789   */
3790  var EditLibrary = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.EditLibrary.prototype */{
3791      buttons: {
3792          close: true
3793      }
3794  });
3795  
3796  module.exports = EditLibrary;
3797  
3798  
3799  /***/ }),
3800  
3801  /***/ 4593:
3802  /***/ ((module) => {
3803  
3804  /**
3805   * wp.media.view.Attachment.EditSelection
3806   *
3807   * @memberOf wp.media.view.Attachment
3808   *
3809   * @class
3810   * @augments wp.media.view.Attachment.Selection
3811   * @augments wp.media.view.Attachment
3812   * @augments wp.media.View
3813   * @augments wp.Backbone.View
3814   * @augments Backbone.View
3815   */
3816  var EditSelection = wp.media.view.Attachment.Selection.extend(/** @lends wp.media.view.Attachment.EditSelection.prototype */{
3817      buttons: {
3818          close: true
3819      }
3820  });
3821  
3822  module.exports = EditSelection;
3823  
3824  
3825  /***/ }),
3826  
3827  /***/ 3443:
3828  /***/ ((module) => {
3829  
3830  /**
3831   * wp.media.view.Attachment.Library
3832   *
3833   * @memberOf wp.media.view.Attachment
3834   *
3835   * @class
3836   * @augments wp.media.view.Attachment
3837   * @augments wp.media.View
3838   * @augments wp.Backbone.View
3839   * @augments Backbone.View
3840   */
3841  var Library = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Library.prototype */{
3842      buttons: {
3843          check: true
3844      }
3845  });
3846  
3847  module.exports = Library;
3848  
3849  
3850  /***/ }),
3851  
3852  /***/ 3962:
3853  /***/ ((module) => {
3854  
3855  /**
3856   * wp.media.view.Attachment.Selection
3857   *
3858   * @memberOf wp.media.view.Attachment
3859   *
3860   * @class
3861   * @augments wp.media.view.Attachment
3862   * @augments wp.media.View
3863   * @augments wp.Backbone.View
3864   * @augments Backbone.View
3865   */
3866  var Selection = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Selection.prototype */{
3867      className: 'attachment selection',
3868  
3869      // On click, just select the model, instead of removing the model from
3870      // the selection.
3871      toggleSelection: function() {
3872          this.options.selection.single( this.model );
3873      }
3874  });
3875  
3876  module.exports = Selection;
3877  
3878  
3879  /***/ }),
3880  
3881  /***/ 8142:
3882  /***/ ((module) => {
3883  
3884  var View = wp.media.View,
3885      $ = jQuery,
3886      Attachments,
3887      infiniteScrolling = wp.media.view.settings.infiniteScrolling;
3888  
3889  Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{
3890      tagName:   'ul',
3891      className: 'attachments',
3892  
3893      attributes: {
3894          tabIndex: -1
3895      },
3896  
3897      /**
3898       * Represents the overview of attachments in the Media Library.
3899       *
3900       * The constructor binds events to the collection this view represents when
3901       * adding or removing attachments or resetting the entire collection.
3902       *
3903       * @since 3.5.0
3904       *
3905       * @constructs
3906       * @memberof wp.media.view
3907       *
3908       * @augments wp.media.View
3909       *
3910       * @listens collection:add
3911       * @listens collection:remove
3912       * @listens collection:reset
3913       * @listens controller:library:selection:add
3914       * @listens scrollElement:scroll
3915       * @listens this:ready
3916       * @listens controller:open
3917       */
3918      initialize: function() {
3919          this.el.id = _.uniqueId('__attachments-view-');
3920  
3921          /**
3922           * @since 5.8.0 Added the `infiniteScrolling` parameter.
3923           *
3924           * @param infiniteScrolling  Whether to enable infinite scrolling or use
3925           *                           the default "load more" button.
3926           * @param refreshSensitivity The time in milliseconds to throttle the scroll
3927           *                           handler.
3928           * @param refreshThreshold   The amount of pixels that should be scrolled before
3929           *                           loading more attachments from the server.
3930           * @param AttachmentView     The view class to be used for models in the
3931           *                           collection.
3932           * @param sortable           A jQuery sortable options object
3933           *                           ( http://api.jqueryui.com/sortable/ ).
3934           * @param resize             A boolean indicating whether or not to listen to
3935           *                           resize events.
3936           * @param idealColumnWidth   The width in pixels which a column should have when
3937           *                           calculating the total number of columns.
3938           */
3939          _.defaults( this.options, {
3940              infiniteScrolling:  infiniteScrolling || false,
3941              refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
3942              refreshThreshold:   3,
3943              AttachmentView:     wp.media.view.Attachment,
3944              sortable:           false,
3945              resize:             true,
3946              idealColumnWidth:   $( window ).width() < 640 ? 135 : 150
3947          });
3948  
3949          this._viewsByCid = {};
3950          this.$window = $( window );
3951          this.resizeEvent = 'resize.media-modal-columns';
3952  
3953          this.collection.on( 'add', function( attachment ) {
3954              this.views.add( this.createAttachmentView( attachment ), {
3955                  at: this.collection.indexOf( attachment )
3956              });
3957          }, this );
3958  
3959          /*
3960           * Find the view to be removed, delete it and call the remove function to clear
3961           * any set event handlers.
3962           */
3963          this.collection.on( 'remove', function( attachment ) {
3964              var view = this._viewsByCid[ attachment.cid ];
3965              delete this._viewsByCid[ attachment.cid ];
3966  
3967              if ( view ) {
3968                  view.remove();
3969              }
3970          }, this );
3971  
3972          this.collection.on( 'reset', this.render, this );
3973  
3974          this.controller.on( 'library:selection:add', this.attachmentFocus, this );
3975  
3976          if ( this.options.infiniteScrolling ) {
3977              // Throttle the scroll handler and bind this.
3978              this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
3979  
3980              this.options.scrollElement = this.options.scrollElement || this.el;
3981              $( this.options.scrollElement ).on( 'scroll', this.scroll );
3982          }
3983  
3984          this.initSortable();
3985  
3986          _.bindAll( this, 'setColumns' );
3987  
3988          if ( this.options.resize ) {
3989              this.on( 'ready', this.bindEvents );
3990              this.controller.on( 'open', this.setColumns );
3991  
3992              /*
3993               * Call this.setColumns() after this view has been rendered in the
3994               * DOM so attachments get proper width applied.
3995               */
3996              _.defer( this.setColumns, this );
3997          }
3998      },
3999  
4000      /**
4001       * Listens to the resizeEvent on the window.
4002       *
4003       * Adjusts the amount of columns accordingly. First removes any existing event
4004       * handlers to prevent duplicate listeners.
4005       *
4006       * @since 4.0.0
4007       *
4008       * @listens window:resize
4009       *
4010       * @return {void}
4011       */
4012      bindEvents: function() {
4013          this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) );
4014      },
4015  
4016      /**
4017       * Focuses the first item in the collection.
4018       *
4019       * @since 4.0.0
4020       *
4021       * @return {void}
4022       */
4023      attachmentFocus: function() {
4024          /*
4025           * @todo When uploading new attachments, this tries to move focus to
4026           * the attachments grid. Actually, a progress bar gets initially displayed
4027           * and then updated when uploading completes, so focus is lost.
4028           * Additionally: this view is used for both the attachments list and
4029           * the list of selected attachments in the bottom media toolbar. Thus, when
4030           * uploading attachments, it is called twice and returns two different `this`.
4031           * `this.columns` is truthy within the modal.
4032           */
4033          if ( this.columns ) {
4034              // Move focus to the grid list within the modal.
4035              this.$el.focus();
4036          }
4037      },
4038  
4039      /**
4040       * Restores focus to the selected item in the collection.
4041       *
4042       * Moves focus back to the first selected attachment in the grid. Used when
4043       * tabbing backwards from the attachment details sidebar.
4044       * See media.view.AttachmentsBrowser.
4045       *
4046       * @since 4.0.0
4047       *
4048       * @return {void}
4049       */
4050      restoreFocus: function() {
4051          this.$( 'li.selected:first' ).focus();
4052      },
4053  
4054      /**
4055       * Handles events for arrow key presses.
4056       *
4057       * Focuses the attachment in the direction of the used arrow key if it exists.
4058       *
4059       * @since 4.0.0
4060       *
4061       * @param {KeyboardEvent} event The keyboard event that triggered this function.
4062       *
4063       * @return {void}
4064       */
4065      arrowEvent: function( event ) {
4066          var attachments = this.$el.children( 'li' ),
4067              perRow = this.columns,
4068              index = attachments.filter( ':focus' ).index(),
4069              row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow );
4070  
4071          if ( index === -1 ) {
4072              return;
4073          }
4074  
4075          // Left arrow = 37.
4076          if ( 37 === event.keyCode ) {
4077              if ( 0 === index ) {
4078                  return;
4079              }
4080              attachments.eq( index - 1 ).focus();
4081          }
4082  
4083          // Up arrow = 38.
4084          if ( 38 === event.keyCode ) {
4085              if ( 1 === row ) {
4086                  return;
4087              }
4088              attachments.eq( index - perRow ).focus();
4089          }
4090  
4091          // Right arrow = 39.
4092          if ( 39 === event.keyCode ) {
4093              if ( attachments.length === index ) {
4094                  return;
4095              }
4096              attachments.eq( index + 1 ).focus();
4097          }
4098  
4099          // Down arrow = 40.
4100          if ( 40 === event.keyCode ) {
4101              if ( Math.ceil( attachments.length / perRow ) === row ) {
4102                  return;
4103              }
4104              attachments.eq( index + perRow ).focus();
4105          }
4106      },
4107  
4108      /**
4109       * Clears any set event handlers.
4110       *
4111       * @since 3.5.0
4112       *
4113       * @return {void}
4114       */
4115      dispose: function() {
4116          this.collection.props.off( null, null, this );
4117          if ( this.options.resize ) {
4118              this.$window.off( this.resizeEvent );
4119          }
4120  
4121          // Call 'dispose' directly on the parent class.
4122          View.prototype.dispose.apply( this, arguments );
4123      },
4124  
4125      /**
4126       * Calculates the amount of columns.
4127       *
4128       * Calculates the amount of columns and sets it on the data-columns attribute
4129       * of .media-frame-content.
4130       *
4131       * @since 4.0.0
4132       *
4133       * @return {void}
4134       */
4135      setColumns: function() {
4136          var prev = this.columns,
4137              width = this.$el.width();
4138  
4139          if ( width ) {
4140              this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1;
4141  
4142              if ( ! prev || prev !== this.columns ) {
4143                  this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns );
4144              }
4145          }
4146      },
4147  
4148      /**
4149       * Initializes jQuery sortable on the attachment list.
4150       *
4151       * Fails gracefully if jQuery sortable doesn't exist or isn't passed
4152       * in the options.
4153       *
4154       * @since 3.5.0
4155       *
4156       * @fires collection:reset
4157       *
4158       * @return {void}
4159       */
4160      initSortable: function() {
4161          var collection = this.collection;
4162  
4163          if ( ! this.options.sortable || ! $.fn.sortable ) {
4164              return;
4165          }
4166  
4167          this.$el.sortable( _.extend({
4168              // If the `collection` has a `comparator`, disable sorting.
4169              disabled: !! collection.comparator,
4170  
4171              /*
4172               * Change the position of the attachment as soon as the mouse pointer
4173               * overlaps a thumbnail.
4174               */
4175              tolerance: 'pointer',
4176  
4177              // Record the initial `index` of the dragged model.
4178              start: function( event, ui ) {
4179                  ui.item.data('sortableIndexStart', ui.item.index());
4180              },
4181  
4182              /*
4183               * Update the model's index in the collection. Do so silently, as the view
4184               * is already accurate.
4185               */
4186              update: function( event, ui ) {
4187                  var model = collection.at( ui.item.data('sortableIndexStart') ),
4188                      comparator = collection.comparator;
4189  
4190                  // Temporarily disable the comparator to prevent `add`
4191                  // from re-sorting.
4192                  delete collection.comparator;
4193  
4194                  // Silently shift the model to its new index.
4195                  collection.remove( model, {
4196                      silent: true
4197                  });
4198                  collection.add( model, {
4199                      silent: true,
4200                      at:     ui.item.index()
4201                  });
4202  
4203                  // Restore the comparator.
4204                  collection.comparator = comparator;
4205  
4206                  // Fire the `reset` event to ensure other collections sync.
4207                  collection.trigger( 'reset', collection );
4208  
4209                  // If the collection is sorted by menu order, update the menu order.
4210                  collection.saveMenuOrder();
4211              }
4212          }, this.options.sortable ) );
4213  
4214          /*
4215           * If the `orderby` property is changed on the `collection`,
4216           * check to see if we have a `comparator`. If so, disable sorting.
4217           */
4218          collection.props.on( 'change:orderby', function() {
4219              this.$el.sortable( 'option', 'disabled', !! collection.comparator );
4220          }, this );
4221  
4222          this.collection.props.on( 'change:orderby', this.refreshSortable, this );
4223          this.refreshSortable();
4224      },
4225  
4226      /**
4227       * Disables jQuery sortable if collection has a comparator or collection.orderby
4228       * equals menuOrder.
4229       *
4230       * @since 3.5.0
4231       *
4232       * @return {void}
4233       */
4234      refreshSortable: function() {
4235          if ( ! this.options.sortable || ! $.fn.sortable ) {
4236              return;
4237          }
4238  
4239          var collection = this.collection,
4240              orderby = collection.props.get('orderby'),
4241              enabled = 'menuOrder' === orderby || ! collection.comparator;
4242  
4243          this.$el.sortable( 'option', 'disabled', ! enabled );
4244      },
4245  
4246      /**
4247       * Creates a new view for an attachment and adds it to _viewsByCid.
4248       *
4249       * @since 3.5.0
4250       *
4251       * @param {wp.media.model.Attachment} attachment
4252       *
4253       * @return {wp.media.View} The created view.
4254       */
4255      createAttachmentView: function( attachment ) {
4256          var view = new this.options.AttachmentView({
4257              controller:           this.controller,
4258              model:                attachment,
4259              collection:           this.collection,
4260              selection:            this.options.selection
4261          });
4262  
4263          return this._viewsByCid[ attachment.cid ] = view;
4264      },
4265  
4266      /**
4267       * Prepares view for display.
4268       *
4269       * Creates views for every attachment in collection if the collection is not
4270       * empty, otherwise clears all views and loads more attachments.
4271       *
4272       * @since 3.5.0
4273       *
4274       * @return {void}
4275       */
4276      prepare: function() {
4277          if ( this.collection.length ) {
4278              this.views.set( this.collection.map( this.createAttachmentView, this ) );
4279          } else {
4280              this.views.unset();
4281              if ( this.options.infiniteScrolling ) {
4282                  this.collection.more().done( this.scroll );
4283              }
4284          }
4285      },
4286  
4287      /**
4288       * Triggers the scroll function to check if we should query for additional
4289       * attachments right away.
4290       *
4291       * @since 3.5.0
4292       *
4293       * @return {void}
4294       */
4295      ready: function() {
4296          if ( this.options.infiniteScrolling ) {
4297              this.scroll();
4298          }
4299      },
4300  
4301      /**
4302       * Handles scroll events.
4303       *
4304       * Shows the spinner if we're close to the bottom. Loads more attachments from
4305       * server if we're {refreshThreshold} times away from the bottom.
4306       *
4307       * @since 3.5.0
4308       *
4309       * @return {void}
4310       */
4311      scroll: function() {
4312          var view = this,
4313              el = this.options.scrollElement,
4314              scrollTop = el.scrollTop,
4315              toolbar;
4316  
4317          /*
4318           * The scroll event occurs on the document, but the element that should be
4319           * checked is the document body.
4320           */
4321          if ( el === document ) {
4322              el = document.body;
4323              scrollTop = $(document).scrollTop();
4324          }
4325  
4326          if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) {
4327              return;
4328          }
4329  
4330          toolbar = this.views.parent.toolbar;
4331  
4332          // Show the spinner only if we are close to the bottom.
4333          if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) {
4334              toolbar.get('spinner').show();
4335          }
4336  
4337          if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) {
4338              this.collection.more().done(function() {
4339                  view.scroll();
4340                  toolbar.get('spinner').hide();
4341              });
4342          }
4343      }
4344  });
4345  
4346  module.exports = Attachments;
4347  
4348  
4349  /***/ }),
4350  
4351  /***/ 6829:
4352  /***/ ((module) => {
4353  
4354  var View = wp.media.View,
4355      mediaTrash = wp.media.view.settings.mediaTrash,
4356      l10n = wp.media.view.l10n,
4357      $ = jQuery,
4358      AttachmentsBrowser,
4359      infiniteScrolling = wp.media.view.settings.infiniteScrolling,
4360      __ = wp.i18n.__,
4361      sprintf = wp.i18n.sprintf;
4362  
4363  /**
4364   * wp.media.view.AttachmentsBrowser
4365   *
4366   * @memberOf wp.media.view
4367   *
4368   * @class
4369   * @augments wp.media.View
4370   * @augments wp.Backbone.View
4371   * @augments Backbone.View
4372   *
4373   * @param {object}         [options]               The options hash passed to the view.
4374   * @param {boolean|string} [options.filters=false] Which filters to show in the browser's toolbar.
4375   *                                                 Accepts 'uploaded' and 'all'.
4376   * @param {boolean}        [options.search=true]   Whether to show the search interface in the
4377   *                                                 browser's toolbar.
4378   * @param {boolean}        [options.date=true]     Whether to show the date filter in the
4379   *                                                 browser's toolbar.
4380   * @param {boolean}        [options.display=false] Whether to show the attachments display settings
4381   *                                                 view in the sidebar.
4382   * @param {boolean|string} [options.sidebar=true]  Whether to create a sidebar for the browser.
4383   *                                                 Accepts true, false, and 'errors'.
4384   */
4385  AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.prototype */{
4386      tagName:   'div',
4387      className: 'attachments-browser',
4388  
4389      initialize: function() {
4390          _.defaults( this.options, {
4391              filters: false,
4392              search:  true,
4393              date:    true,
4394              display: false,
4395              sidebar: true,
4396              AttachmentView: wp.media.view.Attachment.Library
4397          });
4398  
4399          this.controller.on( 'toggle:upload:attachment', this.toggleUploader, this );
4400          this.controller.on( 'edit:selection', this.editSelection );
4401  
4402          // In the Media Library, the sidebar is used to display errors before the attachments grid.
4403          if ( this.options.sidebar && 'errors' === this.options.sidebar ) {
4404              this.createSidebar();
4405          }
4406  
4407          /*
4408           * In the grid mode (the Media Library), place the Inline Uploader before
4409           * other sections so that the visual order and the DOM order match. This way,
4410           * the Inline Uploader in the Media Library is right after the "Add New"
4411           * button, see ticket #37188.
4412           */
4413          if ( this.controller.isModeActive( 'grid' ) ) {
4414              this.createUploader();
4415  
4416              /*
4417               * Create a multi-purpose toolbar. Used as main toolbar in the Media Library
4418               * and also for other things, for example the "Drag and drop to reorder" and
4419               * "Suggested dimensions" info in the media modal.
4420               */
4421              this.createToolbar();
4422          } else {
4423              this.createToolbar();
4424              this.createUploader();
4425          }
4426  
4427          // Add a heading before the attachments list.
4428          this.createAttachmentsHeading();
4429  
4430          // Create the attachments wrapper view.
4431          this.createAttachmentsWrapperView();
4432  
4433          if ( ! infiniteScrolling ) {
4434              this.$el.addClass( 'has-load-more' );
4435              this.createLoadMoreView();
4436          }
4437  
4438          // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909.
4439          if ( this.options.sidebar && 'errors' !== this.options.sidebar ) {
4440              this.createSidebar();
4441          }
4442  
4443          this.updateContent();
4444  
4445          if ( ! infiniteScrolling ) {
4446              this.updateLoadMoreView();
4447          }
4448  
4449          if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) {
4450              this.$el.addClass( 'hide-sidebar' );
4451  
4452              if ( 'errors' === this.options.sidebar ) {
4453                  this.$el.addClass( 'sidebar-for-errors' );
4454              }
4455          }
4456  
4457          this.collection.on( 'add remove reset', this.updateContent, this );
4458  
4459          if ( ! infiniteScrolling ) {
4460              this.collection.on( 'add remove reset', this.updateLoadMoreView, this );
4461          }
4462  
4463          // The non-cached or cached attachments query has completed.
4464          this.collection.on( 'attachments:received', this.announceSearchResults, this );
4465      },
4466  
4467      /**
4468       * Updates the `wp.a11y.speak()` ARIA live region with a message to communicate
4469       * the number of search results to screen reader users. This function is
4470       * debounced because the collection updates multiple times.
4471       *
4472       * @since 5.3.0
4473       *
4474       * @return {void}
4475       */
4476      announceSearchResults: _.debounce( function() {
4477          var count,
4478              /* translators: Accessibility text. %d: Number of attachments found in a search. */
4479              mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Click load more for more results.' );
4480  
4481          if ( infiniteScrolling ) {
4482              /* translators: Accessibility text. %d: Number of attachments found in a search. */
4483              mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Scroll the page for more results.' );
4484          }
4485  
4486          if ( this.collection.mirroring && this.collection.mirroring.args.s ) {
4487              count = this.collection.length;
4488  
4489              if ( 0 === count ) {
4490                  wp.a11y.speak( l10n.noMediaTryNewSearch );
4491                  return;
4492              }
4493  
4494              if ( this.collection.hasMore() ) {
4495                  wp.a11y.speak( mediaFoundHasMoreResultsMessage.replace( '%d', count ) );
4496                  return;
4497              }
4498  
4499              wp.a11y.speak( l10n.mediaFound.replace( '%d', count ) );
4500          }
4501      }, 200 ),
4502  
4503      editSelection: function( modal ) {
4504          // When editing a selection, move focus to the "Go to library" button.
4505          modal.$( '.media-button-backToLibrary' ).focus();
4506      },
4507  
4508      /**
4509       * @return {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining.
4510       */
4511      dispose: function() {
4512          this.options.selection.off( null, null, this );
4513          View.prototype.dispose.apply( this, arguments );
4514          return this;
4515      },
4516  
4517      createToolbar: function() {
4518          var LibraryViewSwitcher, Filters, toolbarOptions,
4519              showFilterByType = -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] );
4520  
4521          toolbarOptions = {
4522              controller: this.controller
4523          };
4524  
4525          if ( this.controller.isModeActive( 'grid' ) ) {
4526              toolbarOptions.className = 'media-toolbar wp-filter';
4527          }
4528  
4529          /**
4530          * @member {wp.media.view.Toolbar}
4531          */
4532          this.toolbar = new wp.media.view.Toolbar( toolbarOptions );
4533  
4534          this.views.add( this.toolbar );
4535  
4536          this.toolbar.set( 'spinner', new wp.media.view.Spinner({
4537              priority: -20
4538          }) );
4539  
4540          if ( showFilterByType || this.options.date ) {
4541              /*
4542               * Create a h2 heading before the select elements that filter attachments.
4543               * This heading is visible in the modal and visually hidden in the grid.
4544               */
4545              this.toolbar.set( 'filters-heading', new wp.media.view.Heading( {
4546                  priority:   -100,
4547                  text:       l10n.filterAttachments,
4548                  level:      'h2',
4549                  className:  'media-attachments-filter-heading'
4550              }).render() );
4551          }
4552  
4553          if ( showFilterByType ) {
4554              // "Filters" is a <select>, a visually hidden label element needs to be rendered before.
4555              this.toolbar.set( 'filtersLabel', new wp.media.view.Label({
4556                  value: l10n.filterByType,
4557                  attributes: {
4558                      'for':  'media-attachment-filters'
4559                  },
4560                  priority:   -80
4561              }).render() );
4562  
4563              if ( 'uploaded' === this.options.filters ) {
4564                  this.toolbar.set( 'filters', new wp.media.view.AttachmentFilters.Uploaded({
4565                      controller: this.controller,
4566                      model:      this.collection.props,
4567                      priority:   -80
4568                  }).render() );
4569              } else {
4570                  Filters = new wp.media.view.AttachmentFilters.All({
4571                      controller: this.controller,
4572                      model:      this.collection.props,
4573                      priority:   -80
4574                  });
4575  
4576                  this.toolbar.set( 'filters', Filters.render() );
4577              }
4578          }
4579  
4580          /*
4581           * Feels odd to bring the global media library switcher into the Attachment browser view.
4582           * Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
4583           * which the controller can tap into and add this view?
4584           */
4585          if ( this.controller.isModeActive( 'grid' ) ) {
4586              LibraryViewSwitcher = View.extend({
4587                  className: 'view-switch media-grid-view-switch',
4588                  template: wp.template( 'media-library-view-switcher')
4589              });
4590  
4591              this.toolbar.set( 'libraryViewSwitcher', new LibraryViewSwitcher({
4592                  controller: this.controller,
4593                  priority: -90
4594              }).render() );
4595  
4596              // DateFilter is a <select>, a visually hidden label element needs to be rendered before.
4597              this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({
4598                  value: l10n.filterByDate,
4599                  attributes: {
4600                      'for': 'media-attachment-date-filters'
4601                  },
4602                  priority: -75
4603              }).render() );
4604              this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({
4605                  controller: this.controller,
4606                  model:      this.collection.props,
4607                  priority: -75
4608              }).render() );
4609  
4610              // BulkSelection is a <div> with subviews, including screen reader text.
4611              this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({
4612                  text: l10n.bulkSelect,
4613                  controller: this.controller,
4614                  priority: -70
4615              }).render() );
4616  
4617              this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({
4618                  filters: Filters,
4619                  style: 'primary',
4620                  disabled: true,
4621                  text: mediaTrash ? l10n.trashSelected : l10n.deletePermanently,
4622                  controller: this.controller,
4623                  priority: -80,
4624                  click: function() {
4625                      var changed = [], removed = [],
4626                          selection = this.controller.state().get( 'selection' ),
4627                          library = this.controller.state().get( 'library' );
4628  
4629                      if ( ! selection.length ) {
4630                          return;
4631                      }
4632  
4633                      if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) {
4634                          return;
4635                      }
4636  
4637                      if ( mediaTrash &&
4638                          'trash' !== selection.at( 0 ).get( 'status' ) &&
4639                          ! window.confirm( l10n.warnBulkTrash ) ) {
4640  
4641                          return;
4642                      }
4643  
4644                      selection.each( function( model ) {
4645                          if ( ! model.get( 'nonces' )['delete'] ) {
4646                              removed.push( model );
4647                              return;
4648                          }
4649  
4650                          if ( mediaTrash && 'trash' === model.get( 'status' ) ) {
4651                              model.set( 'status', 'inherit' );
4652                              changed.push( model.save() );
4653                              removed.push( model );
4654                          } else if ( mediaTrash ) {
4655                              model.set( 'status', 'trash' );
4656                              changed.push( model.save() );
4657                              removed.push( model );
4658                          } else {
4659                              model.destroy({wait: true});
4660                          }
4661                      } );
4662  
4663                      if ( changed.length ) {
4664                          selection.remove( removed );
4665  
4666                          $.when.apply( null, changed ).then( _.bind( function() {
4667                              library._requery( true );
4668                              this.controller.trigger( 'selection:action:done' );
4669                          }, this ) );
4670                      } else {
4671                          this.controller.trigger( 'selection:action:done' );
4672                      }
4673                  }
4674              }).render() );
4675  
4676              if ( mediaTrash ) {
4677                  this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({
4678                      filters: Filters,
4679                      style: 'link button-link-delete',
4680                      disabled: true,
4681                      text: l10n.deletePermanently,
4682                      controller: this.controller,
4683                      priority: -55,
4684                      click: function() {
4685                          var removed = [],
4686                              destroy = [],
4687                              selection = this.controller.state().get( 'selection' );
4688  
4689                          if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) {
4690                              return;
4691                          }
4692  
4693                          selection.each( function( model ) {
4694                              if ( ! model.get( 'nonces' )['delete'] ) {
4695                                  removed.push( model );
4696                                  return;
4697                              }
4698  
4699                              destroy.push( model );
4700                          } );
4701  
4702                          if ( removed.length ) {
4703                              selection.remove( removed );
4704                          }
4705  
4706                          if ( destroy.length ) {
4707                              $.when.apply( null, destroy.map( function (item) {
4708                                  return item.destroy();
4709                              } ) ).then( _.bind( function() {
4710                                  this.controller.trigger( 'selection:action:done' );
4711                              }, this ) );
4712                          }
4713                      }
4714                  }).render() );
4715              }
4716  
4717          } else if ( this.options.date ) {
4718              // DateFilter is a <select>, a visually hidden label element needs to be rendered before.
4719              this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({
4720                  value: l10n.filterByDate,
4721                  attributes: {
4722                      'for': 'media-attachment-date-filters'
4723                  },
4724                  priority: -75
4725              }).render() );
4726              this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({
4727                  controller: this.controller,
4728                  model:      this.collection.props,
4729                  priority: -75
4730              }).render() );
4731          }
4732  
4733          if ( this.options.search ) {
4734              // Search is an input, a label element needs to be rendered before.
4735              this.toolbar.set( 'searchLabel', new wp.media.view.Label({
4736                  value: l10n.searchLabel,
4737                  className: 'media-search-input-label',
4738                  attributes: {
4739                      'for': 'media-search-input'
4740                  },
4741                  priority:   60
4742              }).render() );
4743              this.toolbar.set( 'search', new wp.media.view.Search({
4744                  controller: this.controller,
4745                  model:      this.collection.props,
4746                  priority:   60
4747              }).render() );
4748          }
4749  
4750          if ( this.options.dragInfo ) {
4751              this.toolbar.set( 'dragInfo', new View({
4752                  el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0],
4753                  priority: -40
4754              }) );
4755          }
4756  
4757          if ( this.options.suggestedWidth && this.options.suggestedHeight ) {
4758              this.toolbar.set( 'suggestedDimensions', new View({
4759                  el: $( '<div class="instructions">' + l10n.suggestedDimensions.replace( '%1$s', this.options.suggestedWidth ).replace( '%2$s', this.options.suggestedHeight ) + '</div>' )[0],
4760                  priority: -40
4761              }) );
4762          }
4763      },
4764  
4765      updateContent: function() {
4766          var view = this,
4767              noItemsView;
4768  
4769          if ( this.controller.isModeActive( 'grid' ) ) {
4770              // Usually the media library.
4771              noItemsView = view.attachmentsNoResults;
4772          } else {
4773              // Usually the media modal.
4774              noItemsView = view.uploader;
4775          }
4776  
4777          if ( ! this.collection.length ) {
4778              this.toolbar.get( 'spinner' ).show();
4779              this.toolbar.$( '.media-bg-overlay' ).show();
4780              this.dfd = this.collection.more().done( function() {
4781                  if ( ! view.collection.length ) {
4782                      noItemsView.$el.removeClass( 'hidden' );
4783                  } else {
4784                      noItemsView.$el.addClass( 'hidden' );
4785                  }
4786                  view.toolbar.get( 'spinner' ).hide();
4787                  view.toolbar.$( '.media-bg-overlay' ).hide();
4788              } );
4789          } else {
4790              noItemsView.$el.addClass( 'hidden' );
4791              view.toolbar.get( 'spinner' ).hide();
4792              this.toolbar.$( '.media-bg-overlay' ).hide();
4793          }
4794      },
4795  
4796      createUploader: function() {
4797          this.uploader = new wp.media.view.UploaderInline({
4798              controller: this.controller,
4799              status:     false,
4800              message:    this.controller.isModeActive( 'grid' ) ? '' : l10n.noItemsFound,
4801              canClose:   this.controller.isModeActive( 'grid' )
4802          });
4803  
4804          this.uploader.$el.addClass( 'hidden' );
4805          this.views.add( this.uploader );
4806      },
4807  
4808      toggleUploader: function() {
4809          if ( this.uploader.$el.hasClass( 'hidden' ) ) {
4810              this.uploader.show();
4811          } else {
4812              this.uploader.hide();
4813          }
4814      },
4815  
4816      /**
4817       * Creates the Attachments wrapper view.
4818       *
4819       * @since 5.8.0
4820       *
4821       * @return {void}
4822       */
4823      createAttachmentsWrapperView: function() {
4824          this.attachmentsWrapper = new wp.media.View( {
4825              className: 'attachments-wrapper'
4826          } );
4827  
4828          // Create the list of attachments.
4829          this.views.add( this.attachmentsWrapper );
4830          this.createAttachments();
4831      },
4832  
4833      createAttachments: function() {
4834          this.attachments = new wp.media.view.Attachments({
4835              controller:           this.controller,
4836              collection:           this.collection,
4837              selection:            this.options.selection,
4838              model:                this.model,
4839              sortable:             this.options.sortable,
4840              scrollElement:        this.options.scrollElement,
4841              idealColumnWidth:     this.options.idealColumnWidth,
4842  
4843              // The single `Attachment` view to be used in the `Attachments` view.
4844              AttachmentView: this.options.AttachmentView
4845          });
4846  
4847          // Add keydown listener to the instance of the Attachments view.
4848          this.controller.on( 'attachment:keydown:arrow',     _.bind( this.attachments.arrowEvent, this.attachments ) );
4849          this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) );
4850  
4851          this.views.add( '.attachments-wrapper', this.attachments );
4852  
4853          if ( this.controller.isModeActive( 'grid' ) ) {
4854              this.attachmentsNoResults = new View({
4855                  controller: this.controller,
4856                  tagName: 'p'
4857              });
4858  
4859              this.attachmentsNoResults.$el.addClass( 'hidden no-media' );
4860              this.attachmentsNoResults.$el.html( l10n.noMedia );
4861  
4862              this.views.add( this.attachmentsNoResults );
4863          }
4864      },
4865  
4866      /**
4867       * Creates the load more button and attachments counter view.
4868       *
4869       * @since 5.8.0
4870       *
4871       * @return {void}
4872       */
4873      createLoadMoreView: function() {
4874          var view = this;
4875  
4876          this.loadMoreWrapper = new View( {
4877              controller: this.controller,
4878              className: 'load-more-wrapper'
4879          } );
4880  
4881          this.loadMoreCount = new View( {
4882              controller: this.controller,
4883              tagName: 'p',
4884              className: 'load-more-count hidden'
4885          } );
4886  
4887          this.loadMoreButton = new wp.media.view.Button( {
4888              text: __( 'Load more' ),
4889              className: 'load-more hidden',
4890              style: 'primary',
4891              size: '',
4892              click: function() {
4893                  view.loadMoreAttachments();
4894              }
4895          } );
4896  
4897          this.loadMoreSpinner = new wp.media.view.Spinner();
4898  
4899          this.loadMoreJumpToFirst = new wp.media.view.Button( {
4900              text: __( 'Jump to first loaded item' ),
4901              className: 'load-more-jump hidden',
4902              size: '',
4903              click: function() {
4904                  view.jumpToFirstAddedItem();
4905              }
4906          } );
4907  
4908          this.views.add( '.attachments-wrapper', this.loadMoreWrapper );
4909          this.views.add( '.load-more-wrapper', this.loadMoreSpinner );
4910          this.views.add( '.load-more-wrapper', this.loadMoreCount );
4911          this.views.add( '.load-more-wrapper', this.loadMoreButton );
4912          this.views.add( '.load-more-wrapper', this.loadMoreJumpToFirst );
4913      },
4914  
4915      /**
4916       * Updates the Load More view. This function is debounced because the
4917       * collection updates multiple times at the add, remove, and reset events.
4918       * We need it to run only once, after all attachments are added or removed.
4919       *
4920       * @since 5.8.0
4921       *
4922       * @return {void}
4923       */
4924      updateLoadMoreView: _.debounce( function() {
4925          // Ensure the load more view elements are initially hidden at each update.
4926          this.loadMoreButton.$el.addClass( 'hidden' );
4927          this.loadMoreCount.$el.addClass( 'hidden' );
4928          this.loadMoreJumpToFirst.$el.addClass( 'hidden' ).prop( 'disabled', true );
4929  
4930          if ( ! this.collection.getTotalAttachments() ) {
4931              return;
4932          }
4933  
4934          if ( this.collection.length ) {
4935              this.loadMoreCount.$el.text(
4936                  /* translators: 1: Number of displayed attachments, 2: Number of total attachments. */
4937                  sprintf(
4938                      __( 'Showing %1$s of %2$s media items' ),
4939                      this.collection.length,
4940                      this.collection.getTotalAttachments()
4941                  )
4942              );
4943  
4944              this.loadMoreCount.$el.removeClass( 'hidden' );
4945          }
4946  
4947          /*
4948           * Notice that while the collection updates multiple times hasMore() may
4949           * return true when it's actually not true.
4950           */
4951          if ( this.collection.hasMore() ) {
4952              this.loadMoreButton.$el.removeClass( 'hidden' );
4953          }
4954  
4955          // Find the media item to move focus to. The jQuery `eq()` index is zero-based.
4956          this.firstAddedMediaItem = this.$el.find( '.attachment' ).eq( this.firstAddedMediaItemIndex );
4957  
4958          // If there's a media item to move focus to, make the "Jump to" button available.
4959          if ( this.firstAddedMediaItem.length ) {
4960              this.firstAddedMediaItem.addClass( 'new-media' );
4961              this.loadMoreJumpToFirst.$el.removeClass( 'hidden' ).prop( 'disabled', false );
4962          }
4963  
4964          // If there are new items added, but no more to be added, move focus to Jump button.
4965          if ( this.firstAddedMediaItem.length && ! this.collection.hasMore() ) {
4966              this.loadMoreJumpToFirst.$el.trigger( 'focus' );
4967          }
4968      }, 10 ),
4969  
4970      /**
4971       * Loads more attachments.
4972       *
4973       * @since 5.8.0
4974       *
4975       * @return {void}
4976       */
4977      loadMoreAttachments: function() {
4978          var view = this;
4979  
4980          if ( ! this.collection.hasMore() ) {
4981              return;
4982          }
4983  
4984          /*
4985           * The collection index is zero-based while the length counts the actual
4986           * amount of items. Thus the length is equivalent to the position of the
4987           * first added item.
4988           */
4989          this.firstAddedMediaItemIndex = this.collection.length;
4990  
4991          this.$el.addClass( 'more-loaded' );
4992          this.collection.each( function( attachment ) {
4993              var attach_id = attachment.attributes.id;
4994              $( '[data-id="' + attach_id + '"]' ).addClass( 'found-media' );
4995          });
4996  
4997          view.loadMoreSpinner.show();
4998          this.collection.once( 'attachments:received', function() {
4999              view.loadMoreSpinner.hide();
5000          } );
5001          this.collection.more();
5002      },
5003  
5004      /**
5005       * Moves focus to the first new added item.    .
5006       *
5007       * @since 5.8.0
5008       *
5009       * @return {void}
5010       */
5011      jumpToFirstAddedItem: function() {
5012          // Set focus on first added item.
5013          this.firstAddedMediaItem.focus();
5014      },
5015  
5016      createAttachmentsHeading: function() {
5017          this.attachmentsHeading = new wp.media.view.Heading( {
5018              text: l10n.attachmentsList,
5019              level: 'h2',
5020              className: 'media-views-heading screen-reader-text'
5021          } );
5022          this.views.add( this.attachmentsHeading );
5023      },
5024  
5025      createSidebar: function() {
5026          var options = this.options,
5027              selection = options.selection,
5028              sidebar = this.sidebar = new wp.media.view.Sidebar({
5029                  controller: this.controller
5030              });
5031  
5032          this.views.add( sidebar );
5033  
5034          if ( this.controller.uploader ) {
5035              sidebar.set( 'uploads', new wp.media.view.UploaderStatus({
5036                  controller: this.controller,
5037                  priority:   40
5038              }) );
5039          }
5040  
5041          selection.on( 'selection:single', this.createSingle, this );
5042          selection.on( 'selection:unsingle', this.disposeSingle, this );
5043  
5044          if ( selection.single() ) {
5045              this.createSingle();
5046          }
5047      },
5048  
5049      createSingle: function() {
5050          var sidebar = this.sidebar,
5051              single = this.options.selection.single();
5052  
5053          sidebar.set( 'details', new wp.media.view.Attachment.Details({
5054              controller: this.controller,
5055              model:      single,
5056              priority:   80
5057          }) );
5058  
5059          sidebar.set( 'compat', new wp.media.view.AttachmentCompat({
5060              controller: this.controller,
5061              model:      single,
5062              priority:   120
5063          }) );
5064  
5065          if ( this.options.display ) {
5066              sidebar.set( 'display', new wp.media.view.Settings.AttachmentDisplay({
5067                  controller:   this.controller,
5068                  model:        this.model.display( single ),
5069                  attachment:   single,
5070                  priority:     160,
5071                  userSettings: this.model.get('displayUserSettings')
5072              }) );
5073          }
5074  
5075          // Show the sidebar on mobile.
5076          if ( this.model.id === 'insert' ) {
5077              sidebar.$el.addClass( 'visible' );
5078          }
5079      },
5080  
5081      disposeSingle: function() {
5082          var sidebar = this.sidebar;
5083          sidebar.unset('details');
5084          sidebar.unset('compat');
5085          sidebar.unset('display');
5086          // Hide the sidebar on mobile.
5087          sidebar.$el.removeClass( 'visible' );
5088      }
5089  });
5090  
5091  module.exports = AttachmentsBrowser;
5092  
5093  
5094  /***/ }),
5095  
5096  /***/ 3479:
5097  /***/ ((module) => {
5098  
5099  var Attachments = wp.media.view.Attachments,
5100      Selection;
5101  
5102  /**
5103   * wp.media.view.Attachments.Selection
5104   *
5105   * @memberOf wp.media.view.Attachments
5106   *
5107   * @class
5108   * @augments wp.media.view.Attachments
5109   * @augments wp.media.View
5110   * @augments wp.Backbone.View
5111   * @augments Backbone.View
5112   */
5113  Selection = Attachments.extend(/** @lends wp.media.view.Attachments.Selection.prototype */{
5114      events: {},
5115      initialize: function() {
5116          _.defaults( this.options, {
5117              sortable:   false,
5118              resize:     false,
5119  
5120              // The single `Attachment` view to be used in the `Attachments` view.
5121              AttachmentView: wp.media.view.Attachment.Selection
5122          });
5123          // Call 'initialize' directly on the parent class.
5124          return Attachments.prototype.initialize.apply( this, arguments );
5125      }
5126  });
5127  
5128  module.exports = Selection;
5129  
5130  
5131  /***/ }),
5132  
5133  /***/ 168:
5134  /***/ ((module) => {
5135  
5136  var $ = Backbone.$,
5137      ButtonGroup;
5138  
5139  /**
5140   * wp.media.view.ButtonGroup
5141   *
5142   * @memberOf wp.media.view
5143   *
5144   * @class
5145   * @augments wp.media.View
5146   * @augments wp.Backbone.View
5147   * @augments Backbone.View
5148   */
5149  ButtonGroup = wp.media.View.extend(/** @lends wp.media.view.ButtonGroup.prototype */{
5150      tagName:   'div',
5151      className: 'button-group button-large media-button-group',
5152  
5153      initialize: function() {
5154          /**
5155           * @member {wp.media.view.Button[]}
5156           */
5157          this.buttons = _.map( this.options.buttons || [], function( button ) {
5158              if ( button instanceof Backbone.View ) {
5159                  return button;
5160              } else {
5161                  return new wp.media.view.Button( button ).render();
5162              }
5163          });
5164  
5165          delete this.options.buttons;
5166  
5167          if ( this.options.classes ) {
5168              this.$el.addClass( this.options.classes );
5169          }
5170      },
5171  
5172      /**
5173       * @return {wp.media.view.ButtonGroup}
5174       */
5175      render: function() {
5176          this.$el.html( $( _.pluck( this.buttons, 'el' ) ).detach() );
5177          return this;
5178      }
5179  });
5180  
5181  module.exports = ButtonGroup;
5182  
5183  
5184  /***/ }),
5185  
5186  /***/ 846:
5187  /***/ ((module) => {
5188  
5189  /**
5190   * wp.media.view.Button
5191   *
5192   * @memberOf wp.media.view
5193   *
5194   * @class
5195   * @augments wp.media.View
5196   * @augments wp.Backbone.View
5197   * @augments Backbone.View
5198   */
5199  var Button = wp.media.View.extend(/** @lends wp.media.view.Button.prototype */{
5200      tagName:    'button',
5201      className:  'media-button',
5202      attributes: { type: 'button' },
5203  
5204      events: {
5205          'click': 'click'
5206      },
5207  
5208      defaults: {
5209          text:     '',
5210          style:    '',
5211          size:     'large',
5212          disabled: false
5213      },
5214  
5215      initialize: function() {
5216          /**
5217           * Create a model with the provided `defaults`.
5218           *
5219           * @member {Backbone.Model}
5220           */
5221          this.model = new Backbone.Model( this.defaults );
5222  
5223          // If any of the `options` have a key from `defaults`, apply its
5224          // value to the `model` and remove it from the `options object.
5225          _.each( this.defaults, function( def, key ) {
5226              var value = this.options[ key ];
5227              if ( _.isUndefined( value ) ) {
5228                  return;
5229              }
5230  
5231              this.model.set( key, value );
5232              delete this.options[ key ];
5233          }, this );
5234  
5235          this.listenTo( this.model, 'change', this.render );
5236      },
5237      /**
5238       * @return {wp.media.view.Button} Returns itself to allow chaining.
5239       */
5240      render: function() {
5241          var classes = [ 'button', this.className ],
5242              model = this.model.toJSON();
5243  
5244          if ( model.style ) {
5245              classes.push( 'button-' + model.style );
5246          }
5247  
5248          if ( model.size ) {
5249              classes.push( 'button-' + model.size );
5250          }
5251  
5252          classes = _.uniq( classes.concat( this.options.classes ) );
5253          this.el.className = classes.join(' ');
5254  
5255          this.$el.attr( 'disabled', model.disabled );
5256          this.$el.text( this.model.get('text') );
5257  
5258          return this;
5259      },
5260      /**
5261       * @param {Object} event
5262       */
5263      click: function( event ) {
5264          if ( '#' === this.attributes.href ) {
5265              event.preventDefault();
5266          }
5267  
5268          if ( this.options.click && ! this.model.get('disabled') ) {
5269              this.options.click.apply( this, arguments );
5270          }
5271      }
5272  });
5273  
5274  module.exports = Button;
5275  
5276  
5277  /***/ }),
5278  
5279  /***/ 7637:
5280  /***/ ((module) => {
5281  
5282  var View = wp.media.View,
5283      UploaderStatus = wp.media.view.UploaderStatus,
5284      l10n = wp.media.view.l10n,
5285      $ = jQuery,
5286      Cropper;
5287  
5288  /**
5289   * wp.media.view.Cropper
5290   *
5291   * Uses the imgAreaSelect plugin to allow a user to crop an image.
5292   *
5293   * Takes imgAreaSelect options from
5294   * wp.customize.HeaderControl.calculateImageSelectOptions via
5295   * wp.customize.HeaderControl.openMM.
5296   *
5297   * @memberOf wp.media.view
5298   *
5299   * @class
5300   * @augments wp.media.View
5301   * @augments wp.Backbone.View
5302   * @augments Backbone.View
5303   */
5304  Cropper = View.extend(/** @lends wp.media.view.Cropper.prototype */{
5305      className: 'crop-content',
5306      template: wp.template('crop-content'),
5307      initialize: function() {
5308          _.bindAll(this, 'onImageLoad');
5309      },
5310      ready: function() {
5311          this.controller.frame.on('content:error:crop', this.onError, this);
5312          this.$image = this.$el.find('.crop-image');
5313          this.$image.on('load', this.onImageLoad);
5314          $(window).on('resize.cropper', _.debounce(this.onImageLoad, 250));
5315      },
5316      remove: function() {
5317          $(window).off('resize.cropper');
5318          this.$el.remove();
5319          this.$el.off();
5320          View.prototype.remove.apply(this, arguments);
5321      },
5322      prepare: function() {
5323          return {
5324              title: l10n.cropYourImage,
5325              url: this.options.attachment.get('url')
5326          };
5327      },
5328      onImageLoad: function() {
5329          var imgOptions = this.controller.get('imgSelectOptions'),
5330              imgSelect;
5331  
5332          if (typeof imgOptions === 'function') {
5333              imgOptions = imgOptions(this.options.attachment, this.controller);
5334          }
5335  
5336          imgOptions = _.extend(imgOptions, {
5337              parent: this.$el,
5338              onInit: function() {
5339  
5340                  // Store the set ratio.
5341                  var setRatio = imgSelect.getOptions().aspectRatio;
5342  
5343                  // On mousedown, if no ratio is set and the Shift key is down, use a 1:1 ratio.
5344                  this.parent.children().on( 'mousedown touchstart', function( e ) {
5345  
5346                      // If no ratio is set and the shift key is down, use a 1:1 ratio.
5347                      if ( ! setRatio && e.shiftKey ) {
5348                          imgSelect.setOptions( {
5349                              aspectRatio: '1:1'
5350                          } );
5351                      }
5352                  } );
5353  
5354                  this.parent.children().on( 'mouseup touchend', function() {
5355  
5356                      // Restore the set ratio.
5357                      imgSelect.setOptions( {
5358                          aspectRatio: setRatio ? setRatio : false
5359                      } );
5360                  } );
5361              }
5362          } );
5363          this.trigger('image-loaded');
5364          imgSelect = this.controller.imgSelect = this.$image.imgAreaSelect(imgOptions);
5365      },
5366      onError: function() {
5367          var filename = this.options.attachment.get('filename');
5368  
5369          this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({
5370              filename: UploaderStatus.prototype.filename(filename),
5371              message: window._wpMediaViewsL10n.cropError
5372          }), { at: 0 });
5373      }
5374  });
5375  
5376  module.exports = Cropper;
5377  
5378  
5379  /***/ }),
5380  
5381  /***/ 6126:
5382  /***/ ((module) => {
5383  
5384  var View = wp.media.View,
5385      EditImage;
5386  
5387  /**
5388   * wp.media.view.EditImage
5389   *
5390   * @memberOf wp.media.view
5391   *
5392   * @class
5393   * @augments wp.media.View
5394   * @augments wp.Backbone.View
5395   * @augments Backbone.View
5396   */
5397  EditImage = View.extend(/** @lends wp.media.view.EditImage.prototype */{
5398      className: 'image-editor',
5399      template: wp.template('image-editor'),
5400  
5401      initialize: function( options ) {
5402          this.editor = window.imageEdit;
5403          this.controller = options.controller;
5404          View.prototype.initialize.apply( this, arguments );
5405      },
5406  
5407      prepare: function() {
5408          return this.model.toJSON();
5409      },
5410  
5411      loadEditor: function() {
5412          this.editor.open( this.model.get( 'id' ), this.model.get( 'nonces' ).edit, this );
5413      },
5414  
5415      back: function() {
5416          var lastState = this.controller.lastState();
5417          this.controller.setState( lastState );
5418      },
5419  
5420      refresh: function() {
5421          this.model.fetch();
5422      },
5423  
5424      save: function() {
5425          var lastState = this.controller.lastState();
5426  
5427          this.model.fetch().done( _.bind( function() {
5428              this.controller.setState( lastState );
5429          }, this ) );
5430      }
5431  
5432  });
5433  
5434  module.exports = EditImage;
5435  
5436  
5437  /***/ }),
5438  
5439  /***/ 5741:
5440  /***/ ((module) => {
5441  
5442  /**
5443   * wp.media.view.Embed
5444   *
5445   * @memberOf wp.media.view
5446   *
5447   * @class
5448   * @augments wp.media.View
5449   * @augments wp.Backbone.View
5450   * @augments Backbone.View
5451   */
5452  var Embed = wp.media.View.extend(/** @lends wp.media.view.Ember.prototype */{
5453      className: 'media-embed',
5454  
5455      initialize: function() {
5456          /**
5457           * @member {wp.media.view.EmbedUrl}
5458           */
5459          this.url = new wp.media.view.EmbedUrl({
5460              controller: this.controller,
5461              model:      this.model.props
5462          }).render();
5463  
5464          this.views.set([ this.url ]);
5465          this.refresh();
5466          this.listenTo( this.model, 'change:type', this.refresh );
5467          this.listenTo( this.model, 'change:loading', this.loading );
5468      },
5469  
5470      /**
5471       * @param {Object} view
5472       */
5473      settings: function( view ) {
5474          if ( this._settings ) {
5475              this._settings.remove();
5476          }
5477          this._settings = view;
5478          this.views.add( view );
5479      },
5480  
5481      refresh: function() {
5482          var type = this.model.get('type'),
5483              constructor;
5484  
5485          if ( 'image' === type ) {
5486              constructor = wp.media.view.EmbedImage;
5487          } else if ( 'link' === type ) {
5488              constructor = wp.media.view.EmbedLink;
5489          } else {
5490              return;
5491          }
5492  
5493          this.settings( new constructor({
5494              controller: this.controller,
5495              model:      this.model.props,
5496              priority:   40
5497          }) );
5498      },
5499  
5500      loading: function() {
5501          this.$el.toggleClass( 'embed-loading', this.model.get('loading') );
5502      }
5503  });
5504  
5505  module.exports = Embed;
5506  
5507  
5508  /***/ }),
5509  
5510  /***/ 2395:
5511  /***/ ((module) => {
5512  
5513  var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
5514      EmbedImage;
5515  
5516  /**
5517   * wp.media.view.EmbedImage
5518   *
5519   * @memberOf wp.media.view
5520   *
5521   * @class
5522   * @augments wp.media.view.Settings.AttachmentDisplay
5523   * @augments wp.media.view.Settings
5524   * @augments wp.media.View
5525   * @augments wp.Backbone.View
5526   * @augments Backbone.View
5527   */
5528  EmbedImage = AttachmentDisplay.extend(/** @lends wp.media.view.EmbedImage.prototype */{
5529      className: 'embed-media-settings',
5530      template:  wp.template('embed-image-settings'),
5531  
5532      initialize: function() {
5533          /**
5534           * Call `initialize` directly on parent class with passed arguments
5535           */
5536          AttachmentDisplay.prototype.initialize.apply( this, arguments );
5537          this.listenTo( this.model, 'change:url', this.updateImage );
5538      },
5539  
5540      updateImage: function() {
5541          this.$('img').attr( 'src', this.model.get('url') );
5542      }
5543  });
5544  
5545  module.exports = EmbedImage;
5546  
5547  
5548  /***/ }),
5549  
5550  /***/ 8232:
5551  /***/ ((module) => {
5552  
5553  var $ = jQuery,
5554      EmbedLink;
5555  
5556  /**
5557   * wp.media.view.EmbedLink
5558   *
5559   * @memberOf wp.media.view
5560   *
5561   * @class
5562   * @augments wp.media.view.Settings
5563   * @augments wp.media.View
5564   * @augments wp.Backbone.View
5565   * @augments Backbone.View
5566   */
5567  EmbedLink = wp.media.view.Settings.extend(/** @lends wp.media.view.EmbedLink.prototype */{
5568      className: 'embed-link-settings',
5569      template:  wp.template('embed-link-settings'),
5570  
5571      initialize: function() {
5572          this.listenTo( this.model, 'change:url', this.updateoEmbed );
5573      },
5574  
5575      updateoEmbed: _.debounce( function() {
5576          var url = this.model.get( 'url' );
5577  
5578          // Clear out previous results.
5579          this.$('.embed-container').hide().find('.embed-preview').empty();
5580          this.$( '.setting' ).hide();
5581  
5582          // Only proceed with embed if the field contains more than 11 characters.
5583          // Example: http://a.io is 11 chars
5584          if ( url && ( url.length < 11 || ! url.match(/^http(s)?:\/\//) ) ) {
5585              return;
5586          }
5587  
5588          this.fetch();
5589      }, wp.media.controller.Embed.sensitivity ),
5590  
5591      fetch: function() {
5592          var url = this.model.get( 'url' ), re, youTubeEmbedMatch;
5593  
5594          // Check if they haven't typed in 500 ms.
5595          if ( $('#embed-url-field').val() !== url ) {
5596              return;
5597          }
5598  
5599          if ( this.dfd && 'pending' === this.dfd.state() ) {
5600              this.dfd.abort();
5601          }
5602  
5603          // Support YouTube embed urls, since they work once in the editor.
5604          re = /https?:\/\/www\.youtube\.com\/embed\/([^/]+)/;
5605          youTubeEmbedMatch = re.exec( url );
5606          if ( youTubeEmbedMatch ) {
5607              url = 'https://www.youtube.com/watch?v=' + youTubeEmbedMatch[ 1 ];
5608          }
5609  
5610          this.dfd = wp.apiRequest({
5611              url: wp.media.view.settings.oEmbedProxyUrl,
5612              data: {
5613                  url: url,
5614                  maxwidth: this.model.get( 'width' ),
5615                  maxheight: this.model.get( 'height' )
5616              },
5617              type: 'GET',
5618              dataType: 'json',
5619              context: this
5620          })
5621              .done( function( response ) {
5622                  this.renderoEmbed( {
5623                      data: {
5624                          body: response.html || ''
5625                      }
5626                  } );
5627              } )
5628              .fail( this.renderFail );
5629      },
5630  
5631      renderFail: function ( response, status ) {
5632          if ( 'abort' === status ) {
5633              return;
5634          }
5635          this.$( '.link-text' ).show();
5636      },
5637  
5638      renderoEmbed: function( response ) {
5639          var html = ( response && response.data && response.data.body ) || '';
5640  
5641          if ( html ) {
5642              this.$('.embed-container').show().find('.embed-preview').html( html );
5643          } else {
5644              this.renderFail();
5645          }
5646      }
5647  });
5648  
5649  module.exports = EmbedLink;
5650  
5651  
5652  /***/ }),
5653  
5654  /***/ 7327:
5655  /***/ ((module) => {
5656  
5657  var View = wp.media.View,
5658      $ = jQuery,
5659      l10n = wp.media.view.l10n,
5660      EmbedUrl;
5661  
5662  /**
5663   * wp.media.view.EmbedUrl
5664   *
5665   * @memberOf wp.media.view
5666   *
5667   * @class
5668   * @augments wp.media.View
5669   * @augments wp.Backbone.View
5670   * @augments Backbone.View
5671   */
5672  EmbedUrl = View.extend(/** @lends wp.media.view.EmbedUrl.prototype */{
5673      tagName:   'span',
5674      className: 'embed-url',
5675  
5676      events: {
5677          'input': 'url'
5678      },
5679  
5680      initialize: function() {
5681          this.$input = $( '<input id="embed-url-field" type="url" />' )
5682              .attr( 'aria-label', l10n.insertFromUrlTitle )
5683              .val( this.model.get('url') );
5684          this.input = this.$input[0];
5685  
5686          this.spinner = $('<span class="spinner" />')[0];
5687          this.$el.append([ this.input, this.spinner ]);
5688  
5689          this.listenTo( this.model, 'change:url', this.render );
5690  
5691          if ( this.model.get( 'url' ) ) {
5692              _.delay( _.bind( function () {
5693                  this.model.trigger( 'change:url' );
5694              }, this ), 500 );
5695          }
5696      },
5697      /**
5698       * @return {wp.media.view.EmbedUrl} Returns itself to allow chaining.
5699       */
5700      render: function() {
5701          var $input = this.$input;
5702  
5703          if ( $input.is(':focus') ) {
5704              return;
5705          }
5706  
5707          if ( this.model.get( 'url' ) ) {
5708              this.input.value = this.model.get('url');
5709          } else {
5710              this.input.setAttribute( 'placeholder', 'https://' );
5711          }
5712  
5713          /**
5714           * Call `render` directly on parent class with passed arguments
5715           */
5716          View.prototype.render.apply( this, arguments );
5717          return this;
5718      },
5719  
5720      url: function( event ) {
5721          var url = event.target.value || '';
5722          this.model.set( 'url', url.trim() );
5723      }
5724  });
5725  
5726  module.exports = EmbedUrl;
5727  
5728  
5729  /***/ }),
5730  
5731  /***/ 718:
5732  /***/ ((module) => {
5733  
5734  var $ = jQuery;
5735  
5736  /**
5737   * wp.media.view.FocusManager
5738   *
5739   * @memberOf wp.media.view
5740   *
5741   * @class
5742   * @augments wp.media.View
5743   * @augments wp.Backbone.View
5744   * @augments Backbone.View
5745   */
5746  var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.prototype */{
5747  
5748      events: {
5749          'keydown': 'focusManagementMode'
5750      },
5751  
5752      /**
5753       * Initializes the Focus Manager.
5754       *
5755       * @param {Object} options The Focus Manager options.
5756       *
5757       * @since 5.3.0
5758       *
5759       * @return {void}
5760       */
5761      initialize: function( options ) {
5762          this.mode                    = options.mode || 'constrainTabbing';
5763          this.tabsAutomaticActivation = options.tabsAutomaticActivation || false;
5764      },
5765  
5766       /**
5767       * Determines which focus management mode to use.
5768       *
5769       * @since 5.3.0
5770       *
5771       * @param {Object} event jQuery event object.
5772       *
5773       * @return {void}
5774       */
5775      focusManagementMode: function( event ) {
5776          if ( this.mode === 'constrainTabbing' ) {
5777              this.constrainTabbing( event );
5778          }
5779  
5780          if ( this.mode === 'tabsNavigation' ) {
5781              this.tabsNavigation( event );
5782          }
5783      },
5784  
5785      /**
5786       * Gets all the tabbable elements.
5787       *
5788       * @since 5.3.0
5789       *
5790       * @return {Object} A jQuery collection of tabbable elements.
5791       */
5792      getTabbables: function() {
5793          // Skip the file input added by Plupload.
5794          return this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' );
5795      },
5796  
5797      /**
5798       * Moves focus to the modal dialog.
5799       *
5800       * @since 3.5.0
5801       *
5802       * @return {void}
5803       */
5804      focus: function() {
5805          this.$( '.media-modal' ).trigger( 'focus' );
5806      },
5807  
5808      /**
5809       * Constrains navigation with the Tab key within the media view element.
5810       *
5811       * @since 4.0.0
5812       *
5813       * @param {Object} event A keydown jQuery event.
5814       *
5815       * @return {void}
5816       */
5817      constrainTabbing: function( event ) {
5818          var tabbables;
5819  
5820          // Look for the tab key.
5821          if ( 9 !== event.keyCode ) {
5822              return;
5823          }
5824  
5825          tabbables = this.getTabbables();
5826  
5827          // Keep tab focus within media modal while it's open.
5828          if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
5829              tabbables.first().focus();
5830              return false;
5831          } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
5832              tabbables.last().focus();
5833              return false;
5834          }
5835      },
5836  
5837      /**
5838       * Hides from assistive technologies all the body children.
5839       *
5840       * Sets an `aria-hidden="true"` attribute on all the body children except
5841       * the provided element and other elements that should not be hidden.
5842       *
5843       * The reason why we use `aria-hidden` is that `aria-modal="true"` is buggy
5844       * in Safari 11.1 and support is spotty in other browsers. Also, `aria-modal="true"`
5845       * prevents the `wp.a11y.speak()` ARIA live regions to work as they're outside
5846       * of the modal dialog and get hidden from assistive technologies.
5847       *
5848       * @since 5.2.3
5849       *
5850       * @param {Object} visibleElement The jQuery object representing the element that should not be hidden.
5851       *
5852       * @return {void}
5853       */
5854      setAriaHiddenOnBodyChildren: function( visibleElement ) {
5855          var bodyChildren,
5856              self = this;
5857  
5858          if ( this.isBodyAriaHidden ) {
5859              return;
5860          }
5861  
5862          // Get all the body children.
5863          bodyChildren = document.body.children;
5864  
5865          // Loop through the body children and hide the ones that should be hidden.
5866          _.each( bodyChildren, function( element ) {
5867              // Don't hide the modal element.
5868              if ( element === visibleElement[0] ) {
5869                  return;
5870              }
5871  
5872              // Determine the body children to hide.
5873              if ( self.elementShouldBeHidden( element ) ) {
5874                  element.setAttribute( 'aria-hidden', 'true' );
5875                  // Store the hidden elements.
5876                  self.ariaHiddenElements.push( element );
5877              }
5878          } );
5879  
5880          this.isBodyAriaHidden = true;
5881      },
5882  
5883      /**
5884       * Unhides from assistive technologies all the body children.
5885       *
5886       * Makes visible again to assistive technologies all the body children
5887       * previously hidden and stored in this.ariaHiddenElements.
5888       *
5889       * @since 5.2.3
5890       *
5891       * @return {void}
5892       */
5893      removeAriaHiddenFromBodyChildren: function() {
5894          _.each( this.ariaHiddenElements, function( element ) {
5895              element.removeAttribute( 'aria-hidden' );
5896          } );
5897  
5898          this.ariaHiddenElements = [];
5899          this.isBodyAriaHidden   = false;
5900      },
5901  
5902      /**
5903       * Determines if the passed element should not be hidden from assistive technologies.
5904       *
5905       * @since 5.2.3
5906       *
5907       * @param {Object} element The DOM element that should be checked.
5908       *
5909       * @return {boolean} Whether the element should not be hidden from assistive technologies.
5910       */
5911      elementShouldBeHidden: function( element ) {
5912          var role = element.getAttribute( 'role' ),
5913              liveRegionsRoles = [ 'alert', 'status', 'log', 'marquee', 'timer' ];
5914  
5915          /*
5916           * Don't hide scripts, elements that already have `aria-hidden`, and
5917           * ARIA live regions.
5918           */
5919          return ! (
5920              element.tagName === 'SCRIPT' ||
5921              element.hasAttribute( 'aria-hidden' ) ||
5922              element.hasAttribute( 'aria-live' ) ||
5923              liveRegionsRoles.indexOf( role ) !== -1
5924          );
5925      },
5926  
5927      /**
5928       * Whether the body children are hidden from assistive technologies.
5929       *
5930       * @since 5.2.3
5931       */
5932      isBodyAriaHidden: false,
5933  
5934      /**
5935       * Stores an array of DOM elements that should be hidden from assistive
5936       * technologies, for example when the media modal dialog opens.
5937       *
5938       * @since 5.2.3
5939       */
5940      ariaHiddenElements: [],
5941  
5942      /**
5943       * Holds the jQuery collection of ARIA tabs.
5944       *
5945       * @since 5.3.0
5946       */
5947      tabs: $(),
5948  
5949      /**
5950       * Sets up tabs in an ARIA tabbed interface.
5951       *
5952       * @since 5.3.0
5953       *
5954       * @param {Object} event jQuery event object.
5955       *
5956       * @return {void}
5957       */
5958      setupAriaTabs: function() {
5959          this.tabs = this.$( '[role="tab"]' );
5960  
5961          // Set up initial attributes.
5962          this.tabs.attr( {
5963              'aria-selected': 'false',
5964              tabIndex: '-1'
5965          } );
5966  
5967          // Set up attributes on the initially active tab.
5968          this.tabs.filter( '.active' )
5969              .removeAttr( 'tabindex' )
5970              .attr( 'aria-selected', 'true' );
5971      },
5972  
5973      /**
5974       * Enables arrows navigation within the ARIA tabbed interface.
5975       *
5976       * @since 5.3.0
5977       *
5978       * @param {Object} event jQuery event object.
5979       *
5980       * @return {void}
5981       */
5982      tabsNavigation: function( event ) {
5983          var orientation = 'horizontal',
5984              keys = [ 32, 35, 36, 37, 38, 39, 40 ];
5985  
5986          // Return if not Spacebar, End, Home, or Arrow keys.
5987          if ( keys.indexOf( event.which ) === -1 ) {
5988              return;
5989          }
5990  
5991          // Determine navigation direction.
5992          if ( this.$el.attr( 'aria-orientation' ) === 'vertical' ) {
5993              orientation = 'vertical';
5994          }
5995  
5996          // Make Up and Down arrow keys do nothing with horizontal tabs.
5997          if ( orientation === 'horizontal' && [ 38, 40 ].indexOf( event.which ) !== -1 ) {
5998              return;
5999          }
6000  
6001          // Make Left and Right arrow keys do nothing with vertical tabs.
6002          if ( orientation === 'vertical' && [ 37, 39 ].indexOf( event.which ) !== -1 ) {
6003              return;
6004          }
6005  
6006          this.switchTabs( event, this.tabs );
6007      },
6008  
6009      /**
6010       * Switches tabs in the ARIA tabbed interface.
6011       *
6012       * @since 5.3.0
6013       *
6014       * @param {Object} event jQuery event object.
6015       *
6016       * @return {void}
6017       */
6018      switchTabs: function( event ) {
6019          var key   = event.which,
6020              index = this.tabs.index( $( event.target ) ),
6021              newIndex;
6022  
6023          switch ( key ) {
6024              // Space bar: Activate current targeted tab.
6025              case 32: {
6026                  this.activateTab( this.tabs[ index ] );
6027                  break;
6028              }
6029              // End key: Activate last tab.
6030              case 35: {
6031                  event.preventDefault();
6032                  this.activateTab( this.tabs[ this.tabs.length - 1 ] );
6033                  break;
6034              }
6035              // Home key: Activate first tab.
6036              case 36: {
6037                  event.preventDefault();
6038                  this.activateTab( this.tabs[ 0 ] );
6039                  break;
6040              }
6041              // Left and up keys: Activate previous tab.
6042              case 37:
6043              case 38: {
6044                  event.preventDefault();
6045                  newIndex = ( index - 1 ) < 0 ? this.tabs.length - 1 : index - 1;
6046                  this.activateTab( this.tabs[ newIndex ] );
6047                  break;
6048              }
6049              // Right and down keys: Activate next tab.
6050              case 39:
6051              case 40: {
6052                  event.preventDefault();
6053                  newIndex = ( index + 1 ) === this.tabs.length ? 0 : index + 1;
6054                  this.activateTab( this.tabs[ newIndex ] );
6055                  break;
6056              }
6057          }
6058      },
6059  
6060      /**
6061       * Sets a single tab to be focusable and semantically selected.
6062       *
6063       * @since 5.3.0
6064       *
6065       * @param {Object} tab The tab DOM element.
6066       *
6067       * @return {void}
6068       */
6069      activateTab: function( tab ) {
6070          if ( ! tab ) {
6071              return;
6072          }
6073  
6074          // The tab is a DOM element: no need for jQuery methods.
6075          tab.focus();
6076  
6077          // Handle automatic activation.
6078          if ( this.tabsAutomaticActivation ) {
6079              tab.removeAttribute( 'tabindex' );
6080              tab.setAttribute( 'aria-selected', 'true' );
6081              tab.click();
6082  
6083              return;
6084          }
6085  
6086          // Handle manual activation.
6087          $( tab ).on( 'click', function() {
6088              tab.removeAttribute( 'tabindex' );
6089              tab.setAttribute( 'aria-selected', 'true' );
6090          } );
6091       }
6092  });
6093  
6094  module.exports = FocusManager;
6095  
6096  
6097  /***/ }),
6098  
6099  /***/ 1061:
6100  /***/ ((module) => {
6101  
6102  /**
6103   * wp.media.view.Frame
6104   *
6105   * A frame is a composite view consisting of one or more regions and one or more
6106   * states.
6107   *
6108   * @memberOf wp.media.view
6109   *
6110   * @see wp.media.controller.State
6111   * @see wp.media.controller.Region
6112   *
6113   * @class
6114   * @augments wp.media.View
6115   * @augments wp.Backbone.View
6116   * @augments Backbone.View
6117   * @mixes wp.media.controller.StateMachine
6118   */
6119  var Frame = wp.media.View.extend(/** @lends wp.media.view.Frame.prototype */{
6120      initialize: function() {
6121          _.defaults( this.options, {
6122              mode: [ 'select' ]
6123          });
6124          this._createRegions();
6125          this._createStates();
6126          this._createModes();
6127      },
6128  
6129      _createRegions: function() {
6130          // Clone the regions array.
6131          this.regions = this.regions ? this.regions.slice() : [];
6132  
6133          // Initialize regions.
6134          _.each( this.regions, function( region ) {
6135              this[ region ] = new wp.media.controller.Region({
6136                  view:     this,
6137                  id:       region,
6138                  selector: '.media-frame-' + region
6139              });
6140          }, this );
6141      },
6142      /**
6143       * Create the frame's states.
6144       *
6145       * @see wp.media.controller.State
6146       * @see wp.media.controller.StateMachine
6147       *
6148       * @fires wp.media.controller.State#ready
6149       */
6150      _createStates: function() {
6151          // Create the default `states` collection.
6152          this.states = new Backbone.Collection( null, {
6153              model: wp.media.controller.State
6154          });
6155  
6156          // Ensure states have a reference to the frame.
6157          this.states.on( 'add', function( model ) {
6158              model.frame = this;
6159              model.trigger('ready');
6160          }, this );
6161  
6162          if ( this.options.states ) {
6163              this.states.add( this.options.states );
6164          }
6165      },
6166  
6167      /**
6168       * A frame can be in a mode or multiple modes at one time.
6169       *
6170       * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode.
6171       */
6172      _createModes: function() {
6173          // Store active "modes" that the frame is in. Unrelated to region modes.
6174          this.activeModes = new Backbone.Collection();
6175          this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) );
6176  
6177          _.each( this.options.mode, function( mode ) {
6178              this.activateMode( mode );
6179          }, this );
6180      },
6181      /**
6182       * Reset all states on the frame to their defaults.
6183       *
6184       * @return {wp.media.view.Frame} Returns itself to allow chaining.
6185       */
6186      reset: function() {
6187          this.states.invoke( 'trigger', 'reset' );
6188          return this;
6189      },
6190      /**
6191       * Map activeMode collection events to the frame.
6192       */
6193      triggerModeEvents: function( model, collection, options ) {
6194          var collectionEvent,
6195              modeEventMap = {
6196                  add: 'activate',
6197                  remove: 'deactivate'
6198              },
6199              eventToTrigger;
6200          // Probably a better way to do this.
6201          _.each( options, function( value, key ) {
6202              if ( value ) {
6203                  collectionEvent = key;
6204              }
6205          } );
6206  
6207          if ( ! _.has( modeEventMap, collectionEvent ) ) {
6208              return;
6209          }
6210  
6211          eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent];
6212          this.trigger( eventToTrigger );
6213      },
6214      /**
6215       * Activate a mode on the frame.
6216       *
6217       * @param string mode Mode ID.
6218       * @return {this} Returns itself to allow chaining.
6219       */
6220      activateMode: function( mode ) {
6221          // Bail if the mode is already active.
6222          if ( this.isModeActive( mode ) ) {
6223              return;
6224          }
6225          this.activeModes.add( [ { id: mode } ] );
6226          // Add a CSS class to the frame so elements can be styled for the mode.
6227          this.$el.addClass( 'mode-' + mode );
6228  
6229          return this;
6230      },
6231      /**
6232       * Deactivate a mode on the frame.
6233       *
6234       * @param string mode Mode ID.
6235       * @return {this} Returns itself to allow chaining.
6236       */
6237      deactivateMode: function( mode ) {
6238          // Bail if the mode isn't active.
6239          if ( ! this.isModeActive( mode ) ) {
6240              return this;
6241          }
6242          this.activeModes.remove( this.activeModes.where( { id: mode } ) );
6243          this.$el.removeClass( 'mode-' + mode );
6244          /**
6245           * Frame mode deactivation event.
6246           *
6247           * @event wp.media.view.Frame#{mode}:deactivate
6248           */
6249          this.trigger( mode + ':deactivate' );
6250  
6251          return this;
6252      },
6253      /**
6254       * Check if a mode is enabled on the frame.
6255       *
6256       * @param string mode Mode ID.
6257       * @return bool
6258       */
6259      isModeActive: function( mode ) {
6260          return Boolean( this.activeModes.where( { id: mode } ).length );
6261      }
6262  });
6263  
6264  // Make the `Frame` a `StateMachine`.
6265  _.extend( Frame.prototype, wp.media.controller.StateMachine.prototype );
6266  
6267  module.exports = Frame;
6268  
6269  
6270  /***/ }),
6271  
6272  /***/ 5424:
6273  /***/ ((module) => {
6274  
6275  var Select = wp.media.view.MediaFrame.Select,
6276      l10n = wp.media.view.l10n,
6277      ImageDetails;
6278  
6279  /**
6280   * wp.media.view.MediaFrame.ImageDetails
6281   *
6282   * A media frame for manipulating an image that's already been inserted
6283   * into a post.
6284   *
6285   * @memberOf wp.media.view.MediaFrame
6286   *
6287   * @class
6288   * @augments wp.media.view.MediaFrame.Select
6289   * @augments wp.media.view.MediaFrame
6290   * @augments wp.media.view.Frame
6291   * @augments wp.media.View
6292   * @augments wp.Backbone.View
6293   * @augments Backbone.View
6294   * @mixes wp.media.controller.StateMachine
6295   */
6296  ImageDetails = Select.extend(/** @lends wp.media.view.MediaFrame.ImageDetails.prototype */{
6297      defaults: {
6298          id:      'image',
6299          url:     '',
6300          menu:    'image-details',
6301          content: 'image-details',
6302          toolbar: 'image-details',
6303          type:    'link',
6304          title:    l10n.imageDetailsTitle,
6305          priority: 120
6306      },
6307  
6308      initialize: function( options ) {
6309          this.image = new wp.media.model.PostImage( options.metadata );
6310          this.options.selection = new wp.media.model.Selection( this.image.attachment, { multiple: false } );
6311          Select.prototype.initialize.apply( this, arguments );
6312      },
6313  
6314      bindHandlers: function() {
6315          Select.prototype.bindHandlers.apply( this, arguments );
6316          this.on( 'menu:create:image-details', this.createMenu, this );
6317          this.on( 'content:create:image-details', this.imageDetailsContent, this );
6318          this.on( 'content:render:edit-image', this.editImageContent, this );
6319          this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this );
6320          // Override the select toolbar.
6321          this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this );
6322      },
6323  
6324      createStates: function() {
6325          this.states.add([
6326              new wp.media.controller.ImageDetails({
6327                  image: this.image,
6328                  editable: false
6329              }),
6330              new wp.media.controller.ReplaceImage({
6331                  id: 'replace-image',
6332                  library: wp.media.query( { type: 'image' } ),
6333                  image: this.image,
6334                  multiple:  false,
6335                  title:     l10n.imageReplaceTitle,
6336                  toolbar: 'replace',
6337                  priority:  80,
6338                  displaySettings: true
6339              }),
6340              new wp.media.controller.EditImage( {
6341                  image: this.image,
6342                  selection: this.options.selection
6343              } )
6344          ]);
6345      },
6346  
6347      imageDetailsContent: function( options ) {
6348          options.view = new wp.media.view.ImageDetails({
6349              controller: this,
6350              model: this.state().image,
6351              attachment: this.state().image.attachment
6352          });
6353      },
6354  
6355      editImageContent: function() {
6356          var state = this.state(),
6357              model = state.get('image'),
6358              view;
6359  
6360          if ( ! model ) {
6361              return;
6362          }
6363  
6364          view = new wp.media.view.EditImage( { model: model, controller: this } ).render();
6365  
6366          this.content.set( view );
6367  
6368          // After bringing in the frame, load the actual editor via an Ajax call.
6369          view.loadEditor();
6370  
6371      },
6372  
6373      renderImageDetailsToolbar: function() {
6374          this.toolbar.set( new wp.media.view.Toolbar({
6375              controller: this,
6376              items: {
6377                  select: {
6378                      style:    'primary',
6379                      text:     l10n.update,
6380                      priority: 80,
6381  
6382                      click: function() {
6383                          var controller = this.controller,
6384                              state = controller.state();
6385  
6386                          controller.close();
6387  
6388                          // Not sure if we want to use wp.media.string.image which will create a shortcode or
6389                          // perhaps wp.html.string to at least to build the <img />.
6390                          state.trigger( 'update', controller.image.toJSON() );
6391  
6392                          // Restore and reset the default state.
6393                          controller.setState( controller.options.state );
6394                          controller.reset();
6395                      }
6396                  }
6397              }
6398          }) );
6399      },
6400  
6401      renderReplaceImageToolbar: function() {
6402          var frame = this,
6403              lastState = frame.lastState(),
6404              previous = lastState && lastState.id;
6405  
6406          this.toolbar.set( new wp.media.view.Toolbar({
6407              controller: this,
6408              items: {
6409                  back: {
6410                      text:     l10n.back,
6411                      priority: 80,
6412                      click:    function() {
6413                          if ( previous ) {
6414                              frame.setState( previous );
6415                          } else {
6416                              frame.close();
6417                          }
6418                      }
6419                  },
6420  
6421                  replace: {
6422                      style:    'primary',
6423                      text:     l10n.replace,
6424                      priority: 20,
6425                      requires: { selection: true },
6426  
6427                      click: function() {
6428                          var controller = this.controller,
6429                              state = controller.state(),
6430                              selection = state.get( 'selection' ),
6431                              attachment = selection.single();
6432  
6433                          controller.close();
6434  
6435                          controller.image.changeAttachment( attachment, state.display( attachment ) );
6436  
6437                          // Not sure if we want to use wp.media.string.image which will create a shortcode or
6438                          // perhaps wp.html.string to at least to build the <img />.
6439                          state.trigger( 'replace', controller.image.toJSON() );
6440  
6441                          // Restore and reset the default state.
6442                          controller.setState( controller.options.state );
6443                          controller.reset();
6444                      }
6445                  }
6446              }
6447          }) );
6448      }
6449  
6450  });
6451  
6452  module.exports = ImageDetails;
6453  
6454  
6455  /***/ }),
6456  
6457  /***/ 4274:
6458  /***/ ((module) => {
6459  
6460  var Select = wp.media.view.MediaFrame.Select,
6461      Library = wp.media.controller.Library,
6462      l10n = wp.media.view.l10n,
6463      Post;
6464  
6465  /**
6466   * wp.media.view.MediaFrame.Post
6467   *
6468   * The frame for manipulating media on the Edit Post page.
6469   *
6470   * @memberOf wp.media.view.MediaFrame
6471   *
6472   * @class
6473   * @augments wp.media.view.MediaFrame.Select
6474   * @augments wp.media.view.MediaFrame
6475   * @augments wp.media.view.Frame
6476   * @augments wp.media.View
6477   * @augments wp.Backbone.View
6478   * @augments Backbone.View
6479   * @mixes wp.media.controller.StateMachine
6480   */
6481  Post = Select.extend(/** @lends wp.media.view.MediaFrame.Post.prototype */{
6482      initialize: function() {
6483          this.counts = {
6484              audio: {
6485                  count: wp.media.view.settings.attachmentCounts.audio,
6486                  state: 'playlist'
6487              },
6488              video: {
6489                  count: wp.media.view.settings.attachmentCounts.video,
6490                  state: 'video-playlist'
6491              }
6492          };
6493  
6494          _.defaults( this.options, {
6495              multiple:  true,
6496              editing:   false,
6497              state:    'insert',
6498              metadata:  {}
6499          });
6500  
6501          // Call 'initialize' directly on the parent class.
6502          Select.prototype.initialize.apply( this, arguments );
6503          this.createIframeStates();
6504  
6505      },
6506  
6507      /**
6508       * Create the default states.
6509       */
6510      createStates: function() {
6511          var options = this.options;
6512  
6513          this.states.add([
6514              // Main states.
6515              new Library({
6516                  id:         'insert',
6517                  title:      l10n.insertMediaTitle,
6518                  priority:   20,
6519                  toolbar:    'main-insert',
6520                  filterable: 'all',
6521                  library:    wp.media.query( options.library ),
6522                  multiple:   options.multiple ? 'reset' : false,
6523                  editable:   true,
6524  
6525                  // If the user isn't allowed to edit fields,
6526                  // can they still edit it locally?
6527                  allowLocalEdits: true,
6528  
6529                  // Show the attachment display settings.
6530                  displaySettings: true,
6531                  // Update user settings when users adjust the
6532                  // attachment display settings.
6533                  displayUserSettings: true
6534              }),
6535  
6536              new Library({
6537                  id:         'gallery',
6538                  title:      l10n.createGalleryTitle,
6539                  priority:   40,
6540                  toolbar:    'main-gallery',
6541                  filterable: 'uploaded',
6542                  multiple:   'add',
6543                  editable:   false,
6544  
6545                  library:  wp.media.query( _.defaults({
6546                      type: 'image'
6547                  }, options.library ) )
6548              }),
6549  
6550              // Embed states.
6551              new wp.media.controller.Embed( { metadata: options.metadata } ),
6552  
6553              new wp.media.controller.EditImage( { model: options.editImage } ),
6554  
6555              // Gallery states.
6556              new wp.media.controller.GalleryEdit({
6557                  library: options.selection,
6558                  editing: options.editing,
6559                  menu:    'gallery'
6560              }),
6561  
6562              new wp.media.controller.GalleryAdd(),
6563  
6564              new Library({
6565                  id:         'playlist',
6566                  title:      l10n.createPlaylistTitle,
6567                  priority:   60,
6568                  toolbar:    'main-playlist',
6569                  filterable: 'uploaded',
6570                  multiple:   'add',
6571                  editable:   false,
6572  
6573                  library:  wp.media.query( _.defaults({
6574                      type: 'audio'
6575                  }, options.library ) )
6576              }),
6577  
6578              // Playlist states.
6579              new wp.media.controller.CollectionEdit({
6580                  type: 'audio',
6581                  collectionType: 'playlist',
6582                  title:          l10n.editPlaylistTitle,
6583                  SettingsView:   wp.media.view.Settings.Playlist,
6584                  library:        options.selection,
6585                  editing:        options.editing,
6586                  menu:           'playlist',
6587                  dragInfoText:   l10n.playlistDragInfo,
6588                  dragInfo:       false
6589              }),
6590  
6591              new wp.media.controller.CollectionAdd({
6592                  type: 'audio',
6593                  collectionType: 'playlist',
6594                  title: l10n.addToPlaylistTitle
6595              }),
6596  
6597              new Library({
6598                  id:         'video-playlist',
6599                  title:      l10n.createVideoPlaylistTitle,
6600                  priority:   60,
6601                  toolbar:    'main-video-playlist',
6602                  filterable: 'uploaded',
6603                  multiple:   'add',
6604                  editable:   false,
6605  
6606                  library:  wp.media.query( _.defaults({
6607                      type: 'video'
6608                  }, options.library ) )
6609              }),
6610  
6611              new wp.media.controller.CollectionEdit({
6612                  type: 'video',
6613                  collectionType: 'playlist',
6614                  title:          l10n.editVideoPlaylistTitle,
6615                  SettingsView:   wp.media.view.Settings.Playlist,
6616                  library:        options.selection,
6617                  editing:        options.editing,
6618                  menu:           'video-playlist',
6619                  dragInfoText:   l10n.videoPlaylistDragInfo,
6620                  dragInfo:       false
6621              }),
6622  
6623              new wp.media.controller.CollectionAdd({
6624                  type: 'video',
6625                  collectionType: 'playlist',
6626                  title: l10n.addToVideoPlaylistTitle
6627              })
6628          ]);
6629  
6630          if ( wp.media.view.settings.post.featuredImageId ) {
6631              this.states.add( new wp.media.controller.FeaturedImage() );
6632          }
6633      },
6634  
6635      bindHandlers: function() {
6636          var handlers, checkCounts;
6637  
6638          Select.prototype.bindHandlers.apply( this, arguments );
6639  
6640          this.on( 'activate', this.activate, this );
6641  
6642          // Only bother checking media type counts if one of the counts is zero.
6643          checkCounts = _.find( this.counts, function( type ) {
6644              return type.count === 0;
6645          } );
6646  
6647          if ( typeof checkCounts !== 'undefined' ) {
6648              this.listenTo( wp.media.model.Attachments.all, 'change:type', this.mediaTypeCounts );
6649          }
6650  
6651          this.on( 'menu:create:gallery', this.createMenu, this );
6652          this.on( 'menu:create:playlist', this.createMenu, this );
6653          this.on( 'menu:create:video-playlist', this.createMenu, this );
6654          this.on( 'toolbar:create:main-insert', this.createToolbar, this );
6655          this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
6656          this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
6657          this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
6658          this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
6659          this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
6660  
6661          handlers = {
6662              menu: {
6663                  'default': 'mainMenu',
6664                  'gallery': 'galleryMenu',
6665                  'playlist': 'playlistMenu',
6666                  'video-playlist': 'videoPlaylistMenu'
6667              },
6668  
6669              content: {
6670                  'embed':          'embedContent',
6671                  'edit-image':     'editImageContent',
6672                  'edit-selection': 'editSelectionContent'
6673              },
6674  
6675              toolbar: {
6676                  'main-insert':      'mainInsertToolbar',
6677                  'main-gallery':     'mainGalleryToolbar',
6678                  'gallery-edit':     'galleryEditToolbar',
6679                  'gallery-add':      'galleryAddToolbar',
6680                  'main-playlist':    'mainPlaylistToolbar',
6681                  'playlist-edit':    'playlistEditToolbar',
6682                  'playlist-add':        'playlistAddToolbar',
6683                  'main-video-playlist': 'mainVideoPlaylistToolbar',
6684                  'video-playlist-edit': 'videoPlaylistEditToolbar',
6685                  'video-playlist-add': 'videoPlaylistAddToolbar'
6686              }
6687          };
6688  
6689          _.each( handlers, function( regionHandlers, region ) {
6690              _.each( regionHandlers, function( callback, handler ) {
6691                  this.on( region + ':render:' + handler, this[ callback ], this );
6692              }, this );
6693          }, this );
6694      },
6695  
6696      activate: function() {
6697          // Hide menu items for states tied to particular media types if there are no items.
6698          _.each( this.counts, function( type ) {
6699              if ( type.count < 1 ) {
6700                  this.menuItemVisibility( type.state, 'hide' );
6701              }
6702          }, this );
6703      },
6704  
6705      mediaTypeCounts: function( model, attr ) {
6706          if ( typeof this.counts[ attr ] !== 'undefined' && this.counts[ attr ].count < 1 ) {
6707              this.counts[ attr ].count++;
6708              this.menuItemVisibility( this.counts[ attr ].state, 'show' );
6709          }
6710      },
6711  
6712      // Menus.
6713      /**
6714       * @param {wp.Backbone.View} view
6715       */
6716      mainMenu: function( view ) {
6717          view.set({
6718              'library-separator': new wp.media.View({
6719                  className:  'separator',
6720                  priority:   100,
6721                  attributes: {
6722                      role: 'presentation'
6723                  }
6724              })
6725          });
6726      },
6727  
6728      menuItemVisibility: function( state, visibility ) {
6729          var menu = this.menu.get();
6730          if ( visibility === 'hide' ) {
6731              menu.hide( state );
6732          } else if ( visibility === 'show' ) {
6733              menu.show( state );
6734          }
6735      },
6736      /**
6737       * @param {wp.Backbone.View} view
6738       */
6739      galleryMenu: function( view ) {
6740          var lastState = this.lastState(),
6741              previous = lastState && lastState.id,
6742              frame = this;
6743  
6744          view.set({
6745              cancel: {
6746                  text:     l10n.cancelGalleryTitle,
6747                  priority: 20,
6748                  click:    function() {
6749                      if ( previous ) {
6750                          frame.setState( previous );
6751                      } else {
6752                          frame.close();
6753                      }
6754  
6755                      // Move focus to the modal after canceling a Gallery.
6756                      this.controller.modal.focusManager.focus();
6757                  }
6758              },
6759              separateCancel: new wp.media.View({
6760                  className: 'separator',
6761                  priority: 40
6762              })
6763          });
6764      },
6765  
6766      playlistMenu: function( view ) {
6767          var lastState = this.lastState(),
6768              previous = lastState && lastState.id,
6769              frame = this;
6770  
6771          view.set({
6772              cancel: {
6773                  text:     l10n.cancelPlaylistTitle,
6774                  priority: 20,
6775                  click:    function() {
6776                      if ( previous ) {
6777                          frame.setState( previous );
6778                      } else {
6779                          frame.close();
6780                      }
6781  
6782                      // Move focus to the modal after canceling an Audio Playlist.
6783                      this.controller.modal.focusManager.focus();
6784                  }
6785              },
6786              separateCancel: new wp.media.View({
6787                  className: 'separator',
6788                  priority: 40
6789              })
6790          });
6791      },
6792  
6793      videoPlaylistMenu: function( view ) {
6794          var lastState = this.lastState(),
6795              previous = lastState && lastState.id,
6796              frame = this;
6797  
6798          view.set({
6799              cancel: {
6800                  text:     l10n.cancelVideoPlaylistTitle,
6801                  priority: 20,
6802                  click:    function() {
6803                      if ( previous ) {
6804                          frame.setState( previous );
6805                      } else {
6806                          frame.close();
6807                      }
6808  
6809                      // Move focus to the modal after canceling a Video Playlist.
6810                      this.controller.modal.focusManager.focus();
6811                  }
6812              },
6813              separateCancel: new wp.media.View({
6814                  className: 'separator',
6815                  priority: 40
6816              })
6817          });
6818      },
6819  
6820      // Content.
6821      embedContent: function() {
6822          var view = new wp.media.view.Embed({
6823              controller: this,
6824              model:      this.state()
6825          }).render();
6826  
6827          this.content.set( view );
6828      },
6829  
6830      editSelectionContent: function() {
6831          var state = this.state(),
6832              selection = state.get('selection'),
6833              view;
6834  
6835          view = new wp.media.view.AttachmentsBrowser({
6836              controller: this,
6837              collection: selection,
6838              selection:  selection,
6839              model:      state,
6840              sortable:   true,
6841              search:     false,
6842              date:       false,
6843              dragInfo:   true,
6844  
6845              AttachmentView: wp.media.view.Attachments.EditSelection
6846          }).render();
6847  
6848          view.toolbar.set( 'backToLibrary', {
6849              text:     l10n.returnToLibrary,
6850              priority: -100,
6851  
6852              click: function() {
6853                  this.controller.content.mode('browse');
6854                  // Move focus to the modal when jumping back from Edit Selection to Add Media view.
6855                  this.controller.modal.focusManager.focus();
6856              }
6857          });
6858  
6859          // Browse our library of attachments.
6860          this.content.set( view );
6861  
6862          // Trigger the controller to set focus.
6863          this.trigger( 'edit:selection', this );
6864      },
6865  
6866      editImageContent: function() {
6867          var image = this.state().get('image'),
6868              view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
6869  
6870          this.content.set( view );
6871  
6872          // After creating the wrapper view, load the actual editor via an Ajax call.
6873          view.loadEditor();
6874  
6875      },
6876  
6877      // Toolbars.
6878  
6879      /**
6880       * @param {wp.Backbone.View} view
6881       */
6882      selectionStatusToolbar: function( view ) {
6883          var editable = this.state().get('editable');
6884  
6885          view.set( 'selection', new wp.media.view.Selection({
6886              controller: this,
6887              collection: this.state().get('selection'),
6888              priority:   -40,
6889  
6890              // If the selection is editable, pass the callback to
6891              // switch the content mode.
6892              editable: editable && function() {
6893                  this.controller.content.mode('edit-selection');
6894              }
6895          }).render() );
6896      },
6897  
6898      /**
6899       * @param {wp.Backbone.View} view
6900       */
6901      mainInsertToolbar: function( view ) {
6902          var controller = this;
6903  
6904          this.selectionStatusToolbar( view );
6905  
6906          view.set( 'insert', {
6907              style:    'primary',
6908              priority: 80,
6909              text:     l10n.insertIntoPost,
6910              requires: { selection: true },
6911  
6912              /**
6913               * @ignore
6914               *
6915               * @fires wp.media.controller.State#insert
6916               */
6917              click: function() {
6918                  var state = controller.state(),
6919                      selection = state.get('selection');
6920  
6921                  controller.close();
6922                  state.trigger( 'insert', selection ).reset();
6923              }
6924          });
6925      },
6926  
6927      /**
6928       * @param {wp.Backbone.View} view
6929       */
6930      mainGalleryToolbar: function( view ) {
6931          var controller = this;
6932  
6933          this.selectionStatusToolbar( view );
6934  
6935          view.set( 'gallery', {
6936              style:    'primary',
6937              text:     l10n.createNewGallery,
6938              priority: 60,
6939              requires: { selection: true },
6940  
6941              click: function() {
6942                  var selection = controller.state().get('selection'),
6943                      edit = controller.state('gallery-edit'),
6944                      models = selection.where({ type: 'image' });
6945  
6946                  edit.set( 'library', new wp.media.model.Selection( models, {
6947                      props:    selection.props.toJSON(),
6948                      multiple: true
6949                  }) );
6950  
6951                  // Jump to Edit Gallery view.
6952                  this.controller.setState( 'gallery-edit' );
6953  
6954                  // Move focus to the modal after jumping to Edit Gallery view.
6955                  this.controller.modal.focusManager.focus();
6956              }
6957          });
6958      },
6959  
6960      mainPlaylistToolbar: function( view ) {
6961          var controller = this;
6962  
6963          this.selectionStatusToolbar( view );
6964  
6965          view.set( 'playlist', {
6966              style:    'primary',
6967              text:     l10n.createNewPlaylist,
6968              priority: 100,
6969              requires: { selection: true },
6970  
6971              click: function() {
6972                  var selection = controller.state().get('selection'),
6973                      edit = controller.state('playlist-edit'),
6974                      models = selection.where({ type: 'audio' });
6975  
6976                  edit.set( 'library', new wp.media.model.Selection( models, {
6977                      props:    selection.props.toJSON(),
6978                      multiple: true
6979                  }) );
6980  
6981                  // Jump to Edit Audio Playlist view.
6982                  this.controller.setState( 'playlist-edit' );
6983  
6984                  // Move focus to the modal after jumping to Edit Audio Playlist view.
6985                  this.controller.modal.focusManager.focus();
6986              }
6987          });
6988      },
6989  
6990      mainVideoPlaylistToolbar: function( view ) {
6991          var controller = this;
6992  
6993          this.selectionStatusToolbar( view );
6994  
6995          view.set( 'video-playlist', {
6996              style:    'primary',
6997              text:     l10n.createNewVideoPlaylist,
6998              priority: 100,
6999              requires: { selection: true },
7000  
7001              click: function() {
7002                  var selection = controller.state().get('selection'),
7003                      edit = controller.state('video-playlist-edit'),
7004                      models = selection.where({ type: 'video' });
7005  
7006                  edit.set( 'library', new wp.media.model.Selection( models, {
7007                      props:    selection.props.toJSON(),
7008                      multiple: true
7009                  }) );
7010  
7011                  // Jump to Edit Video Playlist view.
7012                  this.controller.setState( 'video-playlist-edit' );
7013  
7014                  // Move focus to the modal after jumping to Edit Video Playlist view.
7015                  this.controller.modal.focusManager.focus();
7016              }
7017          });
7018      },
7019  
7020      featuredImageToolbar: function( toolbar ) {
7021          this.createSelectToolbar( toolbar, {
7022              text:  l10n.setFeaturedImage,
7023              state: this.options.state
7024          });
7025      },
7026  
7027      mainEmbedToolbar: function( toolbar ) {
7028          toolbar.view = new wp.media.view.Toolbar.Embed({
7029              controller: this
7030          });
7031      },
7032  
7033      galleryEditToolbar: function() {
7034          var editing = this.state().get('editing');
7035          this.toolbar.set( new wp.media.view.Toolbar({
7036              controller: this,
7037              items: {
7038                  insert: {
7039                      style:    'primary',
7040                      text:     editing ? l10n.updateGallery : l10n.insertGallery,
7041                      priority: 80,
7042                      requires: { library: true },
7043  
7044                      /**
7045                       * @fires wp.media.controller.State#update
7046                       */
7047                      click: function() {
7048                          var controller = this.controller,
7049                              state = controller.state();
7050  
7051                          controller.close();
7052                          state.trigger( 'update', state.get('library') );
7053  
7054                          // Restore and reset the default state.
7055                          controller.setState( controller.options.state );
7056                          controller.reset();
7057                      }
7058                  }
7059              }
7060          }) );
7061      },
7062  
7063      galleryAddToolbar: function() {
7064          this.toolbar.set( new wp.media.view.Toolbar({
7065              controller: this,
7066              items: {
7067                  insert: {
7068                      style:    'primary',
7069                      text:     l10n.addToGallery,
7070                      priority: 80,
7071                      requires: { selection: true },
7072  
7073                      /**
7074                       * @fires wp.media.controller.State#reset
7075                       */
7076                      click: function() {
7077                          var controller = this.controller,
7078                              state = controller.state(),
7079                              edit = controller.state('gallery-edit');
7080  
7081                          edit.get('library').add( state.get('selection').models );
7082                          state.trigger('reset');
7083                          controller.setState('gallery-edit');
7084                          // Move focus to the modal when jumping back from Add to Gallery to Edit Gallery view.
7085                          this.controller.modal.focusManager.focus();
7086                      }
7087                  }
7088              }
7089          }) );
7090      },
7091  
7092      playlistEditToolbar: function() {
7093          var editing = this.state().get('editing');
7094          this.toolbar.set( new wp.media.view.Toolbar({
7095              controller: this,
7096              items: {
7097                  insert: {
7098                      style:    'primary',
7099                      text:     editing ? l10n.updatePlaylist : l10n.insertPlaylist,
7100                      priority: 80,
7101                      requires: { library: true },
7102  
7103                      /**
7104                       * @fires wp.media.controller.State#update
7105                       */
7106                      click: function() {
7107                          var controller = this.controller,
7108                              state = controller.state();
7109  
7110                          controller.close();
7111                          state.trigger( 'update', state.get('library') );
7112  
7113                          // Restore and reset the default state.
7114                          controller.setState( controller.options.state );
7115                          controller.reset();
7116                      }
7117                  }
7118              }
7119          }) );
7120      },
7121  
7122      playlistAddToolbar: function() {
7123          this.toolbar.set( new wp.media.view.Toolbar({
7124              controller: this,
7125              items: {
7126                  insert: {
7127                      style:    'primary',
7128                      text:     l10n.addToPlaylist,
7129                      priority: 80,
7130                      requires: { selection: true },
7131  
7132                      /**
7133                       * @fires wp.media.controller.State#reset
7134                       */
7135                      click: function() {
7136                          var controller = this.controller,
7137                              state = controller.state(),
7138                              edit = controller.state('playlist-edit');
7139  
7140                          edit.get('library').add( state.get('selection').models );
7141                          state.trigger('reset');
7142                          controller.setState('playlist-edit');
7143                          // Move focus to the modal when jumping back from Add to Audio Playlist to Edit Audio Playlist view.
7144                          this.controller.modal.focusManager.focus();
7145                      }
7146                  }
7147              }
7148          }) );
7149      },
7150  
7151      videoPlaylistEditToolbar: function() {
7152          var editing = this.state().get('editing');
7153          this.toolbar.set( new wp.media.view.Toolbar({
7154              controller: this,
7155              items: {
7156                  insert: {
7157                      style:    'primary',
7158                      text:     editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
7159                      priority: 140,
7160                      requires: { library: true },
7161  
7162                      click: function() {
7163                          var controller = this.controller,
7164                              state = controller.state(),
7165                              library = state.get('library');
7166  
7167                          library.type = 'video';
7168  
7169                          controller.close();
7170                          state.trigger( 'update', library );
7171  
7172                          // Restore and reset the default state.
7173                          controller.setState( controller.options.state );
7174                          controller.reset();
7175                      }
7176                  }
7177              }
7178          }) );
7179      },
7180  
7181      videoPlaylistAddToolbar: function() {
7182          this.toolbar.set( new wp.media.view.Toolbar({
7183              controller: this,
7184              items: {
7185                  insert: {
7186                      style:    'primary',
7187                      text:     l10n.addToVideoPlaylist,
7188                      priority: 140,
7189                      requires: { selection: true },
7190  
7191                      click: function() {
7192                          var controller = this.controller,
7193                              state = controller.state(),
7194                              edit = controller.state('video-playlist-edit');
7195  
7196                          edit.get('library').add( state.get('selection').models );
7197                          state.trigger('reset');
7198                          controller.setState('video-playlist-edit');
7199                          // Move focus to the modal when jumping back from Add to Video Playlist to Edit Video Playlist view.
7200                          this.controller.modal.focusManager.focus();
7201                      }
7202                  }
7203              }
7204          }) );
7205      }
7206  });
7207  
7208  module.exports = Post;
7209  
7210  
7211  /***/ }),
7212  
7213  /***/ 455:
7214  /***/ ((module) => {
7215  
7216  var MediaFrame = wp.media.view.MediaFrame,
7217      l10n = wp.media.view.l10n,
7218      Select;
7219  
7220  /**
7221   * wp.media.view.MediaFrame.Select
7222   *
7223   * A frame for selecting an item or items from the media library.
7224   *
7225   * @memberOf wp.media.view.MediaFrame
7226   *
7227   * @class
7228   * @augments wp.media.view.MediaFrame
7229   * @augments wp.media.view.Frame
7230   * @augments wp.media.View
7231   * @augments wp.Backbone.View
7232   * @augments Backbone.View
7233   * @mixes wp.media.controller.StateMachine
7234   */
7235  Select = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Select.prototype */{
7236      initialize: function() {
7237          // Call 'initialize' directly on the parent class.
7238          MediaFrame.prototype.initialize.apply( this, arguments );
7239  
7240          _.defaults( this.options, {
7241              selection: [],
7242              library:   {},
7243              multiple:  false,
7244              state:    'library'
7245          });
7246  
7247          this.createSelection();
7248          this.createStates();
7249          this.bindHandlers();
7250      },
7251  
7252      /**
7253       * Attach a selection collection to the frame.
7254       *
7255       * A selection is a collection of attachments used for a specific purpose
7256       * by a media frame. e.g. Selecting an attachment (or many) to insert into
7257       * post content.
7258       *
7259       * @see media.model.Selection
7260       */
7261      createSelection: function() {
7262          var selection = this.options.selection;
7263  
7264          if ( ! (selection instanceof wp.media.model.Selection) ) {
7265              this.options.selection = new wp.media.model.Selection( selection, {
7266                  multiple: this.options.multiple
7267              });
7268          }
7269  
7270          this._selection = {
7271              attachments: new wp.media.model.Attachments(),
7272              difference: []
7273          };
7274      },
7275  
7276      editImageContent: function() {
7277          var image = this.state().get('image'),
7278              view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
7279  
7280          this.content.set( view );
7281  
7282          // After creating the wrapper view, load the actual editor via an Ajax call.
7283          view.loadEditor();
7284      },
7285  
7286      /**
7287       * Create the default states on the frame.
7288       */
7289      createStates: function() {
7290          var options = this.options;
7291  
7292          if ( this.options.states ) {
7293              return;
7294          }
7295  
7296          // Add the default states.
7297          this.states.add([
7298              // Main states.
7299              new wp.media.controller.Library({
7300                  library:   wp.media.query( options.library ),
7301                  multiple:  options.multiple,
7302                  title:     options.title,
7303                  priority:  20
7304              }),
7305              new wp.media.controller.EditImage( { model: options.editImage } )
7306          ]);
7307      },
7308  
7309      /**
7310       * Bind region mode event callbacks.
7311       *
7312       * @see media.controller.Region.render
7313       */
7314      bindHandlers: function() {
7315          this.on( 'router:create:browse', this.createRouter, this );
7316          this.on( 'router:render:browse', this.browseRouter, this );
7317          this.on( 'content:create:browse', this.browseContent, this );
7318          this.on( 'content:render:upload', this.uploadContent, this );
7319          this.on( 'toolbar:create:select', this.createSelectToolbar, this );
7320          this.on( 'content:render:edit-image', this.editImageContent, this );
7321      },
7322  
7323      /**
7324       * Render callback for the router region in the `browse` mode.
7325       *
7326       * @param {wp.media.view.Router} routerView
7327       */
7328      browseRouter: function( routerView ) {
7329          routerView.set({
7330              upload: {
7331                  text:     l10n.uploadFilesTitle,
7332                  priority: 20
7333              },
7334              browse: {
7335                  text:     l10n.mediaLibraryTitle,
7336                  priority: 40
7337              }
7338          });
7339      },
7340  
7341      /**
7342       * Render callback for the content region in the `browse` mode.
7343       *
7344       * @param {wp.media.controller.Region} contentRegion
7345       */
7346      browseContent: function( contentRegion ) {
7347          var state = this.state();
7348  
7349          this.$el.removeClass('hide-toolbar');
7350  
7351          // Browse our library of attachments.
7352          contentRegion.view = new wp.media.view.AttachmentsBrowser({
7353              controller: this,
7354              collection: state.get('library'),
7355              selection:  state.get('selection'),
7356              model:      state,
7357              sortable:   state.get('sortable'),
7358              search:     state.get('searchable'),
7359              filters:    state.get('filterable'),
7360              date:       state.get('date'),
7361              display:    state.has('display') ? state.get('display') : state.get('displaySettings'),
7362              dragInfo:   state.get('dragInfo'),
7363  
7364              idealColumnWidth: state.get('idealColumnWidth'),
7365              suggestedWidth:   state.get('suggestedWidth'),
7366              suggestedHeight:  state.get('suggestedHeight'),
7367  
7368              AttachmentView: state.get('AttachmentView')
7369          });
7370      },
7371  
7372      /**
7373       * Render callback for the content region in the `upload` mode.
7374       */
7375      uploadContent: function() {
7376          this.$el.removeClass( 'hide-toolbar' );
7377          this.content.set( new wp.media.view.UploaderInline({
7378              controller: this
7379          }) );
7380      },
7381  
7382      /**
7383       * Toolbars
7384       *
7385       * @param {Object} toolbar
7386       * @param {Object} [options={}]
7387       * @this wp.media.controller.Region
7388       */
7389      createSelectToolbar: function( toolbar, options ) {
7390          options = options || this.options.button || {};
7391          options.controller = this;
7392  
7393          toolbar.view = new wp.media.view.Toolbar.Select( options );
7394      }
7395  });
7396  
7397  module.exports = Select;
7398  
7399  
7400  /***/ }),
7401  
7402  /***/ 170:
7403  /***/ ((module) => {
7404  
7405  /**
7406   * wp.media.view.Heading
7407   *
7408   * A reusable heading component for the media library
7409   *
7410   * Used to add accessibility friendly headers in the media library/modal.
7411   *
7412   * @class
7413   * @augments wp.media.View
7414   * @augments wp.Backbone.View
7415   * @augments Backbone.View
7416   */
7417  var Heading = wp.media.View.extend( {
7418      tagName: function() {
7419          return this.options.level || 'h1';
7420      },
7421      className: 'media-views-heading',
7422  
7423      initialize: function() {
7424  
7425          if ( this.options.className ) {
7426              this.$el.addClass( this.options.className );
7427          }
7428  
7429          this.text = this.options.text;
7430      },
7431  
7432      render: function() {
7433          this.$el.html( this.text );
7434          return this;
7435      }
7436  } );
7437  
7438  module.exports = Heading;
7439  
7440  
7441  /***/ }),
7442  
7443  /***/ 1982:
7444  /***/ ((module) => {
7445  
7446  /**
7447   * wp.media.view.Iframe
7448   *
7449   * @memberOf wp.media.view
7450   *
7451   * @class
7452   * @augments wp.media.View
7453   * @augments wp.Backbone.View
7454   * @augments Backbone.View
7455   */
7456  var Iframe = wp.media.View.extend(/** @lends wp.media.view.Iframe.prototype */{
7457      className: 'media-iframe',
7458      /**
7459       * @return {wp.media.view.Iframe} Returns itself to allow chaining.
7460       */
7461      render: function() {
7462          this.views.detach();
7463          this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
7464          this.views.render();
7465          return this;
7466      }
7467  });
7468  
7469  module.exports = Iframe;
7470  
7471  
7472  /***/ }),
7473  
7474  /***/ 2650:
7475  /***/ ((module) => {
7476  
7477  var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
7478      $ = jQuery,
7479      ImageDetails;
7480  
7481  /**
7482   * wp.media.view.ImageDetails
7483   *
7484   * @memberOf wp.media.view
7485   *
7486   * @class
7487   * @augments wp.media.view.Settings.AttachmentDisplay
7488   * @augments wp.media.view.Settings
7489   * @augments wp.media.View
7490   * @augments wp.Backbone.View
7491   * @augments Backbone.View
7492   */
7493  ImageDetails = AttachmentDisplay.extend(/** @lends wp.media.view.ImageDetails.prototype */{
7494      className: 'image-details',
7495      template:  wp.template('image-details'),
7496      events: _.defaults( AttachmentDisplay.prototype.events, {
7497          'click .edit-attachment': 'editAttachment',
7498          'click .replace-attachment': 'replaceAttachment',
7499          'click .advanced-toggle': 'onToggleAdvanced',
7500          'change [data-setting="customWidth"]': 'onCustomSize',
7501          'change [data-setting="customHeight"]': 'onCustomSize',
7502          'keyup [data-setting="customWidth"]': 'onCustomSize',
7503          'keyup [data-setting="customHeight"]': 'onCustomSize'
7504      } ),
7505      initialize: function() {
7506          // Used in AttachmentDisplay.prototype.updateLinkTo.
7507          this.options.attachment = this.model.attachment;
7508          this.listenTo( this.model, 'change:url', this.updateUrl );
7509          this.listenTo( this.model, 'change:link', this.toggleLinkSettings );
7510          this.listenTo( this.model, 'change:size', this.toggleCustomSize );
7511  
7512          AttachmentDisplay.prototype.initialize.apply( this, arguments );
7513      },
7514  
7515      prepare: function() {
7516          var attachment = false;
7517  
7518          if ( this.model.attachment ) {
7519              attachment = this.model.attachment.toJSON();
7520          }
7521          return _.defaults({
7522              model: this.model.toJSON(),
7523              attachment: attachment
7524          }, this.options );
7525      },
7526  
7527      render: function() {
7528          var args = arguments;
7529  
7530          if ( this.model.attachment && 'pending' === this.model.dfd.state() ) {
7531              this.model.dfd
7532                  .done( _.bind( function() {
7533                      AttachmentDisplay.prototype.render.apply( this, args );
7534                      this.postRender();
7535                  }, this ) )
7536                  .fail( _.bind( function() {
7537                      this.model.attachment = false;
7538                      AttachmentDisplay.prototype.render.apply( this, args );
7539                      this.postRender();
7540                  }, this ) );
7541          } else {
7542              AttachmentDisplay.prototype.render.apply( this, arguments );
7543              this.postRender();
7544          }
7545  
7546          return this;
7547      },
7548  
7549      postRender: function() {
7550          setTimeout( _.bind( this.scrollToTop, this ), 10 );
7551          this.toggleLinkSettings();
7552          if ( window.getUserSetting( 'advImgDetails' ) === 'show' ) {
7553              this.toggleAdvanced( true );
7554          }
7555          this.trigger( 'post-render' );
7556      },
7557  
7558      scrollToTop: function() {
7559          this.$( '.embed-media-settings' ).scrollTop( 0 );
7560      },
7561  
7562      updateUrl: function() {
7563          this.$( '.image img' ).attr( 'src', this.model.get( 'url' ) );
7564          this.$( '.url' ).val( this.model.get( 'url' ) );
7565      },
7566  
7567      toggleLinkSettings: function() {
7568          if ( this.model.get( 'link' ) === 'none' ) {
7569              this.$( '.link-settings' ).addClass('hidden');
7570          } else {
7571              this.$( '.link-settings' ).removeClass('hidden');
7572          }
7573      },
7574  
7575      toggleCustomSize: function() {
7576          if ( this.model.get( 'size' ) !== 'custom' ) {
7577              this.$( '.custom-size' ).addClass('hidden');
7578          } else {
7579              this.$( '.custom-size' ).removeClass('hidden');
7580          }
7581      },
7582  
7583      onCustomSize: function( event ) {
7584          var dimension = $( event.target ).data('setting'),
7585              num = $( event.target ).val(),
7586              value;
7587  
7588          // Ignore bogus input.
7589          if ( ! /^\d+/.test( num ) || parseInt( num, 10 ) < 1 ) {
7590              event.preventDefault();
7591              return;
7592          }
7593  
7594          if ( dimension === 'customWidth' ) {
7595              value = Math.round( 1 / this.model.get( 'aspectRatio' ) * num );
7596              this.model.set( 'customHeight', value, { silent: true } );
7597              this.$( '[data-setting="customHeight"]' ).val( value );
7598          } else {
7599              value = Math.round( this.model.get( 'aspectRatio' ) * num );
7600              this.model.set( 'customWidth', value, { silent: true  } );
7601              this.$( '[data-setting="customWidth"]' ).val( value );
7602          }
7603      },
7604  
7605      onToggleAdvanced: function( event ) {
7606          event.preventDefault();
7607          this.toggleAdvanced();
7608      },
7609  
7610      toggleAdvanced: function( show ) {
7611          var $advanced = this.$el.find( '.advanced-section' ),
7612              mode;
7613  
7614          if ( $advanced.hasClass('advanced-visible') || show === false ) {
7615              $advanced.removeClass('advanced-visible');
7616              $advanced.find('.advanced-settings').addClass('hidden');
7617              mode = 'hide';
7618          } else {
7619              $advanced.addClass('advanced-visible');
7620              $advanced.find('.advanced-settings').removeClass('hidden');
7621              mode = 'show';
7622          }
7623  
7624          window.setUserSetting( 'advImgDetails', mode );
7625      },
7626  
7627      editAttachment: function( event ) {
7628          var editState = this.controller.states.get( 'edit-image' );
7629  
7630          if ( window.imageEdit && editState ) {
7631              event.preventDefault();
7632              editState.set( 'image', this.model.attachment );
7633              this.controller.setState( 'edit-image' );
7634          }
7635      },
7636  
7637      replaceAttachment: function( event ) {
7638          event.preventDefault();
7639          this.controller.setState( 'replace-image' );
7640      }
7641  });
7642  
7643  module.exports = ImageDetails;
7644  
7645  
7646  /***/ }),
7647  
7648  /***/ 4338:
7649  /***/ ((module) => {
7650  
7651  /**
7652   * wp.media.view.Label
7653   *
7654   * @memberOf wp.media.view
7655   *
7656   * @class
7657   * @augments wp.media.View
7658   * @augments wp.Backbone.View
7659   * @augments Backbone.View
7660   */
7661  var Label = wp.media.View.extend(/** @lends wp.media.view.Label.prototype */{
7662      tagName: 'label',
7663      className: 'screen-reader-text',
7664  
7665      initialize: function() {
7666          this.value = this.options.value;
7667      },
7668  
7669      render: function() {
7670          this.$el.html( this.value );
7671  
7672          return this;
7673      }
7674  });
7675  
7676  module.exports = Label;
7677  
7678  
7679  /***/ }),
7680  
7681  /***/ 2836:
7682  /***/ ((module) => {
7683  
7684  var Frame = wp.media.view.Frame,
7685      l10n = wp.media.view.l10n,
7686      $ = jQuery,
7687      MediaFrame;
7688  
7689  /**
7690   * wp.media.view.MediaFrame
7691   *
7692   * The frame used to create the media modal.
7693   *
7694   * @memberOf wp.media.view
7695   *
7696   * @class
7697   * @augments wp.media.view.Frame
7698   * @augments wp.media.View
7699   * @augments wp.Backbone.View
7700   * @augments Backbone.View
7701   * @mixes wp.media.controller.StateMachine
7702   */
7703  MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{
7704      className: 'media-frame',
7705      template:  wp.template('media-frame'),
7706      regions:   ['menu','title','content','toolbar','router'],
7707  
7708      events: {
7709          'click .media-frame-menu-toggle': 'toggleMenu'
7710      },
7711  
7712      /**
7713       * @constructs
7714       */
7715      initialize: function() {
7716          Frame.prototype.initialize.apply( this, arguments );
7717  
7718          _.defaults( this.options, {
7719              title:    l10n.mediaFrameDefaultTitle,
7720              modal:    true,
7721              uploader: true
7722          });
7723  
7724          // Ensure core UI is enabled.
7725          this.$el.addClass('wp-core-ui');
7726  
7727          // Initialize modal container view.
7728          if ( this.options.modal ) {
7729              this.modal = new wp.media.view.Modal({
7730                  controller: this,
7731                  title:      this.options.title
7732              });
7733  
7734              this.modal.content( this );
7735          }
7736  
7737          // Force the uploader off if the upload limit has been exceeded or
7738          // if the browser isn't supported.
7739          if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
7740              this.options.uploader = false;
7741          }
7742  
7743          // Initialize window-wide uploader.
7744          if ( this.options.uploader ) {
7745              this.uploader = new wp.media.view.UploaderWindow({
7746                  controller: this,
7747                  uploader: {
7748                      dropzone:  this.modal ? this.modal.$el : this.$el,
7749                      container: this.$el
7750                  }
7751              });
7752              this.views.set( '.media-frame-uploader', this.uploader );
7753          }
7754  
7755          this.on( 'attach', _.bind( this.views.ready, this.views ), this );
7756  
7757          // Bind default title creation.
7758          this.on( 'title:create:default', this.createTitle, this );
7759          this.title.mode('default');
7760  
7761          // Bind default menu.
7762          this.on( 'menu:create:default', this.createMenu, this );
7763  
7764          // Set the menu ARIA tab panel attributes when the modal opens.
7765          this.on( 'open', this.setMenuTabPanelAriaAttributes, this );
7766          // Set the router ARIA tab panel attributes when the modal opens.
7767          this.on( 'open', this.setRouterTabPanelAriaAttributes, this );
7768  
7769          // Update the menu ARIA tab panel attributes when the content updates.
7770          this.on( 'content:render', this.setMenuTabPanelAriaAttributes, this );
7771          // Update the router ARIA tab panel attributes when the content updates.
7772          this.on( 'content:render', this.setRouterTabPanelAriaAttributes, this );
7773      },
7774  
7775      /**
7776       * Sets the attributes to be used on the menu ARIA tab panel.
7777       *
7778       * @since 5.3.0
7779       *
7780       * @return {void}
7781       */
7782      setMenuTabPanelAriaAttributes: function() {
7783          var stateId = this.state().get( 'id' ),
7784              tabPanelEl = this.$el.find( '.media-frame-tab-panel' ),
7785              ariaLabelledby;
7786  
7787          tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
7788  
7789          if ( this.state().get( 'menu' ) && this.menuView && this.menuView.isVisible ) {
7790              ariaLabelledby = 'menu-item-' + stateId;
7791  
7792              // Set the tab panel attributes only if the tabs are visible.
7793              tabPanelEl
7794                  .attr( {
7795                      role: 'tabpanel',
7796                      'aria-labelledby': ariaLabelledby,
7797                      tabIndex: '0'
7798                  } );
7799          }
7800      },
7801  
7802      /**
7803       * Sets the attributes to be used on the router ARIA tab panel.
7804       *
7805       * @since 5.3.0
7806       *
7807       * @return {void}
7808       */
7809      setRouterTabPanelAriaAttributes: function() {
7810          var tabPanelEl = this.$el.find( '.media-frame-content' ),
7811              ariaLabelledby;
7812  
7813          tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
7814  
7815          // Set the tab panel attributes only if the tabs are visible.
7816          if ( this.state().get( 'router' ) && this.routerView && this.routerView.isVisible && this.content._mode ) {
7817              ariaLabelledby = 'menu-item-' + this.content._mode;
7818  
7819              tabPanelEl
7820                  .attr( {
7821                      role: 'tabpanel',
7822                      'aria-labelledby': ariaLabelledby,
7823                      tabIndex: '0'
7824                  } );
7825          }
7826      },
7827  
7828      /**
7829       * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
7830       */
7831      render: function() {
7832          // Activate the default state if no active state exists.
7833          if ( ! this.state() && this.options.state ) {
7834              this.setState( this.options.state );
7835          }
7836          /**
7837           * call 'render' directly on the parent class
7838           */
7839          return Frame.prototype.render.apply( this, arguments );
7840      },
7841      /**
7842       * @param {Object} title
7843       * @this wp.media.controller.Region
7844       */
7845      createTitle: function( title ) {
7846          title.view = new wp.media.View({
7847              controller: this,
7848              tagName: 'h1'
7849          });
7850      },
7851      /**
7852       * @param {Object} menu
7853       * @this wp.media.controller.Region
7854       */
7855      createMenu: function( menu ) {
7856          menu.view = new wp.media.view.Menu({
7857              controller: this,
7858  
7859              attributes: {
7860                  role:               'tablist',
7861                  'aria-orientation': 'vertical'
7862              }
7863          });
7864  
7865          this.menuView = menu.view;
7866      },
7867  
7868      toggleMenu: function( event ) {
7869          var menu = this.$el.find( '.media-menu' );
7870  
7871          menu.toggleClass( 'visible' );
7872          $( event.target ).attr( 'aria-expanded', menu.hasClass( 'visible' ) );
7873      },
7874  
7875      /**
7876       * @param {Object} toolbar
7877       * @this wp.media.controller.Region
7878       */
7879      createToolbar: function( toolbar ) {
7880          toolbar.view = new wp.media.view.Toolbar({
7881              controller: this
7882          });
7883      },
7884      /**
7885       * @param {Object} router
7886       * @this wp.media.controller.Region
7887       */
7888      createRouter: function( router ) {
7889          router.view = new wp.media.view.Router({
7890              controller: this,
7891  
7892              attributes: {
7893                  role:               'tablist',
7894                  'aria-orientation': 'horizontal'
7895              }
7896          });
7897  
7898          this.routerView = router.view;
7899      },
7900      /**
7901       * @param {Object} options
7902       */
7903      createIframeStates: function( options ) {
7904          var settings = wp.media.view.settings,
7905              tabs = settings.tabs,
7906              tabUrl = settings.tabUrl,
7907              $postId;
7908  
7909          if ( ! tabs || ! tabUrl ) {
7910              return;
7911          }
7912  
7913          // Add the post ID to the tab URL if it exists.
7914          $postId = $('#post_ID');
7915          if ( $postId.length ) {
7916              tabUrl += '&post_id=' + $postId.val();
7917          }
7918  
7919          // Generate the tab states.
7920          _.each( tabs, function( title, id ) {
7921              this.state( 'iframe:' + id ).set( _.defaults({
7922                  tab:     id,
7923                  src:     tabUrl + '&tab=' + id,
7924                  title:   title,
7925                  content: 'iframe',
7926                  menu:    'default'
7927              }, options ) );
7928          }, this );
7929  
7930          this.on( 'content:create:iframe', this.iframeContent, this );
7931          this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
7932          this.on( 'menu:render:default', this.iframeMenu, this );
7933          this.on( 'open', this.hijackThickbox, this );
7934          this.on( 'close', this.restoreThickbox, this );
7935      },
7936  
7937      /**
7938       * @param {Object} content
7939       * @this wp.media.controller.Region
7940       */
7941      iframeContent: function( content ) {
7942          this.$el.addClass('hide-toolbar');
7943          content.view = new wp.media.view.Iframe({
7944              controller: this
7945          });
7946      },
7947  
7948      iframeContentCleanup: function() {
7949          this.$el.removeClass('hide-toolbar');
7950      },
7951  
7952      iframeMenu: function( view ) {
7953          var views = {};
7954  
7955          if ( ! view ) {
7956              return;
7957          }
7958  
7959          _.each( wp.media.view.settings.tabs, function( title, id ) {
7960              views[ 'iframe:' + id ] = {
7961                  text: this.state( 'iframe:' + id ).get('title'),
7962                  priority: 200
7963              };
7964          }, this );
7965  
7966          view.set( views );
7967      },
7968  
7969      hijackThickbox: function() {
7970          var frame = this;
7971  
7972          if ( ! window.tb_remove || this._tb_remove ) {
7973              return;
7974          }
7975  
7976          this._tb_remove = window.tb_remove;
7977          window.tb_remove = function() {
7978              frame.close();
7979              frame.reset();
7980              frame.setState( frame.options.state );
7981              frame._tb_remove.call( window );
7982          };
7983      },
7984  
7985      restoreThickbox: function() {
7986          if ( ! this._tb_remove ) {
7987              return;
7988          }
7989  
7990          window.tb_remove = this._tb_remove;
7991          delete this._tb_remove;
7992      }
7993  });
7994  
7995  // Map some of the modal's methods to the frame.
7996  _.each(['open','close','attach','detach','escape'], function( method ) {
7997      /**
7998       * @function open
7999       * @memberOf wp.media.view.MediaFrame
8000       * @instance
8001       *
8002       * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
8003       */
8004      /**
8005       * @function close
8006       * @memberOf wp.media.view.MediaFrame
8007       * @instance
8008       *
8009       * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
8010       */
8011      /**
8012       * @function attach
8013       * @memberOf wp.media.view.MediaFrame
8014       * @instance
8015       *
8016       * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
8017       */
8018      /**
8019       * @function detach
8020       * @memberOf wp.media.view.MediaFrame
8021       * @instance
8022       *
8023       * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
8024       */
8025      /**
8026       * @function escape
8027       * @memberOf wp.media.view.MediaFrame
8028       * @instance
8029       *
8030       * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
8031       */
8032      MediaFrame.prototype[ method ] = function() {
8033          if ( this.modal ) {
8034              this.modal[ method ].apply( this.modal, arguments );
8035          }
8036          return this;
8037      };
8038  });
8039  
8040  module.exports = MediaFrame;
8041  
8042  
8043  /***/ }),
8044  
8045  /***/ 9013:
8046  /***/ ((module) => {
8047  
8048  var MenuItem;
8049  
8050  /**
8051   * wp.media.view.MenuItem
8052   *
8053   * @memberOf wp.media.view
8054   *
8055   * @class
8056   * @augments wp.media.View
8057   * @augments wp.Backbone.View
8058   * @augments Backbone.View
8059   */
8060  MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{
8061      tagName:   'button',
8062      className: 'media-menu-item',
8063  
8064      attributes: {
8065          type: 'button',
8066          role: 'tab'
8067      },
8068  
8069      events: {
8070          'click': '_click'
8071      },
8072  
8073      /**
8074       * Allows to override the click event.
8075       */
8076      _click: function() {
8077          var clickOverride = this.options.click;
8078  
8079          if ( clickOverride ) {
8080              clickOverride.call( this );
8081          } else {
8082              this.click();
8083          }
8084      },
8085  
8086      click: function() {
8087          var state = this.options.state;
8088  
8089          if ( state ) {
8090              this.controller.setState( state );
8091              // Toggle the menu visibility in the responsive view.
8092              this.views.parent.$el.removeClass( 'visible' ); // @todo Or hide on any click, see below.
8093          }
8094      },
8095  
8096      /**
8097       * @return {wp.media.view.MenuItem} returns itself to allow chaining.
8098       */
8099      render: function() {
8100          var options = this.options,
8101              menuProperty = options.state || options.contentMode;
8102  
8103          if ( options.text ) {
8104              this.$el.text( options.text );
8105          } else if ( options.html ) {
8106              this.$el.html( options.html );
8107          }
8108  
8109          // Set the menu item ID based on the frame state associated to the menu item.
8110          this.$el.attr( 'id', 'menu-item-' + menuProperty );
8111  
8112          return this;
8113      }
8114  });
8115  
8116  module.exports = MenuItem;
8117  
8118  
8119  /***/ }),
8120  
8121  /***/ 1:
8122  /***/ ((module) => {
8123  
8124  var MenuItem = wp.media.view.MenuItem,
8125      PriorityList = wp.media.view.PriorityList,
8126      Menu;
8127  
8128  /**
8129   * wp.media.view.Menu
8130   *
8131   * @memberOf wp.media.view
8132   *
8133   * @class
8134   * @augments wp.media.view.PriorityList
8135   * @augments wp.media.View
8136   * @augments wp.Backbone.View
8137   * @augments Backbone.View
8138   */
8139  Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{
8140      tagName:   'div',
8141      className: 'media-menu',
8142      property:  'state',
8143      ItemView:  MenuItem,
8144      region:    'menu',
8145  
8146      attributes: {
8147          role:               'tablist',
8148          'aria-orientation': 'horizontal'
8149      },
8150  
8151      initialize: function() {
8152          this._views = {};
8153  
8154          this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
8155          delete this.options.views;
8156  
8157          if ( ! this.options.silent ) {
8158              this.render();
8159          }
8160  
8161          // Initialize the Focus Manager.
8162          this.focusManager = new wp.media.view.FocusManager( {
8163              el:   this.el,
8164              mode: 'tabsNavigation'
8165          } );
8166  
8167          // The menu is always rendered and can be visible or hidden on some frames.
8168          this.isVisible = true;
8169      },
8170  
8171      /**
8172       * @param {Object} options
8173       * @param {string} id
8174       * @return {wp.media.View}
8175       */
8176      toView: function( options, id ) {
8177          options = options || {};
8178          options[ this.property ] = options[ this.property ] || id;
8179          return new this.ItemView( options ).render();
8180      },
8181  
8182      ready: function() {
8183          /**
8184           * call 'ready' directly on the parent class
8185           */
8186          PriorityList.prototype.ready.apply( this, arguments );
8187          this.visibility();
8188  
8189          // Set up aria tabs initial attributes.
8190          this.focusManager.setupAriaTabs();
8191      },
8192  
8193      set: function() {
8194          /**
8195           * call 'set' directly on the parent class
8196           */
8197          PriorityList.prototype.set.apply( this, arguments );
8198          this.visibility();
8199      },
8200  
8201      unset: function() {
8202          /**
8203           * call 'unset' directly on the parent class
8204           */
8205          PriorityList.prototype.unset.apply( this, arguments );
8206          this.visibility();
8207      },
8208  
8209      visibility: function() {
8210          var region = this.region,
8211              view = this.controller[ region ].get(),
8212              views = this.views.get(),
8213              hide = ! views || views.length < 2;
8214  
8215          if ( this === view ) {
8216              // Flag this menu as hidden or visible.
8217              this.isVisible = ! hide;
8218              // Set or remove a CSS class to hide the menu.
8219              this.controller.$el.toggleClass( 'hide-' + region, hide );
8220          }
8221      },
8222      /**
8223       * @param {string} id
8224       */
8225      select: function( id ) {
8226          var view = this.get( id );
8227  
8228          if ( ! view ) {
8229              return;
8230          }
8231  
8232          this.deselect();
8233          view.$el.addClass('active');
8234  
8235          // Set up again the aria tabs initial attributes after the menu updates.
8236          this.focusManager.setupAriaTabs();
8237      },
8238  
8239      deselect: function() {
8240          this.$el.children().removeClass('active');
8241      },
8242  
8243      hide: function( id ) {
8244          var view = this.get( id );
8245  
8246          if ( ! view ) {
8247              return;
8248          }
8249  
8250          view.$el.addClass('hidden');
8251      },
8252  
8253      show: function( id ) {
8254          var view = this.get( id );
8255  
8256          if ( ! view ) {
8257              return;
8258          }
8259  
8260          view.$el.removeClass('hidden');
8261      }
8262  });
8263  
8264  module.exports = Menu;
8265  
8266  
8267  /***/ }),
8268  
8269  /***/ 2621:
8270  /***/ ((module) => {
8271  
8272  var $ = jQuery,
8273      Modal;
8274  
8275  /**
8276   * wp.media.view.Modal
8277   *
8278   * A modal view, which the media modal uses as its default container.
8279   *
8280   * @memberOf wp.media.view
8281   *
8282   * @class
8283   * @augments wp.media.View
8284   * @augments wp.Backbone.View
8285   * @augments Backbone.View
8286   */
8287  Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{
8288      tagName:  'div',
8289      template: wp.template('media-modal'),
8290  
8291      events: {
8292          'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
8293          'keydown': 'keydown'
8294      },
8295  
8296      clickedOpenerEl: null,
8297  
8298      initialize: function() {
8299          _.defaults( this.options, {
8300              container:      document.body,
8301              title:          '',
8302              propagate:      true,
8303              hasCloseButton: true
8304          });
8305  
8306          this.focusManager = new wp.media.view.FocusManager({
8307              el: this.el
8308          });
8309      },
8310      /**
8311       * @return {Object}
8312       */
8313      prepare: function() {
8314          return {
8315              title:          this.options.title,
8316              hasCloseButton: this.options.hasCloseButton
8317          };
8318      },
8319  
8320      /**
8321       * @return {wp.media.view.Modal} Returns itself to allow chaining.
8322       */
8323      attach: function() {
8324          if ( this.views.attached ) {
8325              return this;
8326          }
8327  
8328          if ( ! this.views.rendered ) {
8329              this.render();
8330          }
8331  
8332          this.$el.appendTo( this.options.container );
8333  
8334          // Manually mark the view as attached and trigger ready.
8335          this.views.attached = true;
8336          this.views.ready();
8337  
8338          return this.propagate('attach');
8339      },
8340  
8341      /**
8342       * @return {wp.media.view.Modal} Returns itself to allow chaining.
8343       */
8344      detach: function() {
8345          if ( this.$el.is(':visible') ) {
8346              this.close();
8347          }
8348  
8349          this.$el.detach();
8350          this.views.attached = false;
8351          return this.propagate('detach');
8352      },
8353  
8354      /**
8355       * @return {wp.media.view.Modal} Returns itself to allow chaining.
8356       */
8357      open: function() {
8358          var $el = this.$el,
8359              mceEditor;
8360  
8361          if ( $el.is(':visible') ) {
8362              return this;
8363          }
8364  
8365          this.clickedOpenerEl = document.activeElement;
8366  
8367          if ( ! this.views.attached ) {
8368              this.attach();
8369          }
8370  
8371          // Disable page scrolling.
8372          $( 'body' ).addClass( 'modal-open' );
8373  
8374          $el.show();
8375  
8376          // Try to close the onscreen keyboard.
8377          if ( 'ontouchend' in document ) {
8378              if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor ) && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
8379                  mceEditor.iframeElement.focus();
8380                  mceEditor.iframeElement.blur();
8381  
8382                  setTimeout( function() {
8383                      mceEditor.iframeElement.blur();
8384                  }, 100 );
8385              }
8386          }
8387  
8388          // Set initial focus on the content instead of this view element, to avoid page scrolling.
8389          this.$( '.media-modal' ).trigger( 'focus' );
8390  
8391          // Hide the page content from assistive technologies.
8392          this.focusManager.setAriaHiddenOnBodyChildren( $el );
8393  
8394          return this.propagate('open');
8395      },
8396  
8397      /**
8398       * @param {Object} options
8399       * @return {wp.media.view.Modal} Returns itself to allow chaining.
8400       */
8401      close: function( options ) {
8402          if ( ! this.views.attached || ! this.$el.is(':visible') ) {
8403              return this;
8404          }
8405  
8406          // Pause current audio/video even after closing the modal.
8407          $( '.mejs-pause button' ).trigger( 'click' );
8408  
8409          // Enable page scrolling.
8410          $( 'body' ).removeClass( 'modal-open' );
8411  
8412          // Hide the modal element by adding display:none.
8413          this.$el.hide();
8414  
8415          /*
8416           * Make visible again to assistive technologies all body children that
8417           * have been made hidden when the modal opened.
8418           */
8419          this.focusManager.removeAriaHiddenFromBodyChildren();
8420  
8421          // Move focus back in useful location once modal is closed.
8422          if ( null !== this.clickedOpenerEl ) {
8423              // Move focus back to the element that opened the modal.
8424              this.clickedOpenerEl.focus();
8425          } else {
8426              // Fallback to the admin page main element.
8427              $( '#wpbody-content' )
8428                  .attr( 'tabindex', '-1' )
8429                  .trigger( 'focus' );
8430          }
8431  
8432          this.propagate('close');
8433  
8434          if ( options && options.escape ) {
8435              this.propagate('escape');
8436          }
8437  
8438          return this;
8439      },
8440      /**
8441       * @return {wp.media.view.Modal} Returns itself to allow chaining.
8442       */
8443      escape: function() {
8444          return this.close({ escape: true });
8445      },
8446      /**
8447       * @param {Object} event
8448       */
8449      escapeHandler: function( event ) {
8450          event.preventDefault();
8451          this.escape();
8452      },
8453  
8454      /**
8455       * Handles the selection of attachments when the command or control key is pressed with the enter key.
8456       *
8457       * @since 6.7
8458       *
8459       * @param {Object} event The keydown event object.
8460       */
8461      selectHandler: function( event ) {
8462          var selection = this.controller.state().get( 'selection' );
8463  
8464          if ( selection.length <= 0 ) {
8465              return;
8466          }
8467  
8468          if ( 'insert' === this.controller.options.state ) {
8469              this.controller.trigger( 'insert', selection );
8470          } else {
8471              this.controller.trigger( 'select', selection );
8472              event.preventDefault();
8473              this.escape();
8474          }
8475      },
8476  
8477      /**
8478       * @param {Array|Object} content Views to register to '.media-modal-content'
8479       * @return {wp.media.view.Modal} Returns itself to allow chaining.
8480       */
8481      content: function( content ) {
8482          this.views.set( '.media-modal-content', content );
8483          return this;
8484      },
8485  
8486      /**
8487       * Triggers a modal event and if the `propagate` option is set,
8488       * forwards events to the modal's controller.
8489       *
8490       * @param {string} id
8491       * @return {wp.media.view.Modal} Returns itself to allow chaining.
8492       */
8493      propagate: function( id ) {
8494          this.trigger( id );
8495  
8496          if ( this.options.propagate ) {
8497              this.controller.trigger( id );
8498          }
8499  
8500          return this;
8501      },
8502      /**
8503       * @param {Object} event
8504       */
8505      keydown: function( event ) {
8506          // Close the modal when escape is pressed.
8507          if ( 27 === event.which && this.$el.is(':visible') ) {
8508              this.escape();
8509              event.stopImmediatePropagation();
8510          }
8511  
8512          // Select the attachment when command or control and enter are pressed.
8513          if ( ( 13 === event.which || 10 === event.which ) && ( event.metaKey || event.ctrlKey ) ) {
8514              this.selectHandler( event );
8515              event.stopImmediatePropagation();
8516          }
8517  
8518      }
8519  });
8520  
8521  module.exports = Modal;
8522  
8523  
8524  /***/ }),
8525  
8526  /***/ 8815:
8527  /***/ ((module) => {
8528  
8529  /**
8530   * wp.media.view.PriorityList
8531   *
8532   * @memberOf wp.media.view
8533   *
8534   * @class
8535   * @augments wp.media.View
8536   * @augments wp.Backbone.View
8537   * @augments Backbone.View
8538   */
8539  var PriorityList = wp.media.View.extend(/** @lends wp.media.view.PriorityList.prototype */{
8540      tagName:   'div',
8541  
8542      initialize: function() {
8543          this._views = {};
8544  
8545          this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
8546          delete this.options.views;
8547  
8548          if ( ! this.options.silent ) {
8549              this.render();
8550          }
8551      },
8552      /**
8553       * @param {string} id
8554       * @param {wp.media.View|Object} view
8555       * @param {Object} options
8556       * @return {wp.media.view.PriorityList} Returns itself to allow chaining.
8557       */
8558      set: function( id, view, options ) {
8559          var priority, views, index;
8560  
8561          options = options || {};
8562  
8563          // Accept an object with an `id` : `view` mapping.
8564          if ( _.isObject( id ) ) {
8565              _.each( id, function( view, id ) {
8566                  this.set( id, view );
8567              }, this );
8568              return this;
8569          }
8570  
8571          if ( ! (view instanceof Backbone.View) ) {
8572              view = this.toView( view, id, options );
8573          }
8574          view.controller = view.controller || this.controller;
8575  
8576          this.unset( id );
8577  
8578          priority = view.options.priority || 10;
8579          views = this.views.get() || [];
8580  
8581          _.find( views, function( existing, i ) {
8582              if ( existing.options.priority > priority ) {
8583                  index = i;
8584                  return true;
8585              }
8586          });
8587  
8588          this._views[ id ] = view;
8589          this.views.add( view, {
8590              at: _.isNumber( index ) ? index : views.length || 0
8591          });
8592  
8593          return this;
8594      },
8595      /**
8596       * @param {string} id
8597       * @return {wp.media.View}
8598       */
8599      get: function( id ) {
8600          return this._views[ id ];
8601      },
8602      /**
8603       * @param {string} id
8604       * @return {wp.media.view.PriorityList}
8605       */
8606      unset: function( id ) {
8607          var view = this.get( id );
8608  
8609          if ( view ) {
8610              view.remove();
8611          }
8612  
8613          delete this._views[ id ];
8614          return this;
8615      },
8616      /**
8617       * @param {Object} options
8618       * @return {wp.media.View}
8619       */
8620      toView: function( options ) {
8621          return new wp.media.View( options );
8622      }
8623  });
8624  
8625  module.exports = PriorityList;
8626  
8627  
8628  /***/ }),
8629  
8630  /***/ 6327:
8631  /***/ ((module) => {
8632  
8633  /**
8634   * wp.media.view.RouterItem
8635   *
8636   * @memberOf wp.media.view
8637   *
8638   * @class
8639   * @augments wp.media.view.MenuItem
8640   * @augments wp.media.View
8641   * @augments wp.Backbone.View
8642   * @augments Backbone.View
8643   */
8644  var RouterItem = wp.media.view.MenuItem.extend(/** @lends wp.media.view.RouterItem.prototype */{
8645      /**
8646       * On click handler to activate the content region's corresponding mode.
8647       */
8648      click: function() {
8649          var contentMode = this.options.contentMode;
8650          if ( contentMode ) {
8651              this.controller.content.mode( contentMode );
8652          }
8653      }
8654  });
8655  
8656  module.exports = RouterItem;
8657  
8658  
8659  /***/ }),
8660  
8661  /***/ 4783:
8662  /***/ ((module) => {
8663  
8664  var Menu = wp.media.view.Menu,
8665      Router;
8666  
8667  /**
8668   * wp.media.view.Router
8669   *
8670   * @memberOf wp.media.view
8671   *
8672   * @class
8673   * @augments wp.media.view.Menu
8674   * @augments wp.media.view.PriorityList
8675   * @augments wp.media.View
8676   * @augments wp.Backbone.View
8677   * @augments Backbone.View
8678   */
8679  Router = Menu.extend(/** @lends wp.media.view.Router.prototype */{
8680      tagName:   'div',
8681      className: 'media-router',
8682      property:  'contentMode',
8683      ItemView:  wp.media.view.RouterItem,
8684      region:    'router',
8685  
8686      attributes: {
8687          role:               'tablist',
8688          'aria-orientation': 'horizontal'
8689      },
8690  
8691      initialize: function() {
8692          this.controller.on( 'content:render', this.update, this );
8693          // Call 'initialize' directly on the parent class.
8694          Menu.prototype.initialize.apply( this, arguments );
8695      },
8696  
8697      update: function() {
8698          var mode = this.controller.content.mode();
8699          if ( mode ) {
8700              this.select( mode );
8701          }
8702      }
8703  });
8704  
8705  module.exports = Router;
8706  
8707  
8708  /***/ }),
8709  
8710  /***/ 2102:
8711  /***/ ((module) => {
8712  
8713  var Search;
8714  
8715  /**
8716   * wp.media.view.Search
8717   *
8718   * @memberOf wp.media.view
8719   *
8720   * @class
8721   * @augments wp.media.View
8722   * @augments wp.Backbone.View
8723   * @augments Backbone.View
8724   */
8725  Search = wp.media.View.extend(/** @lends wp.media.view.Search.prototype */{
8726      tagName:   'input',
8727      className: 'search',
8728      id:        'media-search-input',
8729  
8730      attributes: {
8731          type: 'search'
8732      },
8733  
8734      events: {
8735          'input': 'search'
8736      },
8737  
8738      /**
8739       * @return {wp.media.view.Search} Returns itself to allow chaining.
8740       */
8741      render: function() {
8742          this.el.value = this.model.escape('search');
8743          return this;
8744      },
8745  
8746      search: _.debounce( function( event ) {
8747          var searchTerm = event.target.value.trim();
8748  
8749          // Trigger the search only after 2 ASCII characters.
8750          if ( searchTerm && searchTerm.length > 1 ) {
8751              this.model.set( 'search', searchTerm );
8752          } else {
8753              this.model.unset( 'search' );
8754          }
8755      }, 500 )
8756  });
8757  
8758  module.exports = Search;
8759  
8760  
8761  /***/ }),
8762  
8763  /***/ 8282:
8764  /***/ ((module) => {
8765  
8766  var _n = wp.i18n._n,
8767      sprintf = wp.i18n.sprintf,
8768      Selection;
8769  
8770  /**
8771   * wp.media.view.Selection
8772   *
8773   * @memberOf wp.media.view
8774   *
8775   * @class
8776   * @augments wp.media.View
8777   * @augments wp.Backbone.View
8778   * @augments Backbone.View
8779   */
8780  Selection = wp.media.View.extend(/** @lends wp.media.view.Selection.prototype */{
8781      tagName:   'div',
8782      className: 'media-selection',
8783      template:  wp.template('media-selection'),
8784  
8785      events: {
8786          'click .edit-selection':  'edit',
8787          'click .clear-selection': 'clear'
8788      },
8789  
8790      initialize: function() {
8791          _.defaults( this.options, {
8792              editable:  false,
8793              clearable: true
8794          });
8795  
8796          /**
8797           * @member {wp.media.view.Attachments.Selection}
8798           */
8799          this.attachments = new wp.media.view.Attachments.Selection({
8800              controller: this.controller,
8801              collection: this.collection,
8802              selection:  this.collection,
8803              model:      new Backbone.Model()
8804          });
8805  
8806          this.views.set( '.selection-view', this.attachments );
8807          this.collection.on( 'add remove reset', this.refresh, this );
8808          this.controller.on( 'content:activate', this.refresh, this );
8809      },
8810  
8811      ready: function() {
8812          this.refresh();
8813      },
8814  
8815      refresh: function() {
8816          // If the selection hasn't been rendered, bail.
8817          if ( ! this.$el.children().length ) {
8818              return;
8819          }
8820  
8821          var collection = this.collection,
8822              editing = 'edit-selection' === this.controller.content.mode();
8823  
8824          // If nothing is selected, display nothing.
8825          this.$el.toggleClass( 'empty', ! collection.length );
8826          this.$el.toggleClass( 'one', 1 === collection.length );
8827          this.$el.toggleClass( 'editing', editing );
8828  
8829          this.$( '.count' ).text(
8830              /* translators: %s: Number of selected media attachments. */
8831              sprintf( _n( '%s item selected', '%s items selected', collection.length ), collection.length )
8832          );
8833      },
8834  
8835      edit: function( event ) {
8836          event.preventDefault();
8837          if ( this.options.editable ) {
8838              this.options.editable.call( this, this.collection );
8839          }
8840      },
8841  
8842      clear: function( event ) {
8843          event.preventDefault();
8844          this.collection.reset();
8845  
8846          // Move focus to the modal.
8847          this.controller.modal.focusManager.focus();
8848      }
8849  });
8850  
8851  module.exports = Selection;
8852  
8853  
8854  /***/ }),
8855  
8856  /***/ 1915:
8857  /***/ ((module) => {
8858  
8859  var View = wp.media.View,
8860      $ = Backbone.$,
8861      Settings;
8862  
8863  /**
8864   * wp.media.view.Settings
8865   *
8866   * @memberOf wp.media.view
8867   *
8868   * @class
8869   * @augments wp.media.View
8870   * @augments wp.Backbone.View
8871   * @augments Backbone.View
8872   */
8873  Settings = View.extend(/** @lends wp.media.view.Settings.prototype */{
8874      events: {
8875          'click button':    'updateHandler',
8876          'change input':    'updateHandler',
8877          'change select':   'updateHandler',
8878          'change textarea': 'updateHandler'
8879      },
8880  
8881      initialize: function() {
8882          this.model = this.model || new Backbone.Model();
8883          this.listenTo( this.model, 'change', this.updateChanges );
8884      },
8885  
8886      prepare: function() {
8887          return _.defaults({
8888              model: this.model.toJSON()
8889          }, this.options );
8890      },
8891      /**
8892       * @return {wp.media.view.Settings} Returns itself to allow chaining.
8893       */
8894      render: function() {
8895          View.prototype.render.apply( this, arguments );
8896          // Select the correct values.
8897          _( this.model.attributes ).chain().keys().each( this.update, this );
8898          return this;
8899      },
8900      /**
8901       * @param {string} key
8902       */
8903      update: function( key ) {
8904          var value = this.model.get( key ),
8905              $setting = this.$('[data-setting="' + key + '"]'),
8906              $buttons, $value;
8907  
8908          // Bail if we didn't find a matching setting.
8909          if ( ! $setting.length ) {
8910              return;
8911          }
8912  
8913          // Attempt to determine how the setting is rendered and update
8914          // the selected value.
8915  
8916          // Handle dropdowns.
8917          if ( $setting.is('select') ) {
8918              $value = $setting.find('[value="' + value + '"]');
8919  
8920              if ( $value.length ) {
8921                  $setting.find('option').prop( 'selected', false );
8922                  $value.prop( 'selected', true );
8923              } else {
8924                  // If we can't find the desired value, record what *is* selected.
8925                  this.model.set( key, $setting.find(':selected').val() );
8926              }
8927  
8928          // Handle button groups.
8929          } else if ( $setting.hasClass('button-group') ) {
8930              $buttons = $setting.find( 'button' )
8931                  .removeClass( 'active' )
8932                  .attr( 'aria-pressed', 'false' );
8933              $buttons.filter( '[value="' + value + '"]' )
8934                  .addClass( 'active' )
8935                  .attr( 'aria-pressed', 'true' );
8936  
8937          // Handle text inputs and textareas.
8938          } else if ( $setting.is('input[type="text"], textarea') ) {
8939              if ( ! $setting.is(':focus') ) {
8940                  $setting.val( value );
8941              }
8942          // Handle checkboxes.
8943          } else if ( $setting.is('input[type="checkbox"]') ) {
8944              $setting.prop( 'checked', !! value && 'false' !== value );
8945          }
8946      },
8947      /**
8948       * @param {Object} event
8949       */
8950      updateHandler: function( event ) {
8951          var $setting = $( event.target ).closest('[data-setting]'),
8952              value = event.target.value,
8953              userSetting;
8954  
8955          event.preventDefault();
8956  
8957          if ( ! $setting.length ) {
8958              return;
8959          }
8960  
8961          // Use the correct value for checkboxes.
8962          if ( $setting.is('input[type="checkbox"]') ) {
8963              value = $setting[0].checked;
8964          }
8965  
8966          // Update the corresponding setting.
8967          this.model.set( $setting.data('setting'), value );
8968  
8969          // If the setting has a corresponding user setting,
8970          // update that as well.
8971          userSetting = $setting.data('userSetting');
8972          if ( userSetting ) {
8973              window.setUserSetting( userSetting, value );
8974          }
8975      },
8976  
8977      updateChanges: function( model ) {
8978          if ( model.hasChanged() ) {
8979              _( model.changed ).chain().keys().each( this.update, this );
8980          }
8981      }
8982  });
8983  
8984  module.exports = Settings;
8985  
8986  
8987  /***/ }),
8988  
8989  /***/ 7656:
8990  /***/ ((module) => {
8991  
8992  var Settings = wp.media.view.Settings,
8993      AttachmentDisplay;
8994  
8995  /**
8996   * wp.media.view.Settings.AttachmentDisplay
8997   *
8998   * @memberOf wp.media.view.Settings
8999   *
9000   * @class
9001   * @augments wp.media.view.Settings
9002   * @augments wp.media.View
9003   * @augments wp.Backbone.View
9004   * @augments Backbone.View
9005   */
9006  AttachmentDisplay = Settings.extend(/** @lends wp.media.view.Settings.AttachmentDisplay.prototype */{
9007      className: 'attachment-display-settings',
9008      template:  wp.template('attachment-display-settings'),
9009  
9010      initialize: function() {
9011          var attachment = this.options.attachment;
9012  
9013          _.defaults( this.options, {
9014              userSettings: false
9015          });
9016          // Call 'initialize' directly on the parent class.
9017          Settings.prototype.initialize.apply( this, arguments );
9018          this.listenTo( this.model, 'change:link', this.updateLinkTo );
9019  
9020          if ( attachment ) {
9021              attachment.on( 'change:uploading', this.render, this );
9022          }
9023      },
9024  
9025      dispose: function() {
9026          var attachment = this.options.attachment;
9027          if ( attachment ) {
9028              attachment.off( null, null, this );
9029          }
9030          /**
9031           * call 'dispose' directly on the parent class
9032           */
9033          Settings.prototype.dispose.apply( this, arguments );
9034      },
9035      /**
9036       * @return {wp.media.view.AttachmentDisplay} Returns itself to allow chaining.
9037       */
9038      render: function() {
9039          var attachment = this.options.attachment;
9040          if ( attachment ) {
9041              _.extend( this.options, {
9042                  sizes: attachment.get('sizes'),
9043                  type:  attachment.get('type')
9044              });
9045          }
9046          /**
9047           * call 'render' directly on the parent class
9048           */
9049          Settings.prototype.render.call( this );
9050          this.updateLinkTo();
9051          return this;
9052      },
9053  
9054      updateLinkTo: function() {
9055          var linkTo = this.model.get('link'),
9056              $input = this.$('.link-to-custom'),
9057              attachment = this.options.attachment;
9058  
9059          if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
9060              $input.closest( '.setting' ).addClass( 'hidden' );
9061              return;
9062          }
9063  
9064          if ( attachment ) {
9065              if ( 'post' === linkTo ) {
9066                  $input.val( attachment.get('link') );
9067              } else if ( 'file' === linkTo ) {
9068                  $input.val( attachment.get('url') );
9069              } else if ( ! this.model.get('linkUrl') ) {
9070                  $input.val('http://');
9071              }
9072  
9073              $input.prop( 'readonly', 'custom' !== linkTo );
9074          }
9075  
9076          $input.closest( '.setting' ).removeClass( 'hidden' );
9077          if ( $input.length ) {
9078              $input[0].scrollIntoView();
9079          }
9080      }
9081  });
9082  
9083  module.exports = AttachmentDisplay;
9084  
9085  
9086  /***/ }),
9087  
9088  /***/ 7266:
9089  /***/ ((module) => {
9090  
9091  /**
9092   * wp.media.view.Settings.Gallery
9093   *
9094   * @memberOf wp.media.view.Settings
9095   *
9096   * @class
9097   * @augments wp.media.view.Settings
9098   * @augments wp.media.View
9099   * @augments wp.Backbone.View
9100   * @augments Backbone.View
9101   */
9102  var Gallery = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Gallery.prototype */{
9103      className: 'collection-settings gallery-settings',
9104      template:  wp.template('gallery-settings')
9105  });
9106  
9107  module.exports = Gallery;
9108  
9109  
9110  /***/ }),
9111  
9112  /***/ 2356:
9113  /***/ ((module) => {
9114  
9115  /**
9116   * wp.media.view.Settings.Playlist
9117   *
9118   * @memberOf wp.media.view.Settings
9119   *
9120   * @class
9121   * @augments wp.media.view.Settings
9122   * @augments wp.media.View
9123   * @augments wp.Backbone.View
9124   * @augments Backbone.View
9125   */
9126  var Playlist = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Playlist.prototype */{
9127      className: 'collection-settings playlist-settings',
9128      template:  wp.template('playlist-settings')
9129  });
9130  
9131  module.exports = Playlist;
9132  
9133  
9134  /***/ }),
9135  
9136  /***/ 1992:
9137  /***/ ((module) => {
9138  
9139  /**
9140   * wp.media.view.Sidebar
9141   *
9142   * @memberOf wp.media.view
9143   *
9144   * @class
9145   * @augments wp.media.view.PriorityList
9146   * @augments wp.media.View
9147   * @augments wp.Backbone.View
9148   * @augments Backbone.View
9149   */
9150  var Sidebar = wp.media.view.PriorityList.extend(/** @lends wp.media.view.Sidebar.prototype */{
9151      className: 'media-sidebar'
9152  });
9153  
9154  module.exports = Sidebar;
9155  
9156  
9157  /***/ }),
9158  
9159  /***/ 443:
9160  /***/ ((module) => {
9161  
9162  var View = wp.media.view,
9163      SiteIconCropper;
9164  
9165  /**
9166   * wp.media.view.SiteIconCropper
9167   *
9168   * Uses the imgAreaSelect plugin to allow a user to crop a Site Icon.
9169   *
9170   * Takes imgAreaSelect options from
9171   * wp.customize.SiteIconControl.calculateImageSelectOptions.
9172   *
9173   * @memberOf wp.media.view
9174   *
9175   * @class
9176   * @augments wp.media.view.Cropper
9177   * @augments wp.media.View
9178   * @augments wp.Backbone.View
9179   * @augments Backbone.View
9180   */
9181  SiteIconCropper = View.Cropper.extend(/** @lends wp.media.view.SiteIconCropper.prototype */{
9182      className: 'crop-content site-icon',
9183  
9184      ready: function () {
9185          View.Cropper.prototype.ready.apply( this, arguments );
9186  
9187          this.$( '.crop-image' ).on( 'load', _.bind( this.addSidebar, this ) );
9188      },
9189  
9190      addSidebar: function() {
9191          this.sidebar = new wp.media.view.Sidebar({
9192              controller: this.controller
9193          });
9194  
9195          this.sidebar.set( 'preview', new wp.media.view.SiteIconPreview({
9196              controller: this.controller,
9197              attachment: this.options.attachment
9198          }) );
9199  
9200          this.controller.cropperView.views.add( this.sidebar );
9201      }
9202  });
9203  
9204  module.exports = SiteIconCropper;
9205  
9206  
9207  /***/ }),
9208  
9209  /***/ 7810:
9210  /***/ ((module) => {
9211  
9212  var View = wp.media.View,
9213      $ = jQuery,
9214      SiteIconPreview;
9215  
9216  /**
9217   * wp.media.view.SiteIconPreview
9218   *
9219   * Shows a preview of the Site Icon as a favicon and app icon while cropping.
9220   *
9221   * @memberOf wp.media.view
9222   *
9223   * @class
9224   * @augments wp.media.View
9225   * @augments wp.Backbone.View
9226   * @augments Backbone.View
9227   */
9228  SiteIconPreview = View.extend(/** @lends wp.media.view.SiteIconPreview.prototype */{
9229      className: 'site-icon-preview-crop-modal',
9230      template: wp.template( 'site-icon-preview-crop' ),
9231  
9232      ready: function() {
9233          this.controller.imgSelect.setOptions({
9234              onInit: this.updatePreview,
9235              onSelectChange: this.updatePreview
9236          });
9237      },
9238  
9239      prepare: function() {
9240          return {
9241              url: this.options.attachment.get( 'url' )
9242          };
9243      },
9244  
9245      updatePreview: function( img, coords ) {
9246          var rx = 64 / coords.width,
9247              ry = 64 / coords.height,
9248              preview_rx = 24 / coords.width,
9249              preview_ry = 24 / coords.height;
9250  
9251          $( '#preview-app-icon' ).css({
9252              width: Math.round(rx * this.imageWidth ) + 'px',
9253              height: Math.round(ry * this.imageHeight ) + 'px',
9254              marginLeft: '-' + Math.round(rx * coords.x1) + 'px',
9255              marginTop: '-' + Math.round(ry * coords.y1) + 'px'
9256          });
9257  
9258          $( '#preview-favicon' ).css({
9259              width: Math.round( preview_rx * this.imageWidth ) + 'px',
9260              height: Math.round( preview_ry * this.imageHeight ) + 'px',
9261              marginLeft: '-' + Math.round( preview_rx * coords.x1 ) + 'px',
9262              marginTop: '-' + Math.floor( preview_ry* coords.y1 ) + 'px'
9263          });
9264      }
9265  });
9266  
9267  module.exports = SiteIconPreview;
9268  
9269  
9270  /***/ }),
9271  
9272  /***/ 9141:
9273  /***/ ((module) => {
9274  
9275  /**
9276   * wp.media.view.Spinner
9277   *
9278   * Represents a spinner in the Media Library.
9279   *
9280   * @since 3.9.0
9281   *
9282   * @memberOf wp.media.view
9283   *
9284   * @class
9285   * @augments wp.media.View
9286   * @augments wp.Backbone.View
9287   * @augments Backbone.View
9288   */
9289  var Spinner = wp.media.View.extend(/** @lends wp.media.view.Spinner.prototype */{
9290      tagName:   'span',
9291      className: 'spinner',
9292      spinnerTimeout: false,
9293      delay: 400,
9294  
9295      /**
9296       * Shows the spinner. Delays the visibility by the configured amount.
9297       *
9298       * @since 3.9.0
9299       *
9300       * @return {wp.media.view.Spinner} The spinner.
9301       */
9302      show: function() {
9303          if ( ! this.spinnerTimeout ) {
9304              this.spinnerTimeout = _.delay(function( $el ) {
9305                  $el.addClass( 'is-active' );
9306              }, this.delay, this.$el );
9307          }
9308  
9309          return this;
9310      },
9311  
9312      /**
9313       * Hides the spinner.
9314       *
9315       * @since 3.9.0
9316       *
9317       * @return {wp.media.view.Spinner} The spinner.
9318       */
9319      hide: function() {
9320          this.$el.removeClass( 'is-active' );
9321          this.spinnerTimeout = clearTimeout( this.spinnerTimeout );
9322  
9323          return this;
9324      }
9325  });
9326  
9327  module.exports = Spinner;
9328  
9329  
9330  /***/ }),
9331  
9332  /***/ 5275:
9333  /***/ ((module) => {
9334  
9335  var View = wp.media.View,
9336      Toolbar;
9337  
9338  /**
9339   * wp.media.view.Toolbar
9340   *
9341   * A toolbar which consists of a primary and a secondary section. Each sections
9342   * can be filled with views.
9343   *
9344   * @memberOf wp.media.view
9345   *
9346   * @class
9347   * @augments wp.media.View
9348   * @augments wp.Backbone.View
9349   * @augments Backbone.View
9350   */
9351  Toolbar = View.extend(/** @lends wp.media.view.Toolbar.prototype */{
9352      tagName:   'div',
9353      className: 'media-toolbar',
9354  
9355      initialize: function() {
9356          var state = this.controller.state(),
9357              selection = this.selection = state.get('selection'),
9358              library = this.library = state.get('library');
9359  
9360          this._views = {};
9361  
9362          // The toolbar is composed of two `PriorityList` views.
9363          this.primary   = new wp.media.view.PriorityList();
9364          this.secondary = new wp.media.view.PriorityList();
9365          this.tertiary  = new wp.media.view.PriorityList();
9366          this.primary.$el.addClass('media-toolbar-primary search-form');
9367          this.secondary.$el.addClass('media-toolbar-secondary');
9368          this.tertiary.$el.addClass('media-bg-overlay');
9369  
9370          this.views.set([ this.secondary, this.primary, this.tertiary ]);
9371  
9372          if ( this.options.items ) {
9373              this.set( this.options.items, { silent: true });
9374          }
9375  
9376          if ( ! this.options.silent ) {
9377              this.render();
9378          }
9379  
9380          if ( selection ) {
9381              selection.on( 'add remove reset', this.refresh, this );
9382          }
9383  
9384          if ( library ) {
9385              library.on( 'add remove reset', this.refresh, this );
9386          }
9387      },
9388      /**
9389       * @return {wp.media.view.Toolbar} Returns itself to allow chaining
9390       */
9391      dispose: function() {
9392          if ( this.selection ) {
9393              this.selection.off( null, null, this );
9394          }
9395  
9396          if ( this.library ) {
9397              this.library.off( null, null, this );
9398          }
9399          /**
9400           * call 'dispose' directly on the parent class
9401           */
9402          return View.prototype.dispose.apply( this, arguments );
9403      },
9404  
9405      ready: function() {
9406          this.refresh();
9407      },
9408  
9409      /**
9410       * @param {string} id
9411       * @param {Backbone.View|Object} view
9412       * @param {Object} [options={}]
9413       * @return {wp.media.view.Toolbar} Returns itself to allow chaining.
9414       */
9415      set: function( id, view, options ) {
9416          var list;
9417          options = options || {};
9418  
9419          // Accept an object with an `id` : `view` mapping.
9420          if ( _.isObject( id ) ) {
9421              _.each( id, function( view, id ) {
9422                  this.set( id, view, { silent: true });
9423              }, this );
9424  
9425          } else {
9426              if ( ! ( view instanceof Backbone.View ) ) {
9427                  view.classes = [ 'media-button-' + id ].concat( view.classes || [] );
9428                  view = new wp.media.view.Button( view ).render();
9429              }
9430  
9431              view.controller = view.controller || this.controller;
9432  
9433              this._views[ id ] = view;
9434  
9435              list = view.options.priority < 0 ? 'secondary' : 'primary';
9436              this[ list ].set( id, view, options );
9437          }
9438  
9439          if ( ! options.silent ) {
9440              this.refresh();
9441          }
9442  
9443          return this;
9444      },
9445      /**
9446       * @param {string} id
9447       * @return {wp.media.view.Button}
9448       */
9449      get: function( id ) {
9450          return this._views[ id ];
9451      },
9452      /**
9453       * @param {string} id
9454       * @param {Object} options
9455       * @return {wp.media.view.Toolbar} Returns itself to allow chaining.
9456       */
9457      unset: function( id, options ) {
9458          delete this._views[ id ];
9459          this.primary.unset( id, options );
9460          this.secondary.unset( id, options );
9461          this.tertiary.unset( id, options );
9462  
9463          if ( ! options || ! options.silent ) {
9464              this.refresh();
9465          }
9466          return this;
9467      },
9468  
9469      refresh: function() {
9470          var state = this.controller.state(),
9471              library = state.get('library'),
9472              selection = state.get('selection');
9473  
9474          _.each( this._views, function( button ) {
9475              if ( ! button.model || ! button.options || ! button.options.requires ) {
9476                  return;
9477              }
9478  
9479              var requires = button.options.requires,
9480                  disabled = false;
9481  
9482              // Prevent insertion of attachments if any of them are still uploading.
9483              if ( selection && selection.models ) {
9484                  disabled = _.some( selection.models, function( attachment ) {
9485                      return attachment.get('uploading') === true;
9486                  });
9487              }
9488  
9489              if ( requires.selection && selection && ! selection.length ) {
9490                  disabled = true;
9491              } else if ( requires.library && library && ! library.length ) {
9492                  disabled = true;
9493              }
9494              button.model.set( 'disabled', disabled );
9495          });
9496      }
9497  });
9498  
9499  module.exports = Toolbar;
9500  
9501  
9502  /***/ }),
9503  
9504  /***/ 397:
9505  /***/ ((module) => {
9506  
9507  var Select = wp.media.view.Toolbar.Select,
9508      l10n = wp.media.view.l10n,
9509      Embed;
9510  
9511  /**
9512   * wp.media.view.Toolbar.Embed
9513   *
9514   * @memberOf wp.media.view.Toolbar
9515   *
9516   * @class
9517   * @augments wp.media.view.Toolbar.Select
9518   * @augments wp.media.view.Toolbar
9519   * @augments wp.media.View
9520   * @augments wp.Backbone.View
9521   * @augments Backbone.View
9522   */
9523  Embed = Select.extend(/** @lends wp.media.view.Toolbar.Embed.prototype */{
9524      initialize: function() {
9525          _.defaults( this.options, {
9526              text: l10n.insertIntoPost,
9527              requires: false
9528          });
9529          // Call 'initialize' directly on the parent class.
9530          Select.prototype.initialize.apply( this, arguments );
9531      },
9532  
9533      refresh: function() {
9534          var url = this.controller.state().props.get('url');
9535          this.get('select').model.set( 'disabled', ! url || url === 'http://' );
9536          /**
9537           * call 'refresh' directly on the parent class
9538           */
9539          Select.prototype.refresh.apply( this, arguments );
9540      }
9541  });
9542  
9543  module.exports = Embed;
9544  
9545  
9546  /***/ }),
9547  
9548  /***/ 9458:
9549  /***/ ((module) => {
9550  
9551  var Toolbar = wp.media.view.Toolbar,
9552      l10n = wp.media.view.l10n,
9553      Select;
9554  
9555  /**
9556   * wp.media.view.Toolbar.Select
9557   *
9558   * @memberOf wp.media.view.Toolbar
9559   *
9560   * @class
9561   * @augments wp.media.view.Toolbar
9562   * @augments wp.media.View
9563   * @augments wp.Backbone.View
9564   * @augments Backbone.View
9565   */
9566  Select = Toolbar.extend(/** @lends wp.media.view.Toolbar.Select.prototype */{
9567      initialize: function() {
9568          var options = this.options;
9569  
9570          _.bindAll( this, 'clickSelect' );
9571  
9572          _.defaults( options, {
9573              event: 'select',
9574              state: false,
9575              reset: true,
9576              close: true,
9577              text:  l10n.select,
9578  
9579              // Does the button rely on the selection?
9580              requires: {
9581                  selection: true
9582              }
9583          });
9584  
9585          options.items = _.defaults( options.items || {}, {
9586              select: {
9587                  style:    'primary',
9588                  text:     options.text,
9589                  priority: 80,
9590                  click:    this.clickSelect,
9591                  requires: options.requires
9592              }
9593          });
9594          // Call 'initialize' directly on the parent class.
9595          Toolbar.prototype.initialize.apply( this, arguments );
9596      },
9597  
9598      clickSelect: function() {
9599          var options = this.options,
9600              controller = this.controller;
9601  
9602          if ( options.close ) {
9603              controller.close();
9604          }
9605  
9606          if ( options.event ) {
9607              controller.state().trigger( options.event );
9608          }
9609  
9610          if ( options.state ) {
9611              controller.setState( options.state );
9612          }
9613  
9614          if ( options.reset ) {
9615              controller.reset();
9616          }
9617      }
9618  });
9619  
9620  module.exports = Select;
9621  
9622  
9623  /***/ }),
9624  
9625  /***/ 3674:
9626  /***/ ((module) => {
9627  
9628  var View = wp.media.View,
9629      l10n = wp.media.view.l10n,
9630      $ = jQuery,
9631      EditorUploader;
9632  
9633  /**
9634   * Creates a dropzone on WP editor instances (elements with .wp-editor-wrap)
9635   * and relays drag'n'dropped files to a media workflow.
9636   *
9637   * wp.media.view.EditorUploader
9638   *
9639   * @memberOf wp.media.view
9640   *
9641   * @class
9642   * @augments wp.media.View
9643   * @augments wp.Backbone.View
9644   * @augments Backbone.View
9645   */
9646  EditorUploader = View.extend(/** @lends wp.media.view.EditorUploader.prototype */{
9647      tagName:   'div',
9648      className: 'uploader-editor',
9649      template:  wp.template( 'uploader-editor' ),
9650  
9651      localDrag: false,
9652      overContainer: false,
9653      overDropzone: false,
9654      draggingFile: null,
9655  
9656      /**
9657       * Bind drag'n'drop events to callbacks.
9658       */
9659      initialize: function() {
9660          this.initialized = false;
9661  
9662          // Bail if not enabled or UA does not support drag'n'drop or File API.
9663          if ( ! window.tinyMCEPreInit || ! window.tinyMCEPreInit.dragDropUpload || ! this.browserSupport() ) {
9664              return this;
9665          }
9666  
9667          this.$document = $(document);
9668          this.dropzones = [];
9669          this.files = [];
9670  
9671          this.$document.on( 'drop', '.uploader-editor', _.bind( this.drop, this ) );
9672          this.$document.on( 'dragover', '.uploader-editor', _.bind( this.dropzoneDragover, this ) );
9673          this.$document.on( 'dragleave', '.uploader-editor', _.bind( this.dropzoneDragleave, this ) );
9674          this.$document.on( 'click', '.uploader-editor', _.bind( this.click, this ) );
9675  
9676          this.$document.on( 'dragover', _.bind( this.containerDragover, this ) );
9677          this.$document.on( 'dragleave', _.bind( this.containerDragleave, this ) );
9678  
9679          this.$document.on( 'dragstart dragend drop', _.bind( function( event ) {
9680              this.localDrag = event.type === 'dragstart';
9681  
9682              if ( event.type === 'drop' ) {
9683                  this.containerDragleave();
9684              }
9685          }, this ) );
9686  
9687          this.initialized = true;
9688          return this;
9689      },
9690  
9691      /**
9692       * Check browser support for drag'n'drop.
9693       *
9694       * @return {boolean}
9695       */
9696      browserSupport: function() {
9697          var supports = false, div = document.createElement('div');
9698  
9699          supports = ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div );
9700          supports = supports && !! ( window.File && window.FileList && window.FileReader );
9701          return supports;
9702      },
9703  
9704      isDraggingFile: function( event ) {
9705          if ( this.draggingFile !== null ) {
9706              return this.draggingFile;
9707          }
9708  
9709          if ( _.isUndefined( event.originalEvent ) || _.isUndefined( event.originalEvent.dataTransfer ) ) {
9710              return false;
9711          }
9712  
9713          this.draggingFile = _.indexOf( event.originalEvent.dataTransfer.types, 'Files' ) > -1 &&
9714              _.indexOf( event.originalEvent.dataTransfer.types, 'text/plain' ) === -1;
9715  
9716          return this.draggingFile;
9717      },
9718  
9719      refresh: function( e ) {
9720          var dropzone_id;
9721          for ( dropzone_id in this.dropzones ) {
9722              // Hide the dropzones only if dragging has left the screen.
9723              this.dropzones[ dropzone_id ].toggle( this.overContainer || this.overDropzone );
9724          }
9725  
9726          if ( ! _.isUndefined( e ) ) {
9727              $( e.target ).closest( '.uploader-editor' ).toggleClass( 'droppable', this.overDropzone );
9728          }
9729  
9730          if ( ! this.overContainer && ! this.overDropzone ) {
9731              this.draggingFile = null;
9732          }
9733  
9734          return this;
9735      },
9736  
9737      render: function() {
9738          if ( ! this.initialized ) {
9739              return this;
9740          }
9741  
9742          View.prototype.render.apply( this, arguments );
9743          $( '.wp-editor-wrap' ).each( _.bind( this.attach, this ) );
9744          return this;
9745      },
9746  
9747      attach: function( index, editor ) {
9748          // Attach a dropzone to an editor.
9749          var dropzone = this.$el.clone();
9750          this.dropzones.push( dropzone );
9751          $( editor ).append( dropzone );
9752          return this;
9753      },
9754  
9755      /**
9756       * When a file is dropped on the editor uploader, open up an editor media workflow
9757       * and upload the file immediately.
9758       *
9759       * @param {jQuery.Event} event The 'drop' event.
9760       */
9761      drop: function( event ) {
9762          var $wrap, uploadView;
9763  
9764          this.containerDragleave( event );
9765          this.dropzoneDragleave( event );
9766  
9767          this.files = event.originalEvent.dataTransfer.files;
9768          if ( this.files.length < 1 ) {
9769              return;
9770          }
9771  
9772          // Set the active editor to the drop target.
9773          $wrap = $( event.target ).parents( '.wp-editor-wrap' );
9774          if ( $wrap.length > 0 && $wrap[0].id ) {
9775              window.wpActiveEditor = $wrap[0].id.slice( 3, -5 );
9776          }
9777  
9778          if ( ! this.workflow ) {
9779              this.workflow = wp.media.editor.open( window.wpActiveEditor, {
9780                  frame:    'post',
9781                  state:    'insert',
9782                  title:    l10n.addMedia,
9783                  multiple: true
9784              });
9785  
9786              uploadView = this.workflow.uploader;
9787  
9788              if ( uploadView.uploader && uploadView.uploader.ready ) {
9789                  this.addFiles.apply( this );
9790              } else {
9791                  this.workflow.on( 'uploader:ready', this.addFiles, this );
9792              }
9793          } else {
9794              this.workflow.state().reset();
9795              this.addFiles.apply( this );
9796              this.workflow.open();
9797          }
9798  
9799          return false;
9800      },
9801  
9802      /**
9803       * Add the files to the uploader.
9804       */
9805      addFiles: function() {
9806          if ( this.files.length ) {
9807              this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) );
9808              this.files = [];
9809          }
9810          return this;
9811      },
9812  
9813      containerDragover: function( event ) {
9814          if ( this.localDrag || ! this.isDraggingFile( event ) ) {
9815              return;
9816          }
9817  
9818          this.overContainer = true;
9819          this.refresh();
9820      },
9821  
9822      containerDragleave: function() {
9823          this.overContainer = false;
9824  
9825          // Throttle dragleave because it's called when bouncing from some elements to others.
9826          _.delay( _.bind( this.refresh, this ), 50 );
9827      },
9828  
9829      dropzoneDragover: function( event ) {
9830          if ( this.localDrag || ! this.isDraggingFile( event ) ) {
9831              return;
9832          }
9833  
9834          this.overDropzone = true;
9835          this.refresh( event );
9836          return false;
9837      },
9838  
9839      dropzoneDragleave: function( e ) {
9840          this.overDropzone = false;
9841          _.delay( _.bind( this.refresh, this, e ), 50 );
9842      },
9843  
9844      click: function( e ) {
9845          // In the rare case where the dropzone gets stuck, hide it on click.
9846          this.containerDragleave( e );
9847          this.dropzoneDragleave( e );
9848          this.localDrag = false;
9849      }
9850  });
9851  
9852  module.exports = EditorUploader;
9853  
9854  
9855  /***/ }),
9856  
9857  /***/ 1753:
9858  /***/ ((module) => {
9859  
9860  var View = wp.media.View,
9861      UploaderInline;
9862  
9863  /**
9864   * wp.media.view.UploaderInline
9865   *
9866   * The inline uploader that shows up in the 'Upload Files' tab.
9867   *
9868   * @memberOf wp.media.view
9869   *
9870   * @class
9871   * @augments wp.media.View
9872   * @augments wp.Backbone.View
9873   * @augments Backbone.View
9874   */
9875  UploaderInline = View.extend(/** @lends wp.media.view.UploaderInline.prototype */{
9876      tagName:   'div',
9877      className: 'uploader-inline',
9878      template:  wp.template('uploader-inline'),
9879  
9880      events: {
9881          'click .close': 'hide'
9882      },
9883  
9884      initialize: function() {
9885          _.defaults( this.options, {
9886              message: '',
9887              status:  true,
9888              canClose: false
9889          });
9890  
9891          if ( ! this.options.$browser && this.controller.uploader ) {
9892              this.options.$browser = this.controller.uploader.$browser;
9893          }
9894  
9895          if ( _.isUndefined( this.options.postId ) ) {
9896              this.options.postId = wp.media.view.settings.post.id;
9897          }
9898  
9899          if ( this.options.status ) {
9900              this.views.set( '.upload-inline-status', new wp.media.view.UploaderStatus({
9901                  controller: this.controller
9902              }) );
9903          }
9904      },
9905  
9906      prepare: function() {
9907          var suggestedWidth = this.controller.state().get('suggestedWidth'),
9908              suggestedHeight = this.controller.state().get('suggestedHeight'),
9909              data = {};
9910  
9911          data.message = this.options.message;
9912          data.canClose = this.options.canClose;
9913  
9914          if ( suggestedWidth && suggestedHeight ) {
9915              data.suggestedWidth = suggestedWidth;
9916              data.suggestedHeight = suggestedHeight;
9917          }
9918  
9919          return data;
9920      },
9921      /**
9922       * @return {wp.media.view.UploaderInline} Returns itself to allow chaining.
9923       */
9924      dispose: function() {
9925          if ( this.disposing ) {
9926              /**
9927               * call 'dispose' directly on the parent class
9928               */
9929              return View.prototype.dispose.apply( this, arguments );
9930          }
9931  
9932          /*
9933           * Run remove on `dispose`, so we can be sure to refresh the
9934           * uploader with a view-less DOM. Track whether we're disposing
9935           * so we don't trigger an infinite loop.
9936           */
9937          this.disposing = true;
9938          return this.remove();
9939      },
9940      /**
9941       * @return {wp.media.view.UploaderInline} Returns itself to allow chaining.
9942       */
9943      remove: function() {
9944          /**
9945           * call 'remove' directly on the parent class
9946           */
9947          var result = View.prototype.remove.apply( this, arguments );
9948  
9949          _.defer( _.bind( this.refresh, this ) );
9950          return result;
9951      },
9952  
9953      refresh: function() {
9954          var uploader = this.controller.uploader;
9955  
9956          if ( uploader ) {
9957              uploader.refresh();
9958          }
9959      },
9960      /**
9961       * @return {wp.media.view.UploaderInline}
9962       */
9963      ready: function() {
9964          var $browser = this.options.$browser,
9965              $placeholder;
9966  
9967          if ( this.controller.uploader ) {
9968              $placeholder = this.$('.browser');
9969  
9970              // Check if we've already replaced the placeholder.
9971              if ( $placeholder[0] === $browser[0] ) {
9972                  return;
9973              }
9974  
9975              $browser.detach().text( $placeholder.text() );
9976              $browser[0].className = $placeholder[0].className;
9977              $browser[0].setAttribute( 'aria-labelledby', $browser[0].id + ' ' + $placeholder[0].getAttribute('aria-labelledby') );
9978              $placeholder.replaceWith( $browser.show() );
9979          }
9980  
9981          this.refresh();
9982          return this;
9983      },
9984      show: function() {
9985          this.$el.removeClass( 'hidden' );
9986          if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {
9987              this.controller.$uploaderToggler.attr( 'aria-expanded', 'true' );
9988          }
9989      },
9990      hide: function() {
9991          this.$el.addClass( 'hidden' );
9992          if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {
9993              this.controller.$uploaderToggler
9994                  .attr( 'aria-expanded', 'false' )
9995                  // Move focus back to the toggle button when closing the uploader.
9996                  .trigger( 'focus' );
9997          }
9998      }
9999  
10000  });
10001  
10002  module.exports = UploaderInline;
10003  
10004  
10005  /***/ }),
10006  
10007  /***/ 6442:
10008  /***/ ((module) => {
10009  
10010  /**
10011   * wp.media.view.UploaderStatusError
10012   *
10013   * @memberOf wp.media.view
10014   *
10015   * @class
10016   * @augments wp.media.View
10017   * @augments wp.Backbone.View
10018   * @augments Backbone.View
10019   */
10020  var UploaderStatusError = wp.media.View.extend(/** @lends wp.media.view.UploaderStatusError.prototype */{
10021      className: 'upload-error',
10022      template:  wp.template('uploader-status-error')
10023  });
10024  
10025  module.exports = UploaderStatusError;
10026  
10027  
10028  /***/ }),
10029  
10030  /***/ 8197:
10031  /***/ ((module) => {
10032  
10033  var View = wp.media.View,
10034      UploaderStatus;
10035  
10036  /**
10037   * wp.media.view.UploaderStatus
10038   *
10039   * An uploader status for on-going uploads.
10040   *
10041   * @memberOf wp.media.view
10042   *
10043   * @class
10044   * @augments wp.media.View
10045   * @augments wp.Backbone.View
10046   * @augments Backbone.View
10047   */
10048  UploaderStatus = View.extend(/** @lends wp.media.view.UploaderStatus.prototype */{
10049      className: 'media-uploader-status',
10050      template:  wp.template('uploader-status'),
10051  
10052      events: {
10053          'click .upload-dismiss-errors': 'dismiss'
10054      },
10055  
10056      initialize: function() {
10057          this.queue = wp.Uploader.queue;
10058          this.queue.on( 'add remove reset', this.visibility, this );
10059          this.queue.on( 'add remove reset change:percent', this.progress, this );
10060          this.queue.on( 'add remove reset change:uploading', this.info, this );
10061  
10062          this.errors = wp.Uploader.errors;
10063          this.errors.reset();
10064          this.errors.on( 'add remove reset', this.visibility, this );
10065          this.errors.on( 'add', this.error, this );
10066      },
10067      /**
10068       * @return {wp.media.view.UploaderStatus}
10069       */
10070      dispose: function() {
10071          wp.Uploader.queue.off( null, null, this );
10072          /**
10073           * call 'dispose' directly on the parent class
10074           */
10075          View.prototype.dispose.apply( this, arguments );
10076          return this;
10077      },
10078  
10079      visibility: function() {
10080          this.$el.toggleClass( 'uploading', !! this.queue.length );
10081          this.$el.toggleClass( 'errors', !! this.errors.length );
10082          this.$el.toggle( !! this.queue.length || !! this.errors.length );
10083      },
10084  
10085      ready: function() {
10086          _.each({
10087              '$bar':      '.media-progress-bar div',
10088              '$index':    '.upload-index',
10089              '$total':    '.upload-total',
10090              '$filename': '.upload-filename'
10091          }, function( selector, key ) {
10092              this[ key ] = this.$( selector );
10093          }, this );
10094  
10095          this.visibility();
10096          this.progress();
10097          this.info();
10098      },
10099  
10100      progress: function() {
10101          var queue = this.queue,
10102              $bar = this.$bar;
10103  
10104          if ( ! $bar || ! queue.length ) {
10105              return;
10106          }
10107  
10108          $bar.width( ( queue.reduce( function( memo, attachment ) {
10109              if ( ! attachment.get('uploading') ) {
10110                  return memo + 100;
10111              }
10112  
10113              var percent = attachment.get('percent');
10114              return memo + ( _.isNumber( percent ) ? percent : 100 );
10115          }, 0 ) / queue.length ) + '%' );
10116      },
10117  
10118      info: function() {
10119          var queue = this.queue,
10120              index = 0, active;
10121  
10122          if ( ! queue.length ) {
10123              return;
10124          }
10125  
10126          active = this.queue.find( function( attachment, i ) {
10127              index = i;
10128              return attachment.get('uploading');
10129          });
10130  
10131          if ( this.$index && this.$total && this.$filename ) {
10132              this.$index.text( index + 1 );
10133              this.$total.text( queue.length );
10134              this.$filename.html( active ? this.filename( active.get('filename') ) : '' );
10135          }
10136      },
10137      /**
10138       * @param {string} filename
10139       * @return {string}
10140       */
10141      filename: function( filename ) {
10142          return _.escape( filename );
10143      },
10144      /**
10145       * @param {Backbone.Model} error
10146       */
10147      error: function( error ) {
10148          var statusError = new wp.media.view.UploaderStatusError( {
10149              filename: this.filename( error.get( 'file' ).name ),
10150              message:  error.get( 'message' )
10151          } );
10152  
10153          var buttonClose = this.$el.find( 'button' );
10154  
10155          // Can show additional info here while retrying to create image sub-sizes.
10156          this.views.add( '.upload-errors', statusError, { at: 0 } );
10157          _.delay( function() {
10158              buttonClose.trigger( 'focus' );
10159              wp.a11y.speak( error.get( 'message' ), 'assertive' );
10160          }, 1000 );
10161      },
10162  
10163      dismiss: function() {
10164          var errors = this.views.get('.upload-errors');
10165  
10166          if ( errors ) {
10167              _.invoke( errors, 'remove' );
10168          }
10169          wp.Uploader.errors.reset();
10170          // Move focus to the modal after the dismiss button gets removed from the DOM.
10171          if ( this.controller.modal ) {
10172              this.controller.modal.focusManager.focus();
10173          }
10174      }
10175  });
10176  
10177  module.exports = UploaderStatus;
10178  
10179  
10180  /***/ }),
10181  
10182  /***/ 8291:
10183  /***/ ((module) => {
10184  
10185  var $ = jQuery,
10186      UploaderWindow;
10187  
10188  /**
10189   * wp.media.view.UploaderWindow
10190   *
10191   * An uploader window that allows for dragging and dropping media.
10192   *
10193   * @memberOf wp.media.view
10194   *
10195   * @class
10196   * @augments wp.media.View
10197   * @augments wp.Backbone.View
10198   * @augments Backbone.View
10199   *
10200   * @param {object} [options]                   Options hash passed to the view.
10201   * @param {object} [options.uploader]          Uploader properties.
10202   * @param {jQuery} [options.uploader.browser]
10203   * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.
10204   * @param {object} [options.uploader.params]
10205   */
10206  UploaderWindow = wp.media.View.extend(/** @lends wp.media.view.UploaderWindow.prototype */{
10207      tagName:   'div',
10208      className: 'uploader-window',
10209      template:  wp.template('uploader-window'),
10210  
10211      initialize: function() {
10212          var uploader;
10213  
10214          this.$browser = $( '<button type="button" class="browser" />' ).hide().appendTo( 'body' );
10215  
10216          uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
10217              dropzone:  this.$el,
10218              browser:   this.$browser,
10219              params:    {}
10220          });
10221  
10222          // Ensure the dropzone is a jQuery collection.
10223          if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) {
10224              uploader.dropzone = $( uploader.dropzone );
10225          }
10226  
10227          this.controller.on( 'activate', this.refresh, this );
10228  
10229          this.controller.on( 'detach', function() {
10230              this.$browser.remove();
10231          }, this );
10232      },
10233  
10234      refresh: function() {
10235          if ( this.uploader ) {
10236              this.uploader.refresh();
10237          }
10238      },
10239  
10240      ready: function() {
10241          var postId = wp.media.view.settings.post.id,
10242              dropzone;
10243  
10244          // If the uploader already exists, bail.
10245          if ( this.uploader ) {
10246              return;
10247          }
10248  
10249          if ( postId ) {
10250              this.options.uploader.params.post_id = postId;
10251          }
10252          this.uploader = new wp.Uploader( this.options.uploader );
10253  
10254          dropzone = this.uploader.dropzone;
10255          dropzone.on( 'dropzone:enter', _.bind( this.show, this ) );
10256          dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) );
10257  
10258          $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) );
10259      },
10260  
10261      _ready: function() {
10262          this.controller.trigger( 'uploader:ready' );
10263      },
10264  
10265      show: function() {
10266          var $el = this.$el.show();
10267  
10268          // Ensure that the animation is triggered by waiting until
10269          // the transparent element is painted into the DOM.
10270          _.defer( function() {
10271              $el.css({ opacity: 1 });
10272          });
10273      },
10274  
10275      hide: function() {
10276          var $el = this.$el.css({ opacity: 0 });
10277  
10278          wp.media.transition( $el ).done( function() {
10279              // Transition end events are subject to race conditions.
10280              // Make sure that the value is set as intended.
10281              if ( '0' === $el.css('opacity') ) {
10282                  $el.hide();
10283              }
10284          });
10285  
10286          // https://core.trac.wordpress.org/ticket/27341
10287          _.delay( function() {
10288              if ( '0' === $el.css('opacity') && $el.is(':visible') ) {
10289                  $el.hide();
10290              }
10291          }, 500 );
10292      }
10293  });
10294  
10295  module.exports = UploaderWindow;
10296  
10297  
10298  /***/ }),
10299  
10300  /***/ 4747:
10301  /***/ ((module) => {
10302  
10303  /**
10304   * wp.media.View
10305   *
10306   * The base view class for media.
10307   *
10308   * Undelegating events, removing events from the model, and
10309   * removing events from the controller mirror the code for
10310   * `Backbone.View.dispose` in Backbone 0.9.8 development.
10311   *
10312   * This behavior has since been removed, and should not be used
10313   * outside of the media manager.
10314   *
10315   * @memberOf wp.media
10316   *
10317   * @class
10318   * @augments wp.Backbone.View
10319   * @augments Backbone.View
10320   */
10321  var View = wp.Backbone.View.extend(/** @lends wp.media.View.prototype */{
10322      constructor: function( options ) {
10323          if ( options && options.controller ) {
10324              this.controller = options.controller;
10325          }
10326          wp.Backbone.View.apply( this, arguments );
10327      },
10328      /**
10329       * @todo The internal comment mentions this might have been a stop-gap
10330       *       before Backbone 0.9.8 came out. Figure out if Backbone core takes
10331       *       care of this in Backbone.View now.
10332       *
10333       * @return {wp.media.View} Returns itself to allow chaining.
10334       */
10335      dispose: function() {
10336          /*
10337           * Undelegating events, removing events from the model, and
10338           * removing events from the controller mirror the code for
10339           * `Backbone.View.dispose` in Backbone 0.9.8 development.
10340           */
10341          this.undelegateEvents();
10342  
10343          if ( this.model && this.model.off ) {
10344              this.model.off( null, null, this );
10345          }
10346  
10347          if ( this.collection && this.collection.off ) {
10348              this.collection.off( null, null, this );
10349          }
10350  
10351          // Unbind controller events.
10352          if ( this.controller && this.controller.off ) {
10353              this.controller.off( null, null, this );
10354          }
10355  
10356          return this;
10357      },
10358      /**
10359       * @return {wp.media.View} Returns itself to allow chaining.
10360       */
10361      remove: function() {
10362          this.dispose();
10363          /**
10364           * call 'remove' directly on the parent class
10365           */
10366          return wp.Backbone.View.prototype.remove.apply( this, arguments );
10367      }
10368  });
10369  
10370  module.exports = View;
10371  
10372  
10373  /***/ })
10374  
10375  /******/     });
10376  /************************************************************************/
10377  /******/     // The module cache
10378  /******/     var __webpack_module_cache__ = {};
10379  /******/     
10380  /******/     // The require function
10381  /******/ 	function __webpack_require__(moduleId) {
10382  /******/         // Check if module is in cache
10383  /******/         var cachedModule = __webpack_module_cache__[moduleId];
10384  /******/         if (cachedModule !== undefined) {
10385  /******/             return cachedModule.exports;
10386  /******/         }
10387  /******/         // Create a new module (and put it into the cache)
10388  /******/         var module = __webpack_module_cache__[moduleId] = {
10389  /******/             // no module.id needed
10390  /******/             // no module.loaded needed
10391  /******/             exports: {}
10392  /******/         };
10393  /******/     
10394  /******/         // Execute the module function
10395  /******/         __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
10396  /******/     
10397  /******/         // Return the exports of the module
10398  /******/         return module.exports;
10399  /******/     }
10400  /******/     
10401  /************************************************************************/
10402  var __webpack_exports__ = {};
10403  // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
10404  (() => {
10405  /**
10406   * @output wp-includes/js/media-views.js
10407   */
10408  
10409  var media = wp.media,
10410      $ = jQuery,
10411      l10n;
10412  
10413  media.isTouchDevice = ( 'ontouchend' in document );
10414  
10415  // Link any localized strings.
10416  l10n = media.view.l10n = window._wpMediaViewsL10n || {};
10417  
10418  // Link any settings.
10419  media.view.settings = l10n.settings || {};
10420  delete l10n.settings;
10421  
10422  // Copy the `post` setting over to the model settings.
10423  media.model.settings.post = media.view.settings.post;
10424  
10425  // Check if the browser supports CSS 3.0 transitions.
10426  $.support.transition = (function(){
10427      var style = document.documentElement.style,
10428          transitions = {
10429              WebkitTransition: 'webkitTransitionEnd',
10430              MozTransition:    'transitionend',
10431              OTransition:      'oTransitionEnd otransitionend',
10432              transition:       'transitionend'
10433          }, transition;
10434  
10435      transition = _.find( _.keys( transitions ), function( transition ) {
10436          return ! _.isUndefined( style[ transition ] );
10437      });
10438  
10439      return transition && {
10440          end: transitions[ transition ]
10441      };
10442  }());
10443  
10444  /**
10445   * A shared event bus used to provide events into
10446   * the media workflows that 3rd-party devs can use to hook
10447   * in.
10448   */
10449  media.events = _.extend( {}, Backbone.Events );
10450  
10451  /**
10452   * Makes it easier to bind events using transitions.
10453   *
10454   * @param {string} selector
10455   * @param {number} sensitivity
10456   * @return {Promise}
10457   */
10458  media.transition = function( selector, sensitivity ) {
10459      var deferred = $.Deferred();
10460  
10461      sensitivity = sensitivity || 2000;
10462  
10463      if ( $.support.transition ) {
10464          if ( ! (selector instanceof $) ) {
10465              selector = $( selector );
10466          }
10467  
10468          // Resolve the deferred when the first element finishes animating.
10469          selector.first().one( $.support.transition.end, deferred.resolve );
10470  
10471          // Just in case the event doesn't trigger, fire a callback.
10472          _.delay( deferred.resolve, sensitivity );
10473  
10474      // Otherwise, execute on the spot.
10475      } else {
10476          deferred.resolve();
10477      }
10478  
10479      return deferred.promise();
10480  };
10481  
10482  media.controller.Region = __webpack_require__( 9875 );
10483  media.controller.StateMachine = __webpack_require__( 6150 );
10484  media.controller.State = __webpack_require__( 5694 );
10485  
10486  media.selectionSync = __webpack_require__( 4181 );
10487  media.controller.Library = __webpack_require__( 472 );
10488  media.controller.ImageDetails = __webpack_require__( 705 );
10489  media.controller.GalleryEdit = __webpack_require__( 2038 );
10490  media.controller.GalleryAdd = __webpack_require__( 7127 );
10491  media.controller.CollectionEdit = __webpack_require__( 8612 );
10492  media.controller.CollectionAdd = __webpack_require__( 7145 );
10493  media.controller.FeaturedImage = __webpack_require__( 1169 );
10494  media.controller.ReplaceImage = __webpack_require__( 2275 );
10495  media.controller.EditImage = __webpack_require__( 5663 );
10496  media.controller.MediaLibrary = __webpack_require__( 8065 );
10497  media.controller.Embed = __webpack_require__( 4910 );
10498  media.controller.Cropper = __webpack_require__( 5422 );
10499  media.controller.CustomizeImageCropper = __webpack_require__( 9660 );
10500  media.controller.SiteIconCropper = __webpack_require__( 6172 );
10501  
10502  media.View = __webpack_require__( 4747 );
10503  media.view.Frame = __webpack_require__( 1061 );
10504  media.view.MediaFrame = __webpack_require__( 2836 );
10505  media.view.MediaFrame.Select = __webpack_require__( 455 );
10506  media.view.MediaFrame.Post = __webpack_require__( 4274 );
10507  media.view.MediaFrame.ImageDetails = __webpack_require__( 5424 );
10508  media.view.Modal = __webpack_require__( 2621 );
10509  media.view.FocusManager = __webpack_require__( 718 );
10510  media.view.UploaderWindow = __webpack_require__( 8291 );
10511  media.view.EditorUploader = __webpack_require__( 3674 );
10512  media.view.UploaderInline = __webpack_require__( 1753 );
10513  media.view.UploaderStatus = __webpack_require__( 8197 );
10514  media.view.UploaderStatusError = __webpack_require__( 6442 );
10515  media.view.Toolbar = __webpack_require__( 5275 );
10516  media.view.Toolbar.Select = __webpack_require__( 9458 );
10517  media.view.Toolbar.Embed = __webpack_require__( 397 );
10518  media.view.Button = __webpack_require__( 846 );
10519  media.view.ButtonGroup = __webpack_require__( 168 );
10520  media.view.PriorityList = __webpack_require__( 8815 );
10521  media.view.MenuItem = __webpack_require__( 9013 );
10522  media.view.Menu = __webpack_require__( 1 );
10523  media.view.RouterItem = __webpack_require__( 6327 );
10524  media.view.Router = __webpack_require__( 4783 );
10525  media.view.Sidebar = __webpack_require__( 1992 );
10526  media.view.Attachment = __webpack_require__( 4075 );
10527  media.view.Attachment.Library = __webpack_require__( 3443 );
10528  media.view.Attachment.EditLibrary = __webpack_require__( 5232 );
10529  media.view.Attachments = __webpack_require__( 8142 );
10530  media.view.Search = __webpack_require__( 2102 );
10531  media.view.AttachmentFilters = __webpack_require__( 7709 );
10532  media.view.DateFilter = __webpack_require__( 6472 );
10533  media.view.AttachmentFilters.Uploaded = __webpack_require__( 1368 );
10534  media.view.AttachmentFilters.All = __webpack_require__( 7349 );
10535  media.view.AttachmentsBrowser = __webpack_require__( 6829 );
10536  media.view.Selection = __webpack_require__( 8282 );
10537  media.view.Attachment.Selection = __webpack_require__( 3962 );
10538  media.view.Attachments.Selection = __webpack_require__( 3479 );
10539  media.view.Attachment.EditSelection = __webpack_require__( 4593 );
10540  media.view.Settings = __webpack_require__( 1915 );
10541  media.view.Settings.AttachmentDisplay = __webpack_require__( 7656 );
10542  media.view.Settings.Gallery = __webpack_require__( 7266 );
10543  media.view.Settings.Playlist = __webpack_require__( 2356 );
10544  media.view.Attachment.Details = __webpack_require__( 6090 );
10545  media.view.AttachmentCompat = __webpack_require__( 2982 );
10546  media.view.Iframe = __webpack_require__( 1982 );
10547  media.view.Embed = __webpack_require__( 5741 );
10548  media.view.Label = __webpack_require__( 4338 );
10549  media.view.EmbedUrl = __webpack_require__( 7327 );
10550  media.view.EmbedLink = __webpack_require__( 8232 );
10551  media.view.EmbedImage = __webpack_require__( 2395 );
10552  media.view.ImageDetails = __webpack_require__( 2650 );
10553  media.view.Cropper = __webpack_require__( 7637 );
10554  media.view.SiteIconCropper = __webpack_require__( 443 );
10555  media.view.SiteIconPreview = __webpack_require__( 7810 );
10556  media.view.EditImage = __webpack_require__( 6126 );
10557  media.view.Spinner = __webpack_require__( 9141 );
10558  media.view.Heading = __webpack_require__( 170 );
10559  
10560  })();
10561  
10562  /******/ })()
10563  ;


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref