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