home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / js / media-views.js < prev    next >
Encoding:
JavaScript  |  2018-01-31  |  234.3 KB  |  9,196 lines

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