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