wp/wp-includes/js/media-grid.js
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
    79 /******/ 	// __webpack_public_path__
    79 /******/ 	// __webpack_public_path__
    80 /******/ 	__webpack_require__.p = "";
    80 /******/ 	__webpack_require__.p = "";
    81 /******/
    81 /******/
    82 /******/
    82 /******/
    83 /******/ 	// Load entry module and return exports
    83 /******/ 	// Load entry module and return exports
    84 /******/ 	return __webpack_require__(__webpack_require__.s = 11);
    84 /******/ 	return __webpack_require__(__webpack_require__.s = 1);
    85 /******/ })
    85 /******/ })
    86 /************************************************************************/
    86 /************************************************************************/
    87 /******/ ([
    87 /******/ ({
    88 /* 0 */,
    88 
    89 /* 1 */,
    89 /***/ 1:
    90 /* 2 */,
       
    91 /* 3 */,
       
    92 /* 4 */,
       
    93 /* 5 */,
       
    94 /* 6 */,
       
    95 /* 7 */,
       
    96 /* 8 */,
       
    97 /* 9 */,
       
    98 /* 10 */,
       
    99 /* 11 */
       
   100 /***/ (function(module, exports, __webpack_require__) {
    90 /***/ (function(module, exports, __webpack_require__) {
   101 
    91 
   102 module.exports = __webpack_require__(12);
    92 module.exports = __webpack_require__("LRQ5");
   103 
    93 
   104 
    94 
   105 /***/ }),
    95 /***/ }),
   106 /* 12 */
    96 
   107 /***/ (function(module, exports, __webpack_require__) {
    97 /***/ "1lLZ":
       
    98 /***/ (function(module, exports) {
       
    99 
       
   100 var Button = wp.media.view.Button,
       
   101 	DeleteSelected = wp.media.view.DeleteSelectedButton,
       
   102 	DeleteSelectedPermanently;
   108 
   103 
   109 /**
   104 /**
   110  * @output wp-includes/js/media-grid.js
   105  * wp.media.view.DeleteSelectedPermanentlyButton
   111  */
   106  *
   112 
   107  * When MEDIA_TRASH is true, a button that handles bulk Delete Permanently logic
   113 var media = wp.media;
   108  *
   114 
   109  * @memberOf wp.media.view
   115 media.controller.EditAttachmentMetadata = __webpack_require__( 13 );
       
   116 media.view.MediaFrame.Manage = __webpack_require__( 14 );
       
   117 media.view.Attachment.Details.TwoColumn = __webpack_require__( 15 );
       
   118 media.view.MediaFrame.Manage.Router = __webpack_require__( 16 );
       
   119 media.view.EditImage.Details = __webpack_require__( 17 );
       
   120 media.view.MediaFrame.EditAttachments = __webpack_require__( 18 );
       
   121 media.view.SelectModeToggleButton = __webpack_require__( 19 );
       
   122 media.view.DeleteSelectedButton = __webpack_require__( 20 );
       
   123 media.view.DeleteSelectedPermanentlyButton = __webpack_require__( 21 );
       
   124 
       
   125 
       
   126 /***/ }),
       
   127 /* 13 */
       
   128 /***/ (function(module, exports) {
       
   129 
       
   130 var l10n = wp.media.view.l10n,
       
   131 	EditAttachmentMetadata;
       
   132 
       
   133 /**
       
   134  * wp.media.controller.EditAttachmentMetadata
       
   135  *
       
   136  * A state for editing an attachment's metadata.
       
   137  *
       
   138  * @memberOf wp.media.controller
       
   139  *
   110  *
   140  * @class
   111  * @class
   141  * @augments wp.media.controller.State
   112  * @augments wp.media.view.DeleteSelectedButton
   142  * @augments Backbone.Model
   113  * @augments wp.media.view.Button
   143  */
       
   144 EditAttachmentMetadata = wp.media.controller.State.extend(/** @lends wp.media.controller.EditAttachmentMetadata.prototype */{
       
   145 	defaults: {
       
   146 		id:      'edit-attachment',
       
   147 		// Title string passed to the frame's title region view.
       
   148 		title:   l10n.attachmentDetails,
       
   149 		// Region mode defaults.
       
   150 		content: 'edit-metadata',
       
   151 		menu:    false,
       
   152 		toolbar: false,
       
   153 		router:  false
       
   154 	}
       
   155 });
       
   156 
       
   157 module.exports = EditAttachmentMetadata;
       
   158 
       
   159 
       
   160 /***/ }),
       
   161 /* 14 */
       
   162 /***/ (function(module, exports) {
       
   163 
       
   164 var MediaFrame = wp.media.view.MediaFrame,
       
   165 	Library = wp.media.controller.Library,
       
   166 
       
   167 	$ = Backbone.$,
       
   168 	Manage;
       
   169 
       
   170 /**
       
   171  * wp.media.view.MediaFrame.Manage
       
   172  *
       
   173  * A generic management frame workflow.
       
   174  *
       
   175  * Used in the media grid view.
       
   176  *
       
   177  * @memberOf wp.media.view.MediaFrame
       
   178  *
       
   179  * @class
       
   180  * @augments wp.media.view.MediaFrame
       
   181  * @augments wp.media.view.Frame
       
   182  * @augments wp.media.View
   114  * @augments wp.media.View
   183  * @augments wp.Backbone.View
   115  * @augments wp.Backbone.View
   184  * @augments Backbone.View
   116  * @augments Backbone.View
   185  * @mixes wp.media.controller.StateMachine
       
   186  */
   117  */
   187 Manage = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Manage.prototype */{
   118 DeleteSelectedPermanently = DeleteSelected.extend(/** @lends wp.media.view.DeleteSelectedPermanentlyButton.prototype */{
   188 	/**
       
   189 	 * @constructs
       
   190 	 */
       
   191 	initialize: function() {
   119 	initialize: function() {
   192 		_.defaults( this.options, {
   120 		DeleteSelected.prototype.initialize.apply( this, arguments );
   193 			title:     '',
   121 		this.controller.on( 'select:activate', this.selectActivate, this );
   194 			modal:     false,
   122 		this.controller.on( 'select:deactivate', this.selectDeactivate, this );
   195 			selection: [],
   123 	},
   196 			library:   {}, // Options hash for the query to the media library.
   124 
   197 			multiple:  'add',
   125 	filterChange: function( model ) {
   198 			state:     'library',
   126 		this.canShow = ( 'trash' === model.get( 'status' ) );
   199 			uploader:  true,
   127 	},
   200 			mode:      [ 'grid', 'edit' ]
   128 
   201 		});
   129 	selectActivate: function() {
   202 
   130 		this.toggleDisabled();
   203 		this.$body = $( document.body );
   131 		this.$el.toggleClass( 'hidden', ! this.canShow );
   204 		this.$window = $( window );
   132 	},
   205 		this.$adminBar = $( '#wpadminbar' );
   133 
   206 		// Store the Add New button for later reuse in wp.media.view.UploaderInline.
   134 	selectDeactivate: function() {
   207 		this.$uploaderToggler = $( '.page-title-action' )
   135 		this.toggleDisabled();
   208 			.attr( 'aria-expanded', 'false' )
   136 		this.$el.addClass( 'hidden' );
   209 			.on( 'click', _.bind( this.addNewClickHandler, this ) );
   137 	},
   210 
   138 
   211 		this.$window.on( 'scroll resize', _.debounce( _.bind( this.fixPosition, this ), 15 ) );
   139 	render: function() {
   212 
   140 		Button.prototype.render.apply( this, arguments );
   213 		// Ensure core and media grid view UI is enabled.
   141 		this.selectActivate();
   214 		this.$el.addClass('wp-core-ui');
   142 		return this;
   215 
       
   216 		// Force the uploader off if the upload limit has been exceeded or
       
   217 		// if the browser isn't supported.
       
   218 		if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
       
   219 			this.options.uploader = false;
       
   220 		}
       
   221 
       
   222 		// Initialize a window-wide uploader.
       
   223 		if ( this.options.uploader ) {
       
   224 			this.uploader = new wp.media.view.UploaderWindow({
       
   225 				controller: this,
       
   226 				uploader: {
       
   227 					dropzone:  document.body,
       
   228 					container: document.body
       
   229 				}
       
   230 			}).render();
       
   231 			this.uploader.ready();
       
   232 			$('body').append( this.uploader.el );
       
   233 
       
   234 			this.options.uploader = false;
       
   235 		}
       
   236 
       
   237 		this.gridRouter = new wp.media.view.MediaFrame.Manage.Router();
       
   238 
       
   239 		// Call 'initialize' directly on the parent class.
       
   240 		MediaFrame.prototype.initialize.apply( this, arguments );
       
   241 
       
   242 		// Append the frame view directly the supplied container.
       
   243 		this.$el.appendTo( this.options.container );
       
   244 
       
   245 		this.createStates();
       
   246 		this.bindRegionModeHandlers();
       
   247 		this.render();
       
   248 		this.bindSearchHandler();
       
   249 
       
   250 		wp.media.frames.browse = this;
       
   251 	},
       
   252 
       
   253 	bindSearchHandler: function() {
       
   254 		var search = this.$( '#media-search-input' ),
       
   255 			searchView = this.browserView.toolbar.get( 'search' ).$el,
       
   256 			listMode = this.$( '.view-list' ),
       
   257 
       
   258 			input  = _.throttle( function (e) {
       
   259 				var val = $( e.currentTarget ).val(),
       
   260 					url = '';
       
   261 
       
   262 				if ( val ) {
       
   263 					url += '?search=' + val;
       
   264 					this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
       
   265 				}
       
   266 			}, 1000 );
       
   267 
       
   268 		// Update the URL when entering search string (at most once per second).
       
   269 		search.on( 'input', _.bind( input, this ) );
       
   270 
       
   271 		this.gridRouter
       
   272 			.on( 'route:search', function () {
       
   273 				var href = window.location.href;
       
   274 				if ( href.indexOf( 'mode=' ) > -1 ) {
       
   275 					href = href.replace( /mode=[^&]+/g, 'mode=list' );
       
   276 				} else {
       
   277 					href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list';
       
   278 				}
       
   279 				href = href.replace( 'search=', 's=' );
       
   280 				listMode.prop( 'href', href );
       
   281 			})
       
   282 			.on( 'route:reset', function() {
       
   283 				searchView.val( '' ).trigger( 'input' );
       
   284 			});
       
   285 	},
       
   286 
       
   287 	/**
       
   288 	 * Create the default states for the frame.
       
   289 	 */
       
   290 	createStates: function() {
       
   291 		var options = this.options;
       
   292 
       
   293 		if ( this.options.states ) {
       
   294 			return;
       
   295 		}
       
   296 
       
   297 		// Add the default states.
       
   298 		this.states.add([
       
   299 			new Library({
       
   300 				library:            wp.media.query( options.library ),
       
   301 				multiple:           options.multiple,
       
   302 				title:              options.title,
       
   303 				content:            'browse',
       
   304 				toolbar:            'select',
       
   305 				contentUserSetting: false,
       
   306 				filterable:         'all',
       
   307 				autoSelect:         false
       
   308 			})
       
   309 		]);
       
   310 	},
       
   311 
       
   312 	/**
       
   313 	 * Bind region mode activation events to proper handlers.
       
   314 	 */
       
   315 	bindRegionModeHandlers: function() {
       
   316 		this.on( 'content:create:browse', this.browseContent, this );
       
   317 
       
   318 		// Handle a frame-level event for editing an attachment.
       
   319 		this.on( 'edit:attachment', this.openEditAttachmentModal, this );
       
   320 
       
   321 		this.on( 'select:activate', this.bindKeydown, this );
       
   322 		this.on( 'select:deactivate', this.unbindKeydown, this );
       
   323 	},
       
   324 
       
   325 	handleKeydown: function( e ) {
       
   326 		if ( 27 === e.which ) {
       
   327 			e.preventDefault();
       
   328 			this.deactivateMode( 'select' ).activateMode( 'edit' );
       
   329 		}
       
   330 	},
       
   331 
       
   332 	bindKeydown: function() {
       
   333 		this.$body.on( 'keydown.select', _.bind( this.handleKeydown, this ) );
       
   334 	},
       
   335 
       
   336 	unbindKeydown: function() {
       
   337 		this.$body.off( 'keydown.select' );
       
   338 	},
       
   339 
       
   340 	fixPosition: function() {
       
   341 		var $browser, $toolbar;
       
   342 		if ( ! this.isModeActive( 'select' ) ) {
       
   343 			return;
       
   344 		}
       
   345 
       
   346 		$browser = this.$('.attachments-browser');
       
   347 		$toolbar = $browser.find('.media-toolbar');
       
   348 
       
   349 		// Offset doesn't appear to take top margin into account, hence +16.
       
   350 		if ( ( $browser.offset().top + 16 ) < this.$window.scrollTop() + this.$adminBar.height() ) {
       
   351 			$browser.addClass( 'fixed' );
       
   352 			$toolbar.css('width', $browser.width() + 'px');
       
   353 		} else {
       
   354 			$browser.removeClass( 'fixed' );
       
   355 			$toolbar.css('width', '');
       
   356 		}
       
   357 	},
       
   358 
       
   359 	/**
       
   360 	 * Click handler for the `Add New` button.
       
   361 	 */
       
   362 	addNewClickHandler: function( event ) {
       
   363 		event.preventDefault();
       
   364 		this.trigger( 'toggle:upload:attachment' );
       
   365 
       
   366 		if ( this.uploader ) {
       
   367 			this.uploader.refresh();
       
   368 		}
       
   369 	},
       
   370 
       
   371 	/**
       
   372 	 * Open the Edit Attachment modal.
       
   373 	 */
       
   374 	openEditAttachmentModal: function( model ) {
       
   375 		// Create a new EditAttachment frame, passing along the library and the attachment model.
       
   376 		if ( wp.media.frames.edit ) {
       
   377 			wp.media.frames.edit.open().trigger( 'refresh', model );
       
   378 		} else {
       
   379 			wp.media.frames.edit = wp.media( {
       
   380 				frame:       'edit-attachments',
       
   381 				controller:  this,
       
   382 				library:     this.state().get('library'),
       
   383 				model:       model
       
   384 			} );
       
   385 		}
       
   386 	},
       
   387 
       
   388 	/**
       
   389 	 * Create an attachments browser view within the content region.
       
   390 	 *
       
   391 	 * @param {Object} contentRegion Basic object with a `view` property, which
       
   392 	 *                               should be set with the proper region view.
       
   393 	 * @this wp.media.controller.Region
       
   394 	 */
       
   395 	browseContent: function( contentRegion ) {
       
   396 		var state = this.state();
       
   397 
       
   398 		// Browse our library of attachments.
       
   399 		this.browserView = contentRegion.view = new wp.media.view.AttachmentsBrowser({
       
   400 			controller: this,
       
   401 			collection: state.get('library'),
       
   402 			selection:  state.get('selection'),
       
   403 			model:      state,
       
   404 			sortable:   state.get('sortable'),
       
   405 			search:     state.get('searchable'),
       
   406 			filters:    state.get('filterable'),
       
   407 			date:       state.get('date'),
       
   408 			display:    state.get('displaySettings'),
       
   409 			dragInfo:   state.get('dragInfo'),
       
   410 			sidebar:    'errors',
       
   411 
       
   412 			suggestedWidth:  state.get('suggestedWidth'),
       
   413 			suggestedHeight: state.get('suggestedHeight'),
       
   414 
       
   415 			AttachmentView: state.get('AttachmentView'),
       
   416 
       
   417 			scrollElement: document
       
   418 		});
       
   419 		this.browserView.on( 'ready', _.bind( this.bindDeferred, this ) );
       
   420 
       
   421 		this.errors = wp.Uploader.errors;
       
   422 		this.errors.on( 'add remove reset', this.sidebarVisibility, this );
       
   423 	},
       
   424 
       
   425 	sidebarVisibility: function() {
       
   426 		this.browserView.$( '.media-sidebar' ).toggle( !! this.errors.length );
       
   427 	},
       
   428 
       
   429 	bindDeferred: function() {
       
   430 		if ( ! this.browserView.dfd ) {
       
   431 			return;
       
   432 		}
       
   433 		this.browserView.dfd.done( _.bind( this.startHistory, this ) );
       
   434 	},
       
   435 
       
   436 	startHistory: function() {
       
   437 		// Verify pushState support and activate.
       
   438 		if ( window.history && window.history.pushState ) {
       
   439 			if ( Backbone.History.started ) {
       
   440 				Backbone.history.stop();
       
   441 			}
       
   442 			Backbone.history.start( {
       
   443 				root: window._wpMediaGridSettings.adminUrl,
       
   444 				pushState: true
       
   445 			} );
       
   446 		}
       
   447 	}
   143 	}
   448 });
   144 });
   449 
   145 
   450 module.exports = Manage;
   146 module.exports = DeleteSelectedPermanently;
   451 
   147 
   452 
   148 
   453 /***/ }),
   149 /***/ }),
   454 /* 15 */
   150 
       
   151 /***/ "FcM5":
   455 /***/ (function(module, exports) {
   152 /***/ (function(module, exports) {
   456 
   153 
   457 var Details = wp.media.view.Attachment.Details,
   154 var Details = wp.media.view.Attachment.Details,
   458 	TwoColumn;
   155 	TwoColumn;
   459 
   156 
   489 	},
   186 	},
   490 
   187 
   491 	/**
   188 	/**
   492 	 * Noop this from parent class, doesn't apply here.
   189 	 * Noop this from parent class, doesn't apply here.
   493 	 */
   190 	 */
   494 	toggleSelectionHandler: function() {},
   191 	toggleSelectionHandler: function() {}
   495 
   192 
   496 	render: function() {
       
   497 		Details.prototype.render.apply( this, arguments );
       
   498 
       
   499 		wp.media.mixin.removeAllPlayers();
       
   500 		this.$( 'audio, video' ).each( function (i, elem) {
       
   501 			var el = wp.media.view.MediaDetails.prepareSrc( elem );
       
   502 			new window.MediaElementPlayer( el, wp.media.mixin.mejsSettings );
       
   503 		} );
       
   504 	}
       
   505 });
   193 });
   506 
   194 
   507 module.exports = TwoColumn;
   195 module.exports = TwoColumn;
   508 
   196 
   509 
   197 
   510 /***/ }),
   198 /***/ }),
   511 /* 16 */
   199 
   512 /***/ (function(module, exports) {
   200 /***/ "Ffsb":
   513 
       
   514 /**
       
   515  * wp.media.view.MediaFrame.Manage.Router
       
   516  *
       
   517  * A router for handling the browser history and application state.
       
   518  *
       
   519  * @memberOf wp.media.view.MediaFrame.Manage
       
   520  *
       
   521  * @class
       
   522  * @augments Backbone.Router
       
   523  */
       
   524 var Router = Backbone.Router.extend(/** @lends wp.media.view.MediaFrame.Manage.Router.prototype */{
       
   525 	routes: {
       
   526 		'upload.php?item=:slug&mode=edit': 'editItem',
       
   527 		'upload.php?item=:slug':           'showItem',
       
   528 		'upload.php?search=:query':        'search',
       
   529 		'upload.php':                      'reset'
       
   530 	},
       
   531 
       
   532 	// Map routes against the page URL.
       
   533 	baseUrl: function( url ) {
       
   534 		return 'upload.php' + url;
       
   535 	},
       
   536 
       
   537 	reset: function() {
       
   538 		var frame = wp.media.frames.edit;
       
   539 
       
   540 		if ( frame ) {
       
   541 			frame.close();
       
   542 		}
       
   543 	},
       
   544 
       
   545 	// Respond to the search route by filling the search field and trigggering the input event.
       
   546 	search: function( query ) {
       
   547 		jQuery( '#media-search-input' ).val( query ).trigger( 'input' );
       
   548 	},
       
   549 
       
   550 	// Show the modal with a specific item.
       
   551 	showItem: function( query ) {
       
   552 		var media = wp.media,
       
   553 			frame = media.frames.browse,
       
   554 			library = frame.state().get('library'),
       
   555 			item;
       
   556 
       
   557 		// Trigger the media frame to open the correct item.
       
   558 		item = library.findWhere( { id: parseInt( query, 10 ) } );
       
   559 		item.set( 'skipHistory', true );
       
   560 
       
   561 		if ( item ) {
       
   562 			frame.trigger( 'edit:attachment', item );
       
   563 		} else {
       
   564 			item = media.attachment( query );
       
   565 			frame.listenTo( item, 'change', function( model ) {
       
   566 				frame.stopListening( item );
       
   567 				frame.trigger( 'edit:attachment', model );
       
   568 			} );
       
   569 			item.fetch();
       
   570 		}
       
   571 	},
       
   572 
       
   573 	// Show the modal in edit mode with a specific item.
       
   574 	editItem: function( query ) {
       
   575 		this.showItem( query );
       
   576 		wp.media.frames.edit.content.mode( 'edit-details' );
       
   577 	}
       
   578 });
       
   579 
       
   580 module.exports = Router;
       
   581 
       
   582 
       
   583 /***/ }),
       
   584 /* 17 */
       
   585 /***/ (function(module, exports) {
       
   586 
       
   587 var View = wp.media.View,
       
   588 	EditImage = wp.media.view.EditImage,
       
   589 	Details;
       
   590 
       
   591 /**
       
   592  * wp.media.view.EditImage.Details
       
   593  *
       
   594  * @memberOf wp.media.view.EditImage
       
   595  *
       
   596  * @class
       
   597  * @augments wp.media.view.EditImage
       
   598  * @augments wp.media.View
       
   599  * @augments wp.Backbone.View
       
   600  * @augments Backbone.View
       
   601  */
       
   602 Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype */{
       
   603 	initialize: function( options ) {
       
   604 		this.editor = window.imageEdit;
       
   605 		this.frame = options.frame;
       
   606 		this.controller = options.controller;
       
   607 		View.prototype.initialize.apply( this, arguments );
       
   608 	},
       
   609 
       
   610 	back: function() {
       
   611 		this.frame.content.mode( 'edit-metadata' );
       
   612 	},
       
   613 
       
   614 	save: function() {
       
   615 		this.model.fetch().done( _.bind( function() {
       
   616 			this.frame.content.mode( 'edit-metadata' );
       
   617 		}, this ) );
       
   618 	}
       
   619 });
       
   620 
       
   621 module.exports = Details;
       
   622 
       
   623 
       
   624 /***/ }),
       
   625 /* 18 */
       
   626 /***/ (function(module, exports) {
       
   627 
       
   628 var Frame = wp.media.view.Frame,
       
   629 	MediaFrame = wp.media.view.MediaFrame,
       
   630 
       
   631 	$ = jQuery,
       
   632 	EditAttachments;
       
   633 
       
   634 /**
       
   635  * wp.media.view.MediaFrame.EditAttachments
       
   636  *
       
   637  * A frame for editing the details of a specific media item.
       
   638  *
       
   639  * Opens in a modal by default.
       
   640  *
       
   641  * Requires an attachment model to be passed in the options hash under `model`.
       
   642  *
       
   643  * @memberOf wp.media.view.MediaFrame
       
   644  *
       
   645  * @class
       
   646  * @augments wp.media.view.Frame
       
   647  * @augments wp.media.View
       
   648  * @augments wp.Backbone.View
       
   649  * @augments Backbone.View
       
   650  * @mixes wp.media.controller.StateMachine
       
   651  */
       
   652 EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAttachments.prototype */{
       
   653 
       
   654 	className: 'edit-attachment-frame',
       
   655 	template:  wp.template( 'edit-attachment-frame' ),
       
   656 	regions:   [ 'title', 'content' ],
       
   657 
       
   658 	events: {
       
   659 		'click .left':  'previousMediaItem',
       
   660 		'click .right': 'nextMediaItem'
       
   661 	},
       
   662 
       
   663 	initialize: function() {
       
   664 		Frame.prototype.initialize.apply( this, arguments );
       
   665 
       
   666 		_.defaults( this.options, {
       
   667 			modal: true,
       
   668 			state: 'edit-attachment'
       
   669 		});
       
   670 
       
   671 		this.controller = this.options.controller;
       
   672 		this.gridRouter = this.controller.gridRouter;
       
   673 		this.library = this.options.library;
       
   674 
       
   675 		if ( this.options.model ) {
       
   676 			this.model = this.options.model;
       
   677 		}
       
   678 
       
   679 		this.bindHandlers();
       
   680 		this.createStates();
       
   681 		this.createModal();
       
   682 
       
   683 		this.title.mode( 'default' );
       
   684 		this.toggleNav();
       
   685 	},
       
   686 
       
   687 	bindHandlers: function() {
       
   688 		// Bind default title creation.
       
   689 		this.on( 'title:create:default', this.createTitle, this );
       
   690 
       
   691 		this.on( 'content:create:edit-metadata', this.editMetadataMode, this );
       
   692 		this.on( 'content:create:edit-image', this.editImageMode, this );
       
   693 		this.on( 'content:render:edit-image', this.editImageModeRender, this );
       
   694 		this.on( 'refresh', this.rerender, this );
       
   695 		this.on( 'close', this.detach );
       
   696 
       
   697 		this.bindModelHandlers();
       
   698 		this.listenTo( this.gridRouter, 'route:search', this.close, this );
       
   699 	},
       
   700 
       
   701 	bindModelHandlers: function() {
       
   702 		// Close the modal if the attachment is deleted.
       
   703 		this.listenTo( this.model, 'change:status destroy', this.close, this );
       
   704 	},
       
   705 
       
   706 	createModal: function() {
       
   707 		// Initialize modal container view.
       
   708 		if ( this.options.modal ) {
       
   709 			this.modal = new wp.media.view.Modal({
       
   710 				controller:     this,
       
   711 				title:          this.options.title,
       
   712 				hasCloseButton: false
       
   713 			});
       
   714 
       
   715 			this.modal.on( 'open', _.bind( function () {
       
   716 				$( 'body' ).on( 'keydown.media-modal', _.bind( this.keyEvent, this ) );
       
   717 			}, this ) );
       
   718 
       
   719 			// Completely destroy the modal DOM element when closing it.
       
   720 			this.modal.on( 'close', _.bind( function() {
       
   721 				// Remove the keydown event.
       
   722 				$( 'body' ).off( 'keydown.media-modal' );
       
   723 				// Move focus back to the original item in the grid if possible.
       
   724 				$( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).focus();
       
   725 				this.resetRoute();
       
   726 			}, this ) );
       
   727 
       
   728 			// Set this frame as the modal's content.
       
   729 			this.modal.content( this );
       
   730 			this.modal.open();
       
   731 		}
       
   732 	},
       
   733 
       
   734 	/**
       
   735 	 * Add the default states to the frame.
       
   736 	 */
       
   737 	createStates: function() {
       
   738 		this.states.add([
       
   739 			new wp.media.controller.EditAttachmentMetadata({
       
   740 				model:   this.model,
       
   741 				library: this.library
       
   742 			})
       
   743 		]);
       
   744 	},
       
   745 
       
   746 	/**
       
   747 	 * Content region rendering callback for the `edit-metadata` mode.
       
   748 	 *
       
   749 	 * @param {Object} contentRegion Basic object with a `view` property, which
       
   750 	 *                               should be set with the proper region view.
       
   751 	 */
       
   752 	editMetadataMode: function( contentRegion ) {
       
   753 		contentRegion.view = new wp.media.view.Attachment.Details.TwoColumn({
       
   754 			controller: this,
       
   755 			model:      this.model
       
   756 		});
       
   757 
       
   758 		/**
       
   759 		 * Attach a subview to display fields added via the
       
   760 		 * `attachment_fields_to_edit` filter.
       
   761 		 */
       
   762 		contentRegion.view.views.set( '.attachment-compat', new wp.media.view.AttachmentCompat({
       
   763 			controller: this,
       
   764 			model:      this.model
       
   765 		}) );
       
   766 
       
   767 		// Update browser url when navigating media details, except on load.
       
   768 		if ( this.model && ! this.model.get( 'skipHistory' ) ) {
       
   769 			this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) );
       
   770 		}
       
   771 	},
       
   772 
       
   773 	/**
       
   774 	 * Render the EditImage view into the frame's content region.
       
   775 	 *
       
   776 	 * @param {Object} contentRegion Basic object with a `view` property, which
       
   777 	 *                               should be set with the proper region view.
       
   778 	 */
       
   779 	editImageMode: function( contentRegion ) {
       
   780 		var editImageController = new wp.media.controller.EditImage( {
       
   781 			model: this.model,
       
   782 			frame: this
       
   783 		} );
       
   784 		// Noop some methods.
       
   785 		editImageController._toolbar = function() {};
       
   786 		editImageController._router = function() {};
       
   787 		editImageController._menu = function() {};
       
   788 
       
   789 		contentRegion.view = new wp.media.view.EditImage.Details( {
       
   790 			model: this.model,
       
   791 			frame: this,
       
   792 			controller: editImageController
       
   793 		} );
       
   794 
       
   795 		this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) );
       
   796 
       
   797 	},
       
   798 
       
   799 	editImageModeRender: function( view ) {
       
   800 		view.on( 'ready', view.loadEditor );
       
   801 	},
       
   802 
       
   803 	toggleNav: function() {
       
   804 		this.$( '.left' ).prop( 'disabled', ! this.hasPrevious() );
       
   805 		this.$( '.right' ).prop( 'disabled', ! this.hasNext() );
       
   806 	},
       
   807 
       
   808 	/**
       
   809 	 * Rerender the view.
       
   810 	 */
       
   811 	rerender: function( model ) {
       
   812 		this.stopListening( this.model );
       
   813 
       
   814 		this.model = model;
       
   815 
       
   816 		this.bindModelHandlers();
       
   817 
       
   818 		// Only rerender the `content` region.
       
   819 		if ( this.content.mode() !== 'edit-metadata' ) {
       
   820 			this.content.mode( 'edit-metadata' );
       
   821 		} else {
       
   822 			this.content.render();
       
   823 		}
       
   824 
       
   825 		this.toggleNav();
       
   826 	},
       
   827 
       
   828 	/**
       
   829 	 * Click handler to switch to the previous media item.
       
   830 	 */
       
   831 	previousMediaItem: function() {
       
   832 		if ( ! this.hasPrevious() ) {
       
   833 			return;
       
   834 		}
       
   835 
       
   836 		this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) );
       
   837 		// Move focus to the Previous button. When there are no more items, to the Next button.
       
   838 		this.focusNavButton( this.hasPrevious() ? '.left' : '.right' );
       
   839 	},
       
   840 
       
   841 	/**
       
   842 	 * Click handler to switch to the next media item.
       
   843 	 */
       
   844 	nextMediaItem: function() {
       
   845 		if ( ! this.hasNext() ) {
       
   846 			return;
       
   847 		}
       
   848 
       
   849 		this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) );
       
   850 		// Move focus to the Next button. When there are no more items, to the Previous button.
       
   851 		this.focusNavButton( this.hasNext() ? '.right' : '.left' );
       
   852 	},
       
   853 
       
   854 	/**
       
   855 	 * Set focus to the navigation buttons depending on the browsing direction.
       
   856 	 *
       
   857 	 * @since 5.3.0
       
   858 	 *
       
   859 	 * @param {string} which A CSS selector to target the button to focus.
       
   860 	 */
       
   861 	focusNavButton: function( which ) {
       
   862 		$( which ).focus();
       
   863 	},
       
   864 
       
   865 	getCurrentIndex: function() {
       
   866 		return this.library.indexOf( this.model );
       
   867 	},
       
   868 
       
   869 	hasNext: function() {
       
   870 		return ( this.getCurrentIndex() + 1 ) < this.library.length;
       
   871 	},
       
   872 
       
   873 	hasPrevious: function() {
       
   874 		return ( this.getCurrentIndex() - 1 ) > -1;
       
   875 	},
       
   876 	/**
       
   877 	 * Respond to the keyboard events: right arrow, left arrow, except when
       
   878 	 * focus is in a textarea or input field.
       
   879 	 */
       
   880 	keyEvent: function( event ) {
       
   881 		if ( ( 'INPUT' === event.target.nodeName || 'TEXTAREA' === event.target.nodeName ) && ! ( event.target.readOnly || event.target.disabled ) ) {
       
   882 			return;
       
   883 		}
       
   884 
       
   885 		// The right arrow key.
       
   886 		if ( 39 === event.keyCode ) {
       
   887 			this.nextMediaItem();
       
   888 		}
       
   889 		// The left arrow key.
       
   890 		if ( 37 === event.keyCode ) {
       
   891 			this.previousMediaItem();
       
   892 		}
       
   893 	},
       
   894 
       
   895 	resetRoute: function() {
       
   896 		var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(),
       
   897 			url = '' !== searchTerm ? '?search=' + searchTerm : '';
       
   898 		this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
       
   899 	}
       
   900 });
       
   901 
       
   902 module.exports = EditAttachments;
       
   903 
       
   904 
       
   905 /***/ }),
       
   906 /* 19 */
       
   907 /***/ (function(module, exports) {
   201 /***/ (function(module, exports) {
   908 
   202 
   909 
   203 
   910 var Button = wp.media.view.Button,
   204 var Button = wp.media.view.Button,
   911 	l10n = wp.media.view.l10n,
   205 	l10n = wp.media.view.l10n,
   984 
   278 
   985 module.exports = SelectModeToggle;
   279 module.exports = SelectModeToggle;
   986 
   280 
   987 
   281 
   988 /***/ }),
   282 /***/ }),
   989 /* 20 */
   283 
       
   284 /***/ "HUrf":
       
   285 /***/ (function(module, exports) {
       
   286 
       
   287 var View = wp.media.View,
       
   288 	EditImage = wp.media.view.EditImage,
       
   289 	Details;
       
   290 
       
   291 /**
       
   292  * wp.media.view.EditImage.Details
       
   293  *
       
   294  * @memberOf wp.media.view.EditImage
       
   295  *
       
   296  * @class
       
   297  * @augments wp.media.view.EditImage
       
   298  * @augments wp.media.View
       
   299  * @augments wp.Backbone.View
       
   300  * @augments Backbone.View
       
   301  */
       
   302 Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype */{
       
   303 	initialize: function( options ) {
       
   304 		this.editor = window.imageEdit;
       
   305 		this.frame = options.frame;
       
   306 		this.controller = options.controller;
       
   307 		View.prototype.initialize.apply( this, arguments );
       
   308 	},
       
   309 
       
   310 	back: function() {
       
   311 		this.frame.content.mode( 'edit-metadata' );
       
   312 	},
       
   313 
       
   314 	save: function() {
       
   315 		this.model.fetch().done( _.bind( function() {
       
   316 			this.frame.content.mode( 'edit-metadata' );
       
   317 		}, this ) );
       
   318 	}
       
   319 });
       
   320 
       
   321 module.exports = Details;
       
   322 
       
   323 
       
   324 /***/ }),
       
   325 
       
   326 /***/ "LRQ5":
       
   327 /***/ (function(module, exports, __webpack_require__) {
       
   328 
       
   329 /**
       
   330  * @output wp-includes/js/media-grid.js
       
   331  */
       
   332 
       
   333 var media = wp.media;
       
   334 
       
   335 media.controller.EditAttachmentMetadata = __webpack_require__( "ZJBI" );
       
   336 media.view.MediaFrame.Manage = __webpack_require__( "lH8y" );
       
   337 media.view.Attachment.Details.TwoColumn = __webpack_require__( "FcM5" );
       
   338 media.view.MediaFrame.Manage.Router = __webpack_require__( "OMfl" );
       
   339 media.view.EditImage.Details = __webpack_require__( "HUrf" );
       
   340 media.view.MediaFrame.EditAttachments = __webpack_require__( "wQX5" );
       
   341 media.view.SelectModeToggleButton = __webpack_require__( "Ffsb" );
       
   342 media.view.DeleteSelectedButton = __webpack_require__( "nD7t" );
       
   343 media.view.DeleteSelectedPermanentlyButton = __webpack_require__( "1lLZ" );
       
   344 
       
   345 
       
   346 /***/ }),
       
   347 
       
   348 /***/ "OMfl":
       
   349 /***/ (function(module, exports) {
       
   350 
       
   351 /**
       
   352  * wp.media.view.MediaFrame.Manage.Router
       
   353  *
       
   354  * A router for handling the browser history and application state.
       
   355  *
       
   356  * @memberOf wp.media.view.MediaFrame.Manage
       
   357  *
       
   358  * @class
       
   359  * @augments Backbone.Router
       
   360  */
       
   361 var Router = Backbone.Router.extend(/** @lends wp.media.view.MediaFrame.Manage.Router.prototype */{
       
   362 	routes: {
       
   363 		'upload.php?item=:slug&mode=edit': 'editItem',
       
   364 		'upload.php?item=:slug':           'showItem',
       
   365 		'upload.php?search=:query':        'search',
       
   366 		'upload.php':                      'reset'
       
   367 	},
       
   368 
       
   369 	// Map routes against the page URL.
       
   370 	baseUrl: function( url ) {
       
   371 		return 'upload.php' + url;
       
   372 	},
       
   373 
       
   374 	reset: function() {
       
   375 		var frame = wp.media.frames.edit;
       
   376 
       
   377 		if ( frame ) {
       
   378 			frame.close();
       
   379 		}
       
   380 	},
       
   381 
       
   382 	// Respond to the search route by filling the search field and triggering the input event.
       
   383 	search: function( query ) {
       
   384 		jQuery( '#media-search-input' ).val( query ).trigger( 'input' );
       
   385 	},
       
   386 
       
   387 	// Show the modal with a specific item.
       
   388 	showItem: function( query ) {
       
   389 		var media = wp.media,
       
   390 			frame = media.frames.browse,
       
   391 			library = frame.state().get('library'),
       
   392 			item;
       
   393 
       
   394 		// Trigger the media frame to open the correct item.
       
   395 		item = library.findWhere( { id: parseInt( query, 10 ) } );
       
   396 
       
   397 		if ( item ) {
       
   398 			item.set( 'skipHistory', true );
       
   399 			frame.trigger( 'edit:attachment', item );
       
   400 		} else {
       
   401 			item = media.attachment( query );
       
   402 			frame.listenTo( item, 'change', function( model ) {
       
   403 				frame.stopListening( item );
       
   404 				frame.trigger( 'edit:attachment', model );
       
   405 			} );
       
   406 			item.fetch();
       
   407 		}
       
   408 	},
       
   409 
       
   410 	// Show the modal in edit mode with a specific item.
       
   411 	editItem: function( query ) {
       
   412 		this.showItem( query );
       
   413 		wp.media.frames.edit.content.mode( 'edit-details' );
       
   414 	}
       
   415 });
       
   416 
       
   417 module.exports = Router;
       
   418 
       
   419 
       
   420 /***/ }),
       
   421 
       
   422 /***/ "ZJBI":
       
   423 /***/ (function(module, exports) {
       
   424 
       
   425 var l10n = wp.media.view.l10n,
       
   426 	EditAttachmentMetadata;
       
   427 
       
   428 /**
       
   429  * wp.media.controller.EditAttachmentMetadata
       
   430  *
       
   431  * A state for editing an attachment's metadata.
       
   432  *
       
   433  * @memberOf wp.media.controller
       
   434  *
       
   435  * @class
       
   436  * @augments wp.media.controller.State
       
   437  * @augments Backbone.Model
       
   438  */
       
   439 EditAttachmentMetadata = wp.media.controller.State.extend(/** @lends wp.media.controller.EditAttachmentMetadata.prototype */{
       
   440 	defaults: {
       
   441 		id:      'edit-attachment',
       
   442 		// Title string passed to the frame's title region view.
       
   443 		title:   l10n.attachmentDetails,
       
   444 		// Region mode defaults.
       
   445 		content: 'edit-metadata',
       
   446 		menu:    false,
       
   447 		toolbar: false,
       
   448 		router:  false
       
   449 	}
       
   450 });
       
   451 
       
   452 module.exports = EditAttachmentMetadata;
       
   453 
       
   454 
       
   455 /***/ }),
       
   456 
       
   457 /***/ "lH8y":
       
   458 /***/ (function(module, exports) {
       
   459 
       
   460 var MediaFrame = wp.media.view.MediaFrame,
       
   461 	Library = wp.media.controller.Library,
       
   462 
       
   463 	$ = Backbone.$,
       
   464 	Manage;
       
   465 
       
   466 /**
       
   467  * wp.media.view.MediaFrame.Manage
       
   468  *
       
   469  * A generic management frame workflow.
       
   470  *
       
   471  * Used in the media grid view.
       
   472  *
       
   473  * @memberOf wp.media.view.MediaFrame
       
   474  *
       
   475  * @class
       
   476  * @augments wp.media.view.MediaFrame
       
   477  * @augments wp.media.view.Frame
       
   478  * @augments wp.media.View
       
   479  * @augments wp.Backbone.View
       
   480  * @augments Backbone.View
       
   481  * @mixes wp.media.controller.StateMachine
       
   482  */
       
   483 Manage = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Manage.prototype */{
       
   484 	/**
       
   485 	 * @constructs
       
   486 	 */
       
   487 	initialize: function() {
       
   488 		_.defaults( this.options, {
       
   489 			title:     '',
       
   490 			modal:     false,
       
   491 			selection: [],
       
   492 			library:   {}, // Options hash for the query to the media library.
       
   493 			multiple:  'add',
       
   494 			state:     'library',
       
   495 			uploader:  true,
       
   496 			mode:      [ 'grid', 'edit' ]
       
   497 		});
       
   498 
       
   499 		this.$body = $( document.body );
       
   500 		this.$window = $( window );
       
   501 		this.$adminBar = $( '#wpadminbar' );
       
   502 		// Store the Add New button for later reuse in wp.media.view.UploaderInline.
       
   503 		this.$uploaderToggler = $( '.page-title-action' )
       
   504 			.attr( 'aria-expanded', 'false' )
       
   505 			.on( 'click', _.bind( this.addNewClickHandler, this ) );
       
   506 
       
   507 		this.$window.on( 'scroll resize', _.debounce( _.bind( this.fixPosition, this ), 15 ) );
       
   508 
       
   509 		// Ensure core and media grid view UI is enabled.
       
   510 		this.$el.addClass('wp-core-ui');
       
   511 
       
   512 		// Force the uploader off if the upload limit has been exceeded or
       
   513 		// if the browser isn't supported.
       
   514 		if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
       
   515 			this.options.uploader = false;
       
   516 		}
       
   517 
       
   518 		// Initialize a window-wide uploader.
       
   519 		if ( this.options.uploader ) {
       
   520 			this.uploader = new wp.media.view.UploaderWindow({
       
   521 				controller: this,
       
   522 				uploader: {
       
   523 					dropzone:  document.body,
       
   524 					container: document.body
       
   525 				}
       
   526 			}).render();
       
   527 			this.uploader.ready();
       
   528 			$('body').append( this.uploader.el );
       
   529 
       
   530 			this.options.uploader = false;
       
   531 		}
       
   532 
       
   533 		this.gridRouter = new wp.media.view.MediaFrame.Manage.Router();
       
   534 
       
   535 		// Call 'initialize' directly on the parent class.
       
   536 		MediaFrame.prototype.initialize.apply( this, arguments );
       
   537 
       
   538 		// Append the frame view directly the supplied container.
       
   539 		this.$el.appendTo( this.options.container );
       
   540 
       
   541 		this.createStates();
       
   542 		this.bindRegionModeHandlers();
       
   543 		this.render();
       
   544 		this.bindSearchHandler();
       
   545 
       
   546 		wp.media.frames.browse = this;
       
   547 	},
       
   548 
       
   549 	bindSearchHandler: function() {
       
   550 		var search = this.$( '#media-search-input' ),
       
   551 			searchView = this.browserView.toolbar.get( 'search' ).$el,
       
   552 			listMode = this.$( '.view-list' ),
       
   553 
       
   554 			input  = _.throttle( function (e) {
       
   555 				var val = $( e.currentTarget ).val(),
       
   556 					url = '';
       
   557 
       
   558 				if ( val ) {
       
   559 					url += '?search=' + val;
       
   560 					this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
       
   561 				}
       
   562 			}, 1000 );
       
   563 
       
   564 		// Update the URL when entering search string (at most once per second).
       
   565 		search.on( 'input', _.bind( input, this ) );
       
   566 
       
   567 		this.gridRouter
       
   568 			.on( 'route:search', function () {
       
   569 				var href = window.location.href;
       
   570 				if ( href.indexOf( 'mode=' ) > -1 ) {
       
   571 					href = href.replace( /mode=[^&]+/g, 'mode=list' );
       
   572 				} else {
       
   573 					href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list';
       
   574 				}
       
   575 				href = href.replace( 'search=', 's=' );
       
   576 				listMode.prop( 'href', href );
       
   577 			})
       
   578 			.on( 'route:reset', function() {
       
   579 				searchView.val( '' ).trigger( 'input' );
       
   580 			});
       
   581 	},
       
   582 
       
   583 	/**
       
   584 	 * Create the default states for the frame.
       
   585 	 */
       
   586 	createStates: function() {
       
   587 		var options = this.options;
       
   588 
       
   589 		if ( this.options.states ) {
       
   590 			return;
       
   591 		}
       
   592 
       
   593 		// Add the default states.
       
   594 		this.states.add([
       
   595 			new Library({
       
   596 				library:            wp.media.query( options.library ),
       
   597 				multiple:           options.multiple,
       
   598 				title:              options.title,
       
   599 				content:            'browse',
       
   600 				toolbar:            'select',
       
   601 				contentUserSetting: false,
       
   602 				filterable:         'all',
       
   603 				autoSelect:         false
       
   604 			})
       
   605 		]);
       
   606 	},
       
   607 
       
   608 	/**
       
   609 	 * Bind region mode activation events to proper handlers.
       
   610 	 */
       
   611 	bindRegionModeHandlers: function() {
       
   612 		this.on( 'content:create:browse', this.browseContent, this );
       
   613 
       
   614 		// Handle a frame-level event for editing an attachment.
       
   615 		this.on( 'edit:attachment', this.openEditAttachmentModal, this );
       
   616 
       
   617 		this.on( 'select:activate', this.bindKeydown, this );
       
   618 		this.on( 'select:deactivate', this.unbindKeydown, this );
       
   619 	},
       
   620 
       
   621 	handleKeydown: function( e ) {
       
   622 		if ( 27 === e.which ) {
       
   623 			e.preventDefault();
       
   624 			this.deactivateMode( 'select' ).activateMode( 'edit' );
       
   625 		}
       
   626 	},
       
   627 
       
   628 	bindKeydown: function() {
       
   629 		this.$body.on( 'keydown.select', _.bind( this.handleKeydown, this ) );
       
   630 	},
       
   631 
       
   632 	unbindKeydown: function() {
       
   633 		this.$body.off( 'keydown.select' );
       
   634 	},
       
   635 
       
   636 	fixPosition: function() {
       
   637 		var $browser, $toolbar;
       
   638 		if ( ! this.isModeActive( 'select' ) ) {
       
   639 			return;
       
   640 		}
       
   641 
       
   642 		$browser = this.$('.attachments-browser');
       
   643 		$toolbar = $browser.find('.media-toolbar');
       
   644 
       
   645 		// Offset doesn't appear to take top margin into account, hence +16.
       
   646 		if ( ( $browser.offset().top + 16 ) < this.$window.scrollTop() + this.$adminBar.height() ) {
       
   647 			$browser.addClass( 'fixed' );
       
   648 			$toolbar.css('width', $browser.width() + 'px');
       
   649 		} else {
       
   650 			$browser.removeClass( 'fixed' );
       
   651 			$toolbar.css('width', '');
       
   652 		}
       
   653 	},
       
   654 
       
   655 	/**
       
   656 	 * Click handler for the `Add New` button.
       
   657 	 */
       
   658 	addNewClickHandler: function( event ) {
       
   659 		event.preventDefault();
       
   660 		this.trigger( 'toggle:upload:attachment' );
       
   661 
       
   662 		if ( this.uploader ) {
       
   663 			this.uploader.refresh();
       
   664 		}
       
   665 	},
       
   666 
       
   667 	/**
       
   668 	 * Open the Edit Attachment modal.
       
   669 	 */
       
   670 	openEditAttachmentModal: function( model ) {
       
   671 		// Create a new EditAttachment frame, passing along the library and the attachment model.
       
   672 		if ( wp.media.frames.edit ) {
       
   673 			wp.media.frames.edit.open().trigger( 'refresh', model );
       
   674 		} else {
       
   675 			wp.media.frames.edit = wp.media( {
       
   676 				frame:       'edit-attachments',
       
   677 				controller:  this,
       
   678 				library:     this.state().get('library'),
       
   679 				model:       model
       
   680 			} );
       
   681 		}
       
   682 	},
       
   683 
       
   684 	/**
       
   685 	 * Create an attachments browser view within the content region.
       
   686 	 *
       
   687 	 * @param {Object} contentRegion Basic object with a `view` property, which
       
   688 	 *                               should be set with the proper region view.
       
   689 	 * @this wp.media.controller.Region
       
   690 	 */
       
   691 	browseContent: function( contentRegion ) {
       
   692 		var state = this.state();
       
   693 
       
   694 		// Browse our library of attachments.
       
   695 		this.browserView = contentRegion.view = new wp.media.view.AttachmentsBrowser({
       
   696 			controller: this,
       
   697 			collection: state.get('library'),
       
   698 			selection:  state.get('selection'),
       
   699 			model:      state,
       
   700 			sortable:   state.get('sortable'),
       
   701 			search:     state.get('searchable'),
       
   702 			filters:    state.get('filterable'),
       
   703 			date:       state.get('date'),
       
   704 			display:    state.get('displaySettings'),
       
   705 			dragInfo:   state.get('dragInfo'),
       
   706 			sidebar:    'errors',
       
   707 
       
   708 			suggestedWidth:  state.get('suggestedWidth'),
       
   709 			suggestedHeight: state.get('suggestedHeight'),
       
   710 
       
   711 			AttachmentView: state.get('AttachmentView'),
       
   712 
       
   713 			scrollElement: document
       
   714 		});
       
   715 		this.browserView.on( 'ready', _.bind( this.bindDeferred, this ) );
       
   716 
       
   717 		this.errors = wp.Uploader.errors;
       
   718 		this.errors.on( 'add remove reset', this.sidebarVisibility, this );
       
   719 	},
       
   720 
       
   721 	sidebarVisibility: function() {
       
   722 		this.browserView.$( '.media-sidebar' ).toggle( !! this.errors.length );
       
   723 	},
       
   724 
       
   725 	bindDeferred: function() {
       
   726 		if ( ! this.browserView.dfd ) {
       
   727 			return;
       
   728 		}
       
   729 		this.browserView.dfd.done( _.bind( this.startHistory, this ) );
       
   730 	},
       
   731 
       
   732 	startHistory: function() {
       
   733 		// Verify pushState support and activate.
       
   734 		if ( window.history && window.history.pushState ) {
       
   735 			if ( Backbone.History.started ) {
       
   736 				Backbone.history.stop();
       
   737 			}
       
   738 			Backbone.history.start( {
       
   739 				root: window._wpMediaGridSettings.adminUrl,
       
   740 				pushState: true
       
   741 			} );
       
   742 		}
       
   743 	}
       
   744 });
       
   745 
       
   746 module.exports = Manage;
       
   747 
       
   748 
       
   749 /***/ }),
       
   750 
       
   751 /***/ "nD7t":
   990 /***/ (function(module, exports) {
   752 /***/ (function(module, exports) {
   991 
   753 
   992 var Button = wp.media.view.Button,
   754 var Button = wp.media.view.Button,
   993 	l10n = wp.media.view.l10n,
   755 	l10n = wp.media.view.l10n,
   994 	DeleteSelected;
   756 	DeleteSelected;
  1044 
   806 
  1045 module.exports = DeleteSelected;
   807 module.exports = DeleteSelected;
  1046 
   808 
  1047 
   809 
  1048 /***/ }),
   810 /***/ }),
  1049 /* 21 */
   811 
       
   812 /***/ "wQX5":
  1050 /***/ (function(module, exports) {
   813 /***/ (function(module, exports) {
  1051 
   814 
  1052 var Button = wp.media.view.Button,
   815 var Frame = wp.media.view.Frame,
  1053 	DeleteSelected = wp.media.view.DeleteSelectedButton,
   816 	MediaFrame = wp.media.view.MediaFrame,
  1054 	DeleteSelectedPermanently;
   817 
       
   818 	$ = jQuery,
       
   819 	EditAttachments;
  1055 
   820 
  1056 /**
   821 /**
  1057  * wp.media.view.DeleteSelectedPermanentlyButton
   822  * wp.media.view.MediaFrame.EditAttachments
  1058  *
   823  *
  1059  * When MEDIA_TRASH is true, a button that handles bulk Delete Permanently logic
   824  * A frame for editing the details of a specific media item.
  1060  *
   825  *
  1061  * @memberOf wp.media.view
   826  * Opens in a modal by default.
       
   827  *
       
   828  * Requires an attachment model to be passed in the options hash under `model`.
       
   829  *
       
   830  * @memberOf wp.media.view.MediaFrame
  1062  *
   831  *
  1063  * @class
   832  * @class
  1064  * @augments wp.media.view.DeleteSelectedButton
   833  * @augments wp.media.view.Frame
  1065  * @augments wp.media.view.Button
       
  1066  * @augments wp.media.View
   834  * @augments wp.media.View
  1067  * @augments wp.Backbone.View
   835  * @augments wp.Backbone.View
  1068  * @augments Backbone.View
   836  * @augments Backbone.View
       
   837  * @mixes wp.media.controller.StateMachine
  1069  */
   838  */
  1070 DeleteSelectedPermanently = DeleteSelected.extend(/** @lends wp.media.view.DeleteSelectedPermanentlyButton.prototype */{
   839 EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAttachments.prototype */{
       
   840 
       
   841 	className: 'edit-attachment-frame',
       
   842 	template:  wp.template( 'edit-attachment-frame' ),
       
   843 	regions:   [ 'title', 'content' ],
       
   844 
       
   845 	events: {
       
   846 		'click .left':  'previousMediaItem',
       
   847 		'click .right': 'nextMediaItem'
       
   848 	},
       
   849 
  1071 	initialize: function() {
   850 	initialize: function() {
  1072 		DeleteSelected.prototype.initialize.apply( this, arguments );
   851 		Frame.prototype.initialize.apply( this, arguments );
  1073 		this.controller.on( 'select:activate', this.selectActivate, this );
   852 
  1074 		this.controller.on( 'select:deactivate', this.selectDeactivate, this );
   853 		_.defaults( this.options, {
  1075 	},
   854 			modal: true,
  1076 
   855 			state: 'edit-attachment'
  1077 	filterChange: function( model ) {
   856 		});
  1078 		this.canShow = ( 'trash' === model.get( 'status' ) );
   857 
  1079 	},
   858 		this.controller = this.options.controller;
  1080 
   859 		this.gridRouter = this.controller.gridRouter;
  1081 	selectActivate: function() {
   860 		this.library = this.options.library;
  1082 		this.toggleDisabled();
   861 
  1083 		this.$el.toggleClass( 'hidden', ! this.canShow );
   862 		if ( this.options.model ) {
  1084 	},
   863 			this.model = this.options.model;
  1085 
   864 		}
  1086 	selectDeactivate: function() {
   865 
  1087 		this.toggleDisabled();
   866 		this.bindHandlers();
  1088 		this.$el.addClass( 'hidden' );
   867 		this.createStates();
  1089 	},
   868 		this.createModal();
  1090 
   869 
  1091 	render: function() {
   870 		this.title.mode( 'default' );
  1092 		Button.prototype.render.apply( this, arguments );
   871 		this.toggleNav();
  1093 		this.selectActivate();
   872 	},
  1094 		return this;
   873 
       
   874 	bindHandlers: function() {
       
   875 		// Bind default title creation.
       
   876 		this.on( 'title:create:default', this.createTitle, this );
       
   877 
       
   878 		this.on( 'content:create:edit-metadata', this.editMetadataMode, this );
       
   879 		this.on( 'content:create:edit-image', this.editImageMode, this );
       
   880 		this.on( 'content:render:edit-image', this.editImageModeRender, this );
       
   881 		this.on( 'refresh', this.rerender, this );
       
   882 		this.on( 'close', this.detach );
       
   883 
       
   884 		this.bindModelHandlers();
       
   885 		this.listenTo( this.gridRouter, 'route:search', this.close, this );
       
   886 	},
       
   887 
       
   888 	bindModelHandlers: function() {
       
   889 		// Close the modal if the attachment is deleted.
       
   890 		this.listenTo( this.model, 'change:status destroy', this.close, this );
       
   891 	},
       
   892 
       
   893 	createModal: function() {
       
   894 		// Initialize modal container view.
       
   895 		if ( this.options.modal ) {
       
   896 			this.modal = new wp.media.view.Modal({
       
   897 				controller:     this,
       
   898 				title:          this.options.title,
       
   899 				hasCloseButton: false
       
   900 			});
       
   901 
       
   902 			this.modal.on( 'open', _.bind( function () {
       
   903 				$( 'body' ).on( 'keydown.media-modal', _.bind( this.keyEvent, this ) );
       
   904 			}, this ) );
       
   905 
       
   906 			// Completely destroy the modal DOM element when closing it.
       
   907 			this.modal.on( 'close', _.bind( function() {
       
   908 				// Remove the keydown event.
       
   909 				$( 'body' ).off( 'keydown.media-modal' );
       
   910 				// Move focus back to the original item in the grid if possible.
       
   911 				$( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).trigger( 'focus' );
       
   912 				this.resetRoute();
       
   913 			}, this ) );
       
   914 
       
   915 			// Set this frame as the modal's content.
       
   916 			this.modal.content( this );
       
   917 			this.modal.open();
       
   918 		}
       
   919 	},
       
   920 
       
   921 	/**
       
   922 	 * Add the default states to the frame.
       
   923 	 */
       
   924 	createStates: function() {
       
   925 		this.states.add([
       
   926 			new wp.media.controller.EditAttachmentMetadata({
       
   927 				model:   this.model,
       
   928 				library: this.library
       
   929 			})
       
   930 		]);
       
   931 	},
       
   932 
       
   933 	/**
       
   934 	 * Content region rendering callback for the `edit-metadata` mode.
       
   935 	 *
       
   936 	 * @param {Object} contentRegion Basic object with a `view` property, which
       
   937 	 *                               should be set with the proper region view.
       
   938 	 */
       
   939 	editMetadataMode: function( contentRegion ) {
       
   940 		contentRegion.view = new wp.media.view.Attachment.Details.TwoColumn({
       
   941 			controller: this,
       
   942 			model:      this.model
       
   943 		});
       
   944 
       
   945 		/**
       
   946 		 * Attach a subview to display fields added via the
       
   947 		 * `attachment_fields_to_edit` filter.
       
   948 		 */
       
   949 		contentRegion.view.views.set( '.attachment-compat', new wp.media.view.AttachmentCompat({
       
   950 			controller: this,
       
   951 			model:      this.model
       
   952 		}) );
       
   953 
       
   954 		// Update browser url when navigating media details, except on load.
       
   955 		if ( this.model && ! this.model.get( 'skipHistory' ) ) {
       
   956 			this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) );
       
   957 		}
       
   958 	},
       
   959 
       
   960 	/**
       
   961 	 * Render the EditImage view into the frame's content region.
       
   962 	 *
       
   963 	 * @param {Object} contentRegion Basic object with a `view` property, which
       
   964 	 *                               should be set with the proper region view.
       
   965 	 */
       
   966 	editImageMode: function( contentRegion ) {
       
   967 		var editImageController = new wp.media.controller.EditImage( {
       
   968 			model: this.model,
       
   969 			frame: this
       
   970 		} );
       
   971 		// Noop some methods.
       
   972 		editImageController._toolbar = function() {};
       
   973 		editImageController._router = function() {};
       
   974 		editImageController._menu = function() {};
       
   975 
       
   976 		contentRegion.view = new wp.media.view.EditImage.Details( {
       
   977 			model: this.model,
       
   978 			frame: this,
       
   979 			controller: editImageController
       
   980 		} );
       
   981 
       
   982 		this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) );
       
   983 
       
   984 	},
       
   985 
       
   986 	editImageModeRender: function( view ) {
       
   987 		view.on( 'ready', view.loadEditor );
       
   988 	},
       
   989 
       
   990 	toggleNav: function() {
       
   991 		this.$( '.left' ).prop( 'disabled', ! this.hasPrevious() );
       
   992 		this.$( '.right' ).prop( 'disabled', ! this.hasNext() );
       
   993 	},
       
   994 
       
   995 	/**
       
   996 	 * Rerender the view.
       
   997 	 */
       
   998 	rerender: function( model ) {
       
   999 		this.stopListening( this.model );
       
  1000 
       
  1001 		this.model = model;
       
  1002 
       
  1003 		this.bindModelHandlers();
       
  1004 
       
  1005 		// Only rerender the `content` region.
       
  1006 		if ( this.content.mode() !== 'edit-metadata' ) {
       
  1007 			this.content.mode( 'edit-metadata' );
       
  1008 		} else {
       
  1009 			this.content.render();
       
  1010 		}
       
  1011 
       
  1012 		this.toggleNav();
       
  1013 	},
       
  1014 
       
  1015 	/**
       
  1016 	 * Click handler to switch to the previous media item.
       
  1017 	 */
       
  1018 	previousMediaItem: function() {
       
  1019 		if ( ! this.hasPrevious() ) {
       
  1020 			return;
       
  1021 		}
       
  1022 
       
  1023 		this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) );
       
  1024 		// Move focus to the Previous button. When there are no more items, to the Next button.
       
  1025 		this.focusNavButton( this.hasPrevious() ? '.left' : '.right' );
       
  1026 	},
       
  1027 
       
  1028 	/**
       
  1029 	 * Click handler to switch to the next media item.
       
  1030 	 */
       
  1031 	nextMediaItem: function() {
       
  1032 		if ( ! this.hasNext() ) {
       
  1033 			return;
       
  1034 		}
       
  1035 
       
  1036 		this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) );
       
  1037 		// Move focus to the Next button. When there are no more items, to the Previous button.
       
  1038 		this.focusNavButton( this.hasNext() ? '.right' : '.left' );
       
  1039 	},
       
  1040 
       
  1041 	/**
       
  1042 	 * Set focus to the navigation buttons depending on the browsing direction.
       
  1043 	 *
       
  1044 	 * @since 5.3.0
       
  1045 	 *
       
  1046 	 * @param {string} which A CSS selector to target the button to focus.
       
  1047 	 */
       
  1048 	focusNavButton: function( which ) {
       
  1049 		$( which ).trigger( 'focus' );
       
  1050 	},
       
  1051 
       
  1052 	getCurrentIndex: function() {
       
  1053 		return this.library.indexOf( this.model );
       
  1054 	},
       
  1055 
       
  1056 	hasNext: function() {
       
  1057 		return ( this.getCurrentIndex() + 1 ) < this.library.length;
       
  1058 	},
       
  1059 
       
  1060 	hasPrevious: function() {
       
  1061 		return ( this.getCurrentIndex() - 1 ) > -1;
       
  1062 	},
       
  1063 	/**
       
  1064 	 * Respond to the keyboard events: right arrow, left arrow, except when
       
  1065 	 * focus is in a textarea or input field.
       
  1066 	 */
       
  1067 	keyEvent: function( event ) {
       
  1068 		if ( ( 'INPUT' === event.target.nodeName || 'TEXTAREA' === event.target.nodeName ) && ! ( event.target.readOnly || event.target.disabled ) ) {
       
  1069 			return;
       
  1070 		}
       
  1071 
       
  1072 		// The right arrow key.
       
  1073 		if ( 39 === event.keyCode ) {
       
  1074 			this.nextMediaItem();
       
  1075 		}
       
  1076 		// The left arrow key.
       
  1077 		if ( 37 === event.keyCode ) {
       
  1078 			this.previousMediaItem();
       
  1079 		}
       
  1080 	},
       
  1081 
       
  1082 	resetRoute: function() {
       
  1083 		var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(),
       
  1084 			url = '' !== searchTerm ? '?search=' + searchTerm : '';
       
  1085 		this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
  1095 	}
  1086 	}
  1096 });
  1087 });
  1097 
  1088 
  1098 module.exports = DeleteSelectedPermanently;
  1089 module.exports = EditAttachments;
  1099 
  1090 
  1100 
  1091 
  1101 /***/ })
  1092 /***/ })
  1102 /******/ ]);
  1093 
       
  1094 /******/ });