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


Generated : Fri May 10 08:20:01 2024 Cross-referenced by PHPXref