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