[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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