From 277e288c44845c5b7bacabadcc1e94681b7edb6d Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Tue, 25 May 2021 00:12:57 +0000 Subject: [PATCH] Build: Use deterministic module ids in webpack for media. Used `hashed` IDs rather the `named` IDs to reduce file size and remove references to non-existent paths in the final build. Follow-up for [50940,50984]. Props gziolo, peterwilsoncc. See #53192. Built from https://develop.svn.wordpress.org/trunk@50985 git-svn-id: http://core.svn.wordpress.org/trunk@50594 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/js/media-audiovideo.js | 1558 +-- wp-includes/js/media-audiovideo.min.js | 2 +- wp-includes/js/media-grid.js | 980 +- wp-includes/js/media-grid.min.js | 2 +- wp-includes/js/media-models.js | 958 +- wp-includes/js/media-models.min.js | 2 +- wp-includes/js/media-views.js | 15414 +++++++++++------------ wp-includes/js/media-views.min.js | 2 +- wp-includes/version.php | 2 +- 9 files changed, 9460 insertions(+), 9460 deletions(-) diff --git a/wp-includes/js/media-audiovideo.js b/wp-includes/js/media-audiovideo.js index f470295ee4..91f0b4d57f 100644 --- a/wp-includes/js/media-audiovideo.js +++ b/wp-includes/js/media-audiovideo.js @@ -86,7 +86,776 @@ /************************************************************************/ /******/ ({ -/***/ "./src/js/_enqueues/wp/media/audiovideo.js": +/***/ "+RYg": +/***/ (function(module, exports) { + +/** + * wp.media.model.PostMedia + * + * Shared model class for audio and video. Updates the model after + * "Add Audio|Video Source" and "Replace Audio|Video" states return + * + * @memberOf wp.media.model + * + * @class + * @augments Backbone.Model + */ +var PostMedia = Backbone.Model.extend(/** @lends wp.media.model.PostMedia.prototype */{ + initialize: function() { + this.attachment = false; + }, + + setSource: function( attachment ) { + this.attachment = attachment; + this.extension = attachment.get( 'filename' ).split('.').pop(); + + if ( this.get( 'src' ) && this.extension === this.get( 'src' ).split('.').pop() ) { + this.unset( 'src' ); + } + + if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) { + this.set( this.extension, this.attachment.get( 'url' ) ); + } else { + this.unset( this.extension ); + } + }, + + changeAttachment: function( attachment ) { + this.setSource( attachment ); + + this.unset( 'src' ); + _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function( ext ) { + this.unset( ext ); + }, this ); + } +}); + +module.exports = PostMedia; + + +/***/ }), + +/***/ "/4UI": +/***/ (function(module, exports) { + +/* global MediaElementPlayer */ +var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay, + $ = jQuery, + MediaDetails; + +/** + * wp.media.view.MediaDetails + * + * @memberOf wp.media.view + * + * @class + * @augments wp.media.view.Settings.AttachmentDisplay + * @augments wp.media.view.Settings + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + */ +MediaDetails = AttachmentDisplay.extend(/** @lends wp.media.view.MediaDetails.prototype */{ + initialize: function() { + _.bindAll(this, 'success'); + this.players = []; + this.listenTo( this.controller.states, 'close', wp.media.mixin.unsetPlayers ); + this.on( 'ready', this.setPlayer ); + this.on( 'media:setting:remove', wp.media.mixin.unsetPlayers, this ); + this.on( 'media:setting:remove', this.render ); + this.on( 'media:setting:remove', this.setPlayer ); + + AttachmentDisplay.prototype.initialize.apply( this, arguments ); + }, + + events: function(){ + return _.extend( { + 'click .remove-setting' : 'removeSetting', + 'change .content-track' : 'setTracks', + 'click .remove-track' : 'setTracks', + 'click .add-media-source' : 'addSource' + }, AttachmentDisplay.prototype.events ); + }, + + prepare: function() { + return _.defaults({ + model: this.model.toJSON() + }, this.options ); + }, + + /** + * Remove a setting's UI when the model unsets it + * + * @fires wp.media.view.MediaDetails#media:setting:remove + * + * @param {Event} e + */ + removeSetting : function(e) { + var wrap = $( e.currentTarget ).parent(), setting; + setting = wrap.find( 'input' ).data( 'setting' ); + + if ( setting ) { + this.model.unset( setting ); + this.trigger( 'media:setting:remove', this ); + } + + wrap.remove(); + }, + + /** + * + * @fires wp.media.view.MediaDetails#media:setting:remove + */ + setTracks : function() { + var tracks = ''; + + _.each( this.$('.content-track'), function(track) { + tracks += $( track ).val(); + } ); + + this.model.set( 'content', tracks ); + this.trigger( 'media:setting:remove', this ); + }, + + addSource : function( e ) { + this.controller.lastMime = $( e.currentTarget ).data( 'mime' ); + this.controller.setState( 'add-' + this.controller.defaults.id + '-source' ); + }, + + loadPlayer: function () { + this.players.push( new MediaElementPlayer( this.media, this.settings ) ); + this.scriptXhr = false; + }, + + setPlayer : function() { + var src; + + if ( this.players.length || ! this.media || this.scriptXhr ) { + return; + } + + src = this.model.get( 'src' ); + + if ( src && src.indexOf( 'vimeo' ) > -1 && ! ( 'Vimeo' in window ) ) { + this.scriptXhr = $.getScript( 'https://player.vimeo.com/api/player.js', _.bind( this.loadPlayer, this ) ); + } else { + this.loadPlayer(); + } + }, + + /** + * @abstract + */ + setMedia : function() { + return this; + }, + + success : function(mejs) { + var autoplay = mejs.attributes.autoplay && 'false' !== mejs.attributes.autoplay; + + if ( 'flash' === mejs.pluginType && autoplay ) { + mejs.addEventListener( 'canplay', function() { + mejs.play(); + }, false ); + } + + this.mejs = mejs; + }, + + /** + * @return {media.view.MediaDetails} Returns itself to allow chaining. + */ + render: function() { + AttachmentDisplay.prototype.render.apply( this, arguments ); + + setTimeout( _.bind( function() { + this.scrollToTop(); + }, this ), 10 ); + + this.settings = _.defaults( { + success : this.success + }, wp.media.mixin.mejsSettings ); + + return this.setMedia(); + }, + + scrollToTop: function() { + this.$( '.embed-media-settings' ).scrollTop( 0 ); + } +},/** @lends wp.media.view.MediaDetails */{ + instances : 0, + /** + * When multiple players in the DOM contain the same src, things get weird. + * + * @param {HTMLElement} elem + * @return {HTMLElement} + */ + prepareSrc : function( elem ) { + var i = MediaDetails.instances++; + _.each( $( elem ).find( 'source' ), function( source ) { + source.src = [ + source.src, + source.src.indexOf('?') > -1 ? '&' : '?', + '_=', + i + ].join(''); + } ); + + return elem; + } +}); + +module.exports = MediaDetails; + + +/***/ }), + +/***/ 0: +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__("pMD9"); + + +/***/ }), + +/***/ "6pp6": +/***/ (function(module, exports) { + +var State = wp.media.controller.State, + l10n = wp.media.view.l10n, + AudioDetails; + +/** + * wp.media.controller.AudioDetails + * + * The controller for the Audio Details state + * + * @memberOf wp.media.controller + * + * @class + * @augments wp.media.controller.State + * @augments Backbone.Model + */ +AudioDetails = State.extend(/** @lends wp.media.controller.AudioDetails.prototype */{ + defaults: { + id: 'audio-details', + toolbar: 'audio-details', + title: l10n.audioDetailsTitle, + content: 'audio-details', + menu: 'audio-details', + router: false, + priority: 60 + }, + + initialize: function( options ) { + this.media = options.media; + State.prototype.initialize.apply( this, arguments ); + } +}); + +module.exports = AudioDetails; + + +/***/ }), + +/***/ "Bdio": +/***/ (function(module, exports) { + +var MediaDetails = wp.media.view.MediaFrame.MediaDetails, + MediaLibrary = wp.media.controller.MediaLibrary, + + l10n = wp.media.view.l10n, + AudioDetails; + +/** + * wp.media.view.MediaFrame.AudioDetails + * + * @memberOf wp.media.view.MediaFrame + * + * @class + * @augments wp.media.view.MediaFrame.MediaDetails + * @augments wp.media.view.MediaFrame.Select + * @augments wp.media.view.MediaFrame + * @augments wp.media.view.Frame + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + * @mixes wp.media.controller.StateMachine + */ +AudioDetails = MediaDetails.extend(/** @lends wp.media.view.MediaFrame.AudioDetails.prototype */{ + defaults: { + id: 'audio', + url: '', + menu: 'audio-details', + content: 'audio-details', + toolbar: 'audio-details', + type: 'link', + title: l10n.audioDetailsTitle, + priority: 120 + }, + + initialize: function( options ) { + options.DetailsView = wp.media.view.AudioDetails; + options.cancelText = l10n.audioDetailsCancel; + options.addText = l10n.audioAddSourceTitle; + + MediaDetails.prototype.initialize.call( this, options ); + }, + + bindHandlers: function() { + MediaDetails.prototype.bindHandlers.apply( this, arguments ); + + this.on( 'toolbar:render:replace-audio', this.renderReplaceToolbar, this ); + this.on( 'toolbar:render:add-audio-source', this.renderAddSourceToolbar, this ); + }, + + createStates: function() { + this.states.add([ + new wp.media.controller.AudioDetails( { + media: this.media + } ), + + new MediaLibrary( { + type: 'audio', + id: 'replace-audio', + title: l10n.audioReplaceTitle, + toolbar: 'replace-audio', + media: this.media, + menu: 'audio-details' + } ), + + new MediaLibrary( { + type: 'audio', + id: 'add-audio-source', + title: l10n.audioAddSourceTitle, + toolbar: 'add-audio-source', + media: this.media, + menu: false + } ) + ]); + } +}); + +module.exports = AudioDetails; + + +/***/ }), + +/***/ "LX3m": +/***/ (function(module, exports) { + +var MediaDetails = wp.media.view.MediaDetails, + AudioDetails; + +/** + * wp.media.view.AudioDetails + * + * @memberOf wp.media.view + * + * @class + * @augments wp.media.view.MediaDetails + * @augments wp.media.view.Settings.AttachmentDisplay + * @augments wp.media.view.Settings + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + */ +AudioDetails = MediaDetails.extend(/** @lends wp.media.view.AudioDetails.prototype */{ + className: 'audio-details', + template: wp.template('audio-details'), + + setMedia: function() { + var audio = this.$('.wp-audio-shortcode'); + + if ( audio.find( 'source' ).length ) { + if ( audio.is(':hidden') ) { + audio.show(); + } + this.media = MediaDetails.prepareSrc( audio.get(0) ); + } else { + audio.hide(); + this.media = false; + } + + return this; + } +}); + +module.exports = AudioDetails; + + +/***/ }), + +/***/ "MT9K": +/***/ (function(module, exports) { + +var MediaDetails = wp.media.view.MediaDetails, + VideoDetails; + +/** + * wp.media.view.VideoDetails + * + * @memberOf wp.media.view + * + * @class + * @augments wp.media.view.MediaDetails + * @augments wp.media.view.Settings.AttachmentDisplay + * @augments wp.media.view.Settings + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + */ +VideoDetails = MediaDetails.extend(/** @lends wp.media.view.VideoDetails.prototype */{ + className: 'video-details', + template: wp.template('video-details'), + + setMedia: function() { + var video = this.$('.wp-video-shortcode'); + + if ( video.find( 'source' ).length ) { + if ( video.is(':hidden') ) { + video.show(); + } + + if ( ! video.hasClass( 'youtube-video' ) && ! video.hasClass( 'vimeo-video' ) ) { + this.media = MediaDetails.prepareSrc( video.get(0) ); + } else { + this.media = video.get(0); + } + } else { + video.hide(); + this.media = false; + } + + return this; + } +}); + +module.exports = VideoDetails; + + +/***/ }), + +/***/ "RQe2": +/***/ (function(module, exports) { + +var Select = wp.media.view.MediaFrame.Select, + l10n = wp.media.view.l10n, + MediaDetails; + +/** + * wp.media.view.MediaFrame.MediaDetails + * + * @memberOf wp.media.view.MediaFrame + * + * @class + * @augments wp.media.view.MediaFrame.Select + * @augments wp.media.view.MediaFrame + * @augments wp.media.view.Frame + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + * @mixes wp.media.controller.StateMachine + */ +MediaDetails = Select.extend(/** @lends wp.media.view.MediaFrame.MediaDetails.prototype */{ + defaults: { + id: 'media', + url: '', + menu: 'media-details', + content: 'media-details', + toolbar: 'media-details', + type: 'link', + priority: 120 + }, + + initialize: function( options ) { + this.DetailsView = options.DetailsView; + this.cancelText = options.cancelText; + this.addText = options.addText; + + this.media = new wp.media.model.PostMedia( options.metadata ); + this.options.selection = new wp.media.model.Selection( this.media.attachment, { multiple: false } ); + Select.prototype.initialize.apply( this, arguments ); + }, + + bindHandlers: function() { + var menu = this.defaults.menu; + + Select.prototype.bindHandlers.apply( this, arguments ); + + this.on( 'menu:create:' + menu, this.createMenu, this ); + this.on( 'content:render:' + menu, this.renderDetailsContent, this ); + this.on( 'menu:render:' + menu, this.renderMenu, this ); + this.on( 'toolbar:render:' + menu, this.renderDetailsToolbar, this ); + }, + + renderDetailsContent: function() { + var view = new this.DetailsView({ + controller: this, + model: this.state().media, + attachment: this.state().media.attachment + }).render(); + + this.content.set( view ); + }, + + renderMenu: function( view ) { + var lastState = this.lastState(), + previous = lastState && lastState.id, + frame = this; + + view.set({ + cancel: { + text: this.cancelText, + priority: 20, + click: function() { + if ( previous ) { + frame.setState( previous ); + } else { + frame.close(); + } + } + }, + separateCancel: new wp.media.View({ + className: 'separator', + priority: 40 + }) + }); + + }, + + setPrimaryButton: function(text, handler) { + this.toolbar.set( new wp.media.view.Toolbar({ + controller: this, + items: { + button: { + style: 'primary', + text: text, + priority: 80, + click: function() { + var controller = this.controller; + handler.call( this, controller, controller.state() ); + // Restore and reset the default state. + controller.setState( controller.options.state ); + controller.reset(); + } + } + } + }) ); + }, + + renderDetailsToolbar: function() { + this.setPrimaryButton( l10n.update, function( controller, state ) { + controller.close(); + state.trigger( 'update', controller.media.toJSON() ); + } ); + }, + + renderReplaceToolbar: function() { + this.setPrimaryButton( l10n.replace, function( controller, state ) { + var attachment = state.get( 'selection' ).single(); + controller.media.changeAttachment( attachment ); + state.trigger( 'replace', controller.media.toJSON() ); + } ); + }, + + renderAddSourceToolbar: function() { + this.setPrimaryButton( this.addText, function( controller, state ) { + var attachment = state.get( 'selection' ).single(); + controller.media.setSource( attachment ); + state.trigger( 'add-source', controller.media.toJSON() ); + } ); + } +}); + +module.exports = MediaDetails; + + +/***/ }), + +/***/ "Xcj4": +/***/ (function(module, exports) { + +/** + * wp.media.controller.VideoDetails + * + * The controller for the Video Details state + * + * @memberOf wp.media.controller + * + * @class + * @augments wp.media.controller.State + * @augments Backbone.Model + */ +var State = wp.media.controller.State, + l10n = wp.media.view.l10n, + VideoDetails; + +VideoDetails = State.extend(/** @lends wp.media.controller.VideoDetails.prototype */{ + defaults: { + id: 'video-details', + toolbar: 'video-details', + title: l10n.videoDetailsTitle, + content: 'video-details', + menu: 'video-details', + router: false, + priority: 60 + }, + + initialize: function( options ) { + this.media = options.media; + State.prototype.initialize.apply( this, arguments ); + } +}); + +module.exports = VideoDetails; + + +/***/ }), + +/***/ "m85o": +/***/ (function(module, exports) { + +var MediaDetails = wp.media.view.MediaFrame.MediaDetails, + MediaLibrary = wp.media.controller.MediaLibrary, + l10n = wp.media.view.l10n, + VideoDetails; + +/** + * wp.media.view.MediaFrame.VideoDetails + * + * @memberOf wp.media.view.MediaFrame + * + * @class + * @augments wp.media.view.MediaFrame.MediaDetails + * @augments wp.media.view.MediaFrame.Select + * @augments wp.media.view.MediaFrame + * @augments wp.media.view.Frame + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + * @mixes wp.media.controller.StateMachine + */ +VideoDetails = MediaDetails.extend(/** @lends wp.media.view.MediaFrame.VideoDetails.prototype */{ + defaults: { + id: 'video', + url: '', + menu: 'video-details', + content: 'video-details', + toolbar: 'video-details', + type: 'link', + title: l10n.videoDetailsTitle, + priority: 120 + }, + + initialize: function( options ) { + options.DetailsView = wp.media.view.VideoDetails; + options.cancelText = l10n.videoDetailsCancel; + options.addText = l10n.videoAddSourceTitle; + + MediaDetails.prototype.initialize.call( this, options ); + }, + + bindHandlers: function() { + MediaDetails.prototype.bindHandlers.apply( this, arguments ); + + this.on( 'toolbar:render:replace-video', this.renderReplaceToolbar, this ); + this.on( 'toolbar:render:add-video-source', this.renderAddSourceToolbar, this ); + this.on( 'toolbar:render:select-poster-image', this.renderSelectPosterImageToolbar, this ); + this.on( 'toolbar:render:add-track', this.renderAddTrackToolbar, this ); + }, + + createStates: function() { + this.states.add([ + new wp.media.controller.VideoDetails({ + media: this.media + }), + + new MediaLibrary( { + type: 'video', + id: 'replace-video', + title: l10n.videoReplaceTitle, + toolbar: 'replace-video', + media: this.media, + menu: 'video-details' + } ), + + new MediaLibrary( { + type: 'video', + id: 'add-video-source', + title: l10n.videoAddSourceTitle, + toolbar: 'add-video-source', + media: this.media, + menu: false + } ), + + new MediaLibrary( { + type: 'image', + id: 'select-poster-image', + title: l10n.videoSelectPosterImageTitle, + toolbar: 'select-poster-image', + media: this.media, + menu: 'video-details' + } ), + + new MediaLibrary( { + type: 'text', + id: 'add-track', + title: l10n.videoAddTrackTitle, + toolbar: 'add-track', + media: this.media, + menu: 'video-details' + } ) + ]); + }, + + renderSelectPosterImageToolbar: function() { + this.setPrimaryButton( l10n.videoSelectPosterImageTitle, function( controller, state ) { + var urls = [], attachment = state.get( 'selection' ).single(); + + controller.media.set( 'poster', attachment.get( 'url' ) ); + state.trigger( 'set-poster-image', controller.media.toJSON() ); + + _.each( wp.media.view.settings.embedExts, function (ext) { + if ( controller.media.get( ext ) ) { + urls.push( controller.media.get( ext ) ); + } + } ); + + wp.ajax.send( 'set-attachment-thumbnail', { + data : { + urls: urls, + thumbnail_id: attachment.get( 'id' ) + } + } ); + } ); + }, + + renderAddTrackToolbar: function() { + this.setPrimaryButton( l10n.videoAddTrackTitle, function( controller, state ) { + var attachment = state.get( 'selection' ).single(), + content = controller.media.get( 'content' ); + + if ( -1 === content.indexOf( attachment.get( 'url' ) ) ) { + content += [ + '' + ].join(''); + + controller.media.set( 'content', content ); + } + state.trigger( 'add-track', controller.media.toJSON() ); + } ); + } +}); + +module.exports = VideoDetails; + + +/***/ }), + +/***/ "pMD9": /***/ (function(module, exports, __webpack_require__) { /** @@ -364,784 +1133,15 @@ wp.media.video = { } }; -media.model.PostMedia = __webpack_require__( "./src/js/media/models/post-media.js" ); -media.controller.AudioDetails = __webpack_require__( "./src/js/media/controllers/audio-details.js" ); -media.controller.VideoDetails = __webpack_require__( "./src/js/media/controllers/video-details.js" ); -media.view.MediaFrame.MediaDetails = __webpack_require__( "./src/js/media/views/frame/media-details.js" ); -media.view.MediaFrame.AudioDetails = __webpack_require__( "./src/js/media/views/frame/audio-details.js" ); -media.view.MediaFrame.VideoDetails = __webpack_require__( "./src/js/media/views/frame/video-details.js" ); -media.view.MediaDetails = __webpack_require__( "./src/js/media/views/media-details.js" ); -media.view.AudioDetails = __webpack_require__( "./src/js/media/views/audio-details.js" ); -media.view.VideoDetails = __webpack_require__( "./src/js/media/views/video-details.js" ); - - -/***/ }), - -/***/ "./src/js/media/controllers/audio-details.js": -/***/ (function(module, exports) { - -var State = wp.media.controller.State, - l10n = wp.media.view.l10n, - AudioDetails; - -/** - * wp.media.controller.AudioDetails - * - * The controller for the Audio Details state - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.State - * @augments Backbone.Model - */ -AudioDetails = State.extend(/** @lends wp.media.controller.AudioDetails.prototype */{ - defaults: { - id: 'audio-details', - toolbar: 'audio-details', - title: l10n.audioDetailsTitle, - content: 'audio-details', - menu: 'audio-details', - router: false, - priority: 60 - }, - - initialize: function( options ) { - this.media = options.media; - State.prototype.initialize.apply( this, arguments ); - } -}); - -module.exports = AudioDetails; - - -/***/ }), - -/***/ "./src/js/media/controllers/video-details.js": -/***/ (function(module, exports) { - -/** - * wp.media.controller.VideoDetails - * - * The controller for the Video Details state - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.State - * @augments Backbone.Model - */ -var State = wp.media.controller.State, - l10n = wp.media.view.l10n, - VideoDetails; - -VideoDetails = State.extend(/** @lends wp.media.controller.VideoDetails.prototype */{ - defaults: { - id: 'video-details', - toolbar: 'video-details', - title: l10n.videoDetailsTitle, - content: 'video-details', - menu: 'video-details', - router: false, - priority: 60 - }, - - initialize: function( options ) { - this.media = options.media; - State.prototype.initialize.apply( this, arguments ); - } -}); - -module.exports = VideoDetails; - - -/***/ }), - -/***/ "./src/js/media/models/post-media.js": -/***/ (function(module, exports) { - -/** - * wp.media.model.PostMedia - * - * Shared model class for audio and video. Updates the model after - * "Add Audio|Video Source" and "Replace Audio|Video" states return - * - * @memberOf wp.media.model - * - * @class - * @augments Backbone.Model - */ -var PostMedia = Backbone.Model.extend(/** @lends wp.media.model.PostMedia.prototype */{ - initialize: function() { - this.attachment = false; - }, - - setSource: function( attachment ) { - this.attachment = attachment; - this.extension = attachment.get( 'filename' ).split('.').pop(); - - if ( this.get( 'src' ) && this.extension === this.get( 'src' ).split('.').pop() ) { - this.unset( 'src' ); - } - - if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) { - this.set( this.extension, this.attachment.get( 'url' ) ); - } else { - this.unset( this.extension ); - } - }, - - changeAttachment: function( attachment ) { - this.setSource( attachment ); - - this.unset( 'src' ); - _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function( ext ) { - this.unset( ext ); - }, this ); - } -}); - -module.exports = PostMedia; - - -/***/ }), - -/***/ "./src/js/media/views/audio-details.js": -/***/ (function(module, exports) { - -var MediaDetails = wp.media.view.MediaDetails, - AudioDetails; - -/** - * wp.media.view.AudioDetails - * - * @memberOf wp.media.view - * - * @class - * @augments wp.media.view.MediaDetails - * @augments wp.media.view.Settings.AttachmentDisplay - * @augments wp.media.view.Settings - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -AudioDetails = MediaDetails.extend(/** @lends wp.media.view.AudioDetails.prototype */{ - className: 'audio-details', - template: wp.template('audio-details'), - - setMedia: function() { - var audio = this.$('.wp-audio-shortcode'); - - if ( audio.find( 'source' ).length ) { - if ( audio.is(':hidden') ) { - audio.show(); - } - this.media = MediaDetails.prepareSrc( audio.get(0) ); - } else { - audio.hide(); - this.media = false; - } - - return this; - } -}); - -module.exports = AudioDetails; - - -/***/ }), - -/***/ "./src/js/media/views/frame/audio-details.js": -/***/ (function(module, exports) { - -var MediaDetails = wp.media.view.MediaFrame.MediaDetails, - MediaLibrary = wp.media.controller.MediaLibrary, - - l10n = wp.media.view.l10n, - AudioDetails; - -/** - * wp.media.view.MediaFrame.AudioDetails - * - * @memberOf wp.media.view.MediaFrame - * - * @class - * @augments wp.media.view.MediaFrame.MediaDetails - * @augments wp.media.view.MediaFrame.Select - * @augments wp.media.view.MediaFrame - * @augments wp.media.view.Frame - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - * @mixes wp.media.controller.StateMachine - */ -AudioDetails = MediaDetails.extend(/** @lends wp.media.view.MediaFrame.AudioDetails.prototype */{ - defaults: { - id: 'audio', - url: '', - menu: 'audio-details', - content: 'audio-details', - toolbar: 'audio-details', - type: 'link', - title: l10n.audioDetailsTitle, - priority: 120 - }, - - initialize: function( options ) { - options.DetailsView = wp.media.view.AudioDetails; - options.cancelText = l10n.audioDetailsCancel; - options.addText = l10n.audioAddSourceTitle; - - MediaDetails.prototype.initialize.call( this, options ); - }, - - bindHandlers: function() { - MediaDetails.prototype.bindHandlers.apply( this, arguments ); - - this.on( 'toolbar:render:replace-audio', this.renderReplaceToolbar, this ); - this.on( 'toolbar:render:add-audio-source', this.renderAddSourceToolbar, this ); - }, - - createStates: function() { - this.states.add([ - new wp.media.controller.AudioDetails( { - media: this.media - } ), - - new MediaLibrary( { - type: 'audio', - id: 'replace-audio', - title: l10n.audioReplaceTitle, - toolbar: 'replace-audio', - media: this.media, - menu: 'audio-details' - } ), - - new MediaLibrary( { - type: 'audio', - id: 'add-audio-source', - title: l10n.audioAddSourceTitle, - toolbar: 'add-audio-source', - media: this.media, - menu: false - } ) - ]); - } -}); - -module.exports = AudioDetails; - - -/***/ }), - -/***/ "./src/js/media/views/frame/media-details.js": -/***/ (function(module, exports) { - -var Select = wp.media.view.MediaFrame.Select, - l10n = wp.media.view.l10n, - MediaDetails; - -/** - * wp.media.view.MediaFrame.MediaDetails - * - * @memberOf wp.media.view.MediaFrame - * - * @class - * @augments wp.media.view.MediaFrame.Select - * @augments wp.media.view.MediaFrame - * @augments wp.media.view.Frame - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - * @mixes wp.media.controller.StateMachine - */ -MediaDetails = Select.extend(/** @lends wp.media.view.MediaFrame.MediaDetails.prototype */{ - defaults: { - id: 'media', - url: '', - menu: 'media-details', - content: 'media-details', - toolbar: 'media-details', - type: 'link', - priority: 120 - }, - - initialize: function( options ) { - this.DetailsView = options.DetailsView; - this.cancelText = options.cancelText; - this.addText = options.addText; - - this.media = new wp.media.model.PostMedia( options.metadata ); - this.options.selection = new wp.media.model.Selection( this.media.attachment, { multiple: false } ); - Select.prototype.initialize.apply( this, arguments ); - }, - - bindHandlers: function() { - var menu = this.defaults.menu; - - Select.prototype.bindHandlers.apply( this, arguments ); - - this.on( 'menu:create:' + menu, this.createMenu, this ); - this.on( 'content:render:' + menu, this.renderDetailsContent, this ); - this.on( 'menu:render:' + menu, this.renderMenu, this ); - this.on( 'toolbar:render:' + menu, this.renderDetailsToolbar, this ); - }, - - renderDetailsContent: function() { - var view = new this.DetailsView({ - controller: this, - model: this.state().media, - attachment: this.state().media.attachment - }).render(); - - this.content.set( view ); - }, - - renderMenu: function( view ) { - var lastState = this.lastState(), - previous = lastState && lastState.id, - frame = this; - - view.set({ - cancel: { - text: this.cancelText, - priority: 20, - click: function() { - if ( previous ) { - frame.setState( previous ); - } else { - frame.close(); - } - } - }, - separateCancel: new wp.media.View({ - className: 'separator', - priority: 40 - }) - }); - - }, - - setPrimaryButton: function(text, handler) { - this.toolbar.set( new wp.media.view.Toolbar({ - controller: this, - items: { - button: { - style: 'primary', - text: text, - priority: 80, - click: function() { - var controller = this.controller; - handler.call( this, controller, controller.state() ); - // Restore and reset the default state. - controller.setState( controller.options.state ); - controller.reset(); - } - } - } - }) ); - }, - - renderDetailsToolbar: function() { - this.setPrimaryButton( l10n.update, function( controller, state ) { - controller.close(); - state.trigger( 'update', controller.media.toJSON() ); - } ); - }, - - renderReplaceToolbar: function() { - this.setPrimaryButton( l10n.replace, function( controller, state ) { - var attachment = state.get( 'selection' ).single(); - controller.media.changeAttachment( attachment ); - state.trigger( 'replace', controller.media.toJSON() ); - } ); - }, - - renderAddSourceToolbar: function() { - this.setPrimaryButton( this.addText, function( controller, state ) { - var attachment = state.get( 'selection' ).single(); - controller.media.setSource( attachment ); - state.trigger( 'add-source', controller.media.toJSON() ); - } ); - } -}); - -module.exports = MediaDetails; - - -/***/ }), - -/***/ "./src/js/media/views/frame/video-details.js": -/***/ (function(module, exports) { - -var MediaDetails = wp.media.view.MediaFrame.MediaDetails, - MediaLibrary = wp.media.controller.MediaLibrary, - l10n = wp.media.view.l10n, - VideoDetails; - -/** - * wp.media.view.MediaFrame.VideoDetails - * - * @memberOf wp.media.view.MediaFrame - * - * @class - * @augments wp.media.view.MediaFrame.MediaDetails - * @augments wp.media.view.MediaFrame.Select - * @augments wp.media.view.MediaFrame - * @augments wp.media.view.Frame - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - * @mixes wp.media.controller.StateMachine - */ -VideoDetails = MediaDetails.extend(/** @lends wp.media.view.MediaFrame.VideoDetails.prototype */{ - defaults: { - id: 'video', - url: '', - menu: 'video-details', - content: 'video-details', - toolbar: 'video-details', - type: 'link', - title: l10n.videoDetailsTitle, - priority: 120 - }, - - initialize: function( options ) { - options.DetailsView = wp.media.view.VideoDetails; - options.cancelText = l10n.videoDetailsCancel; - options.addText = l10n.videoAddSourceTitle; - - MediaDetails.prototype.initialize.call( this, options ); - }, - - bindHandlers: function() { - MediaDetails.prototype.bindHandlers.apply( this, arguments ); - - this.on( 'toolbar:render:replace-video', this.renderReplaceToolbar, this ); - this.on( 'toolbar:render:add-video-source', this.renderAddSourceToolbar, this ); - this.on( 'toolbar:render:select-poster-image', this.renderSelectPosterImageToolbar, this ); - this.on( 'toolbar:render:add-track', this.renderAddTrackToolbar, this ); - }, - - createStates: function() { - this.states.add([ - new wp.media.controller.VideoDetails({ - media: this.media - }), - - new MediaLibrary( { - type: 'video', - id: 'replace-video', - title: l10n.videoReplaceTitle, - toolbar: 'replace-video', - media: this.media, - menu: 'video-details' - } ), - - new MediaLibrary( { - type: 'video', - id: 'add-video-source', - title: l10n.videoAddSourceTitle, - toolbar: 'add-video-source', - media: this.media, - menu: false - } ), - - new MediaLibrary( { - type: 'image', - id: 'select-poster-image', - title: l10n.videoSelectPosterImageTitle, - toolbar: 'select-poster-image', - media: this.media, - menu: 'video-details' - } ), - - new MediaLibrary( { - type: 'text', - id: 'add-track', - title: l10n.videoAddTrackTitle, - toolbar: 'add-track', - media: this.media, - menu: 'video-details' - } ) - ]); - }, - - renderSelectPosterImageToolbar: function() { - this.setPrimaryButton( l10n.videoSelectPosterImageTitle, function( controller, state ) { - var urls = [], attachment = state.get( 'selection' ).single(); - - controller.media.set( 'poster', attachment.get( 'url' ) ); - state.trigger( 'set-poster-image', controller.media.toJSON() ); - - _.each( wp.media.view.settings.embedExts, function (ext) { - if ( controller.media.get( ext ) ) { - urls.push( controller.media.get( ext ) ); - } - } ); - - wp.ajax.send( 'set-attachment-thumbnail', { - data : { - urls: urls, - thumbnail_id: attachment.get( 'id' ) - } - } ); - } ); - }, - - renderAddTrackToolbar: function() { - this.setPrimaryButton( l10n.videoAddTrackTitle, function( controller, state ) { - var attachment = state.get( 'selection' ).single(), - content = controller.media.get( 'content' ); - - if ( -1 === content.indexOf( attachment.get( 'url' ) ) ) { - content += [ - '' - ].join(''); - - controller.media.set( 'content', content ); - } - state.trigger( 'add-track', controller.media.toJSON() ); - } ); - } -}); - -module.exports = VideoDetails; - - -/***/ }), - -/***/ "./src/js/media/views/media-details.js": -/***/ (function(module, exports) { - -/* global MediaElementPlayer */ -var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay, - $ = jQuery, - MediaDetails; - -/** - * wp.media.view.MediaDetails - * - * @memberOf wp.media.view - * - * @class - * @augments wp.media.view.Settings.AttachmentDisplay - * @augments wp.media.view.Settings - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -MediaDetails = AttachmentDisplay.extend(/** @lends wp.media.view.MediaDetails.prototype */{ - initialize: function() { - _.bindAll(this, 'success'); - this.players = []; - this.listenTo( this.controller.states, 'close', wp.media.mixin.unsetPlayers ); - this.on( 'ready', this.setPlayer ); - this.on( 'media:setting:remove', wp.media.mixin.unsetPlayers, this ); - this.on( 'media:setting:remove', this.render ); - this.on( 'media:setting:remove', this.setPlayer ); - - AttachmentDisplay.prototype.initialize.apply( this, arguments ); - }, - - events: function(){ - return _.extend( { - 'click .remove-setting' : 'removeSetting', - 'change .content-track' : 'setTracks', - 'click .remove-track' : 'setTracks', - 'click .add-media-source' : 'addSource' - }, AttachmentDisplay.prototype.events ); - }, - - prepare: function() { - return _.defaults({ - model: this.model.toJSON() - }, this.options ); - }, - - /** - * Remove a setting's UI when the model unsets it - * - * @fires wp.media.view.MediaDetails#media:setting:remove - * - * @param {Event} e - */ - removeSetting : function(e) { - var wrap = $( e.currentTarget ).parent(), setting; - setting = wrap.find( 'input' ).data( 'setting' ); - - if ( setting ) { - this.model.unset( setting ); - this.trigger( 'media:setting:remove', this ); - } - - wrap.remove(); - }, - - /** - * - * @fires wp.media.view.MediaDetails#media:setting:remove - */ - setTracks : function() { - var tracks = ''; - - _.each( this.$('.content-track'), function(track) { - tracks += $( track ).val(); - } ); - - this.model.set( 'content', tracks ); - this.trigger( 'media:setting:remove', this ); - }, - - addSource : function( e ) { - this.controller.lastMime = $( e.currentTarget ).data( 'mime' ); - this.controller.setState( 'add-' + this.controller.defaults.id + '-source' ); - }, - - loadPlayer: function () { - this.players.push( new MediaElementPlayer( this.media, this.settings ) ); - this.scriptXhr = false; - }, - - setPlayer : function() { - var src; - - if ( this.players.length || ! this.media || this.scriptXhr ) { - return; - } - - src = this.model.get( 'src' ); - - if ( src && src.indexOf( 'vimeo' ) > -1 && ! ( 'Vimeo' in window ) ) { - this.scriptXhr = $.getScript( 'https://player.vimeo.com/api/player.js', _.bind( this.loadPlayer, this ) ); - } else { - this.loadPlayer(); - } - }, - - /** - * @abstract - */ - setMedia : function() { - return this; - }, - - success : function(mejs) { - var autoplay = mejs.attributes.autoplay && 'false' !== mejs.attributes.autoplay; - - if ( 'flash' === mejs.pluginType && autoplay ) { - mejs.addEventListener( 'canplay', function() { - mejs.play(); - }, false ); - } - - this.mejs = mejs; - }, - - /** - * @return {media.view.MediaDetails} Returns itself to allow chaining. - */ - render: function() { - AttachmentDisplay.prototype.render.apply( this, arguments ); - - setTimeout( _.bind( function() { - this.scrollToTop(); - }, this ), 10 ); - - this.settings = _.defaults( { - success : this.success - }, wp.media.mixin.mejsSettings ); - - return this.setMedia(); - }, - - scrollToTop: function() { - this.$( '.embed-media-settings' ).scrollTop( 0 ); - } -},/** @lends wp.media.view.MediaDetails */{ - instances : 0, - /** - * When multiple players in the DOM contain the same src, things get weird. - * - * @param {HTMLElement} elem - * @return {HTMLElement} - */ - prepareSrc : function( elem ) { - var i = MediaDetails.instances++; - _.each( $( elem ).find( 'source' ), function( source ) { - source.src = [ - source.src, - source.src.indexOf('?') > -1 ? '&' : '?', - '_=', - i - ].join(''); - } ); - - return elem; - } -}); - -module.exports = MediaDetails; - - -/***/ }), - -/***/ "./src/js/media/views/video-details.js": -/***/ (function(module, exports) { - -var MediaDetails = wp.media.view.MediaDetails, - VideoDetails; - -/** - * wp.media.view.VideoDetails - * - * @memberOf wp.media.view - * - * @class - * @augments wp.media.view.MediaDetails - * @augments wp.media.view.Settings.AttachmentDisplay - * @augments wp.media.view.Settings - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -VideoDetails = MediaDetails.extend(/** @lends wp.media.view.VideoDetails.prototype */{ - className: 'video-details', - template: wp.template('video-details'), - - setMedia: function() { - var video = this.$('.wp-video-shortcode'); - - if ( video.find( 'source' ).length ) { - if ( video.is(':hidden') ) { - video.show(); - } - - if ( ! video.hasClass( 'youtube-video' ) && ! video.hasClass( 'vimeo-video' ) ) { - this.media = MediaDetails.prepareSrc( video.get(0) ); - } else { - this.media = video.get(0); - } - } else { - video.hide(); - this.media = false; - } - - return this; - } -}); - -module.exports = VideoDetails; - - -/***/ }), - -/***/ 0: -/***/ (function(module, exports, __webpack_require__) { - -module.exports = __webpack_require__("./src/js/_enqueues/wp/media/audiovideo.js"); +media.model.PostMedia = __webpack_require__( "+RYg" ); +media.controller.AudioDetails = __webpack_require__( "6pp6" ); +media.controller.VideoDetails = __webpack_require__( "Xcj4" ); +media.view.MediaFrame.MediaDetails = __webpack_require__( "RQe2" ); +media.view.MediaFrame.AudioDetails = __webpack_require__( "Bdio" ); +media.view.MediaFrame.VideoDetails = __webpack_require__( "m85o" ); +media.view.MediaDetails = __webpack_require__( "/4UI" ); +media.view.AudioDetails = __webpack_require__( "LX3m" ); +media.view.VideoDetails = __webpack_require__( "MT9K" ); /***/ }) diff --git a/wp-includes/js/media-audiovideo.min.js b/wp-includes/js/media-audiovideo.min.js index 2fb5ca3508..9d9fd3aeb4 100644 --- a/wp-includes/js/media-audiovideo.min.js +++ b/wp-includes/js/media-audiovideo.min.js @@ -1,2 +1,2 @@ /*! This file is auto-generated */ -!function(i){var a={};function s(e){if(a[e])return a[e].exports;var t=a[e]={i:e,l:!1,exports:{}};return i[e].call(t.exports,t,t.exports,s),t.l=!0,t.exports}s.m=i,s.c=a,s.d=function(e,t,i){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(s.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)s.d(i,a,function(e){return t[e]}.bind(null,a));return i},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}({"./src/js/_enqueues/wp/media/audiovideo.js":function(e,t,i){var a=wp.media,s=window._wpmejsSettings||{},o=window._wpMediaViewsL10n||{};wp.media.mixin={mejsSettings:s,removeAllPlayers:function(){if(window.mejs&&window.mejs.players)for(var e in window.mejs.players)window.mejs.players[e].pause(),this.removePlayer(window.mejs.players[e])},removePlayer:function(e){var t,i;if(e.options){for(t in e.options.features)if(e["clean"+(i=e.options.features[t])])try{e["clean"+i](e)}catch(e){}e.isDynamic||e.node.remove(),"html5"!==e.media.rendererName&&e.media.remove(),delete window.mejs.players[e.id],e.container.remove(),e.globalUnbind("resize",e.globalResizeCallback),e.globalUnbind("keydown",e.globalKeydownCallback),e.globalUnbind("click",e.globalClickCallback),delete e.media.player}},unsetPlayers:function(){this.players&&this.players.length&&(_.each(this.players,function(e){e.pause(),wp.media.mixin.removePlayer(e)}),this.players=[])}},wp.media.playlist=new wp.media.collection({tag:"playlist",editTitle:o.editPlaylistTitle,defaults:{id:wp.media.view.settings.post.id,style:"light",tracklist:!0,tracknumbers:!0,images:!0,artists:!0,type:"audio"}}),wp.media.audio={coerce:wp.media.coerce,defaults:{id:wp.media.view.settings.post.id,src:"",loop:!1,autoplay:!1,preload:"none",width:400},edit:function(e){e=wp.shortcode.next("audio",e).shortcode;return wp.media({frame:"audio",state:"audio-details",metadata:_.defaults(e.attrs.named,this.defaults)})},shortcode:function(i){var e;return _.each(this.defaults,function(e,t){i[t]=this.coerce(i,t),e===i[t]&&delete i[t]},this),e=i.content,delete i.content,new wp.shortcode({tag:"audio",attrs:i,content:e})}},wp.media.video={coerce:wp.media.coerce,defaults:{id:wp.media.view.settings.post.id,src:"",poster:"",loop:!1,autoplay:!1,preload:"metadata",content:"",width:640,height:360},edit:function(e){var t=wp.shortcode.next("video",e).shortcode,e=t.attrs.named;return e.content=t.content,wp.media({frame:"video",state:"video-details",metadata:_.defaults(e,this.defaults)})},shortcode:function(i){var e;return _.each(this.defaults,function(e,t){i[t]=this.coerce(i,t),e===i[t]&&delete i[t]},this),e=i.content,delete i.content,new wp.shortcode({tag:"video",attrs:i,content:e})}},a.model.PostMedia=i("./src/js/media/models/post-media.js"),a.controller.AudioDetails=i("./src/js/media/controllers/audio-details.js"),a.controller.VideoDetails=i("./src/js/media/controllers/video-details.js"),a.view.MediaFrame.MediaDetails=i("./src/js/media/views/frame/media-details.js"),a.view.MediaFrame.AudioDetails=i("./src/js/media/views/frame/audio-details.js"),a.view.MediaFrame.VideoDetails=i("./src/js/media/views/frame/video-details.js"),a.view.MediaDetails=i("./src/js/media/views/media-details.js"),a.view.AudioDetails=i("./src/js/media/views/audio-details.js"),a.view.VideoDetails=i("./src/js/media/views/video-details.js")},"./src/js/media/controllers/audio-details.js":function(e,t){var i=wp.media.controller.State,a=wp.media.view.l10n,a=i.extend({defaults:{id:"audio-details",toolbar:"audio-details",title:a.audioDetailsTitle,content:"audio-details",menu:"audio-details",router:!1,priority:60},initialize:function(e){this.media=e.media,i.prototype.initialize.apply(this,arguments)}});e.exports=a},"./src/js/media/controllers/video-details.js":function(e,t){var i=wp.media.controller.State,a=wp.media.view.l10n,a=i.extend({defaults:{id:"video-details",toolbar:"video-details",title:a.videoDetailsTitle,content:"video-details",menu:"video-details",router:!1,priority:60},initialize:function(e){this.media=e.media,i.prototype.initialize.apply(this,arguments)}});e.exports=a},"./src/js/media/models/post-media.js":function(e,t){var i=Backbone.Model.extend({initialize:function(){this.attachment=!1},setSource:function(e){this.attachment=e,this.extension=e.get("filename").split(".").pop(),this.get("src")&&this.extension===this.get("src").split(".").pop()&&this.unset("src"),_.contains(wp.media.view.settings.embedExts,this.extension)?this.set(this.extension,this.attachment.get("url")):this.unset(this.extension)},changeAttachment:function(e){this.setSource(e),this.unset("src"),_.each(_.without(wp.media.view.settings.embedExts,this.extension),function(e){this.unset(e)},this)}});e.exports=i},"./src/js/media/views/audio-details.js":function(e,t){var i=wp.media.view.MediaDetails,a=i.extend({className:"audio-details",template:wp.template("audio-details"),setMedia:function(){var e=this.$(".wp-audio-shortcode");return e.find("source").length?(e.is(":hidden")&&e.show(),this.media=i.prepareSrc(e.get(0))):(e.hide(),this.media=!1),this}});e.exports=a},"./src/js/media/views/frame/audio-details.js":function(e,t){var i=wp.media.view.MediaFrame.MediaDetails,a=wp.media.controller.MediaLibrary,s=wp.media.view.l10n,o=i.extend({defaults:{id:"audio",url:"",menu:"audio-details",content:"audio-details",toolbar:"audio-details",type:"link",title:s.audioDetailsTitle,priority:120},initialize:function(e){e.DetailsView=wp.media.view.AudioDetails,e.cancelText=s.audioDetailsCancel,e.addText=s.audioAddSourceTitle,i.prototype.initialize.call(this,e)},bindHandlers:function(){i.prototype.bindHandlers.apply(this,arguments),this.on("toolbar:render:replace-audio",this.renderReplaceToolbar,this),this.on("toolbar:render:add-audio-source",this.renderAddSourceToolbar,this)},createStates:function(){this.states.add([new wp.media.controller.AudioDetails({media:this.media}),new a({type:"audio",id:"replace-audio",title:s.audioReplaceTitle,toolbar:"replace-audio",media:this.media,menu:"audio-details"}),new a({type:"audio",id:"add-audio-source",title:s.audioAddSourceTitle,toolbar:"add-audio-source",media:this.media,menu:!1})])}});e.exports=o},"./src/js/media/views/frame/media-details.js":function(e,t){var i=wp.media.view.MediaFrame.Select,a=wp.media.view.l10n,s=i.extend({defaults:{id:"media",url:"",menu:"media-details",content:"media-details",toolbar:"media-details",type:"link",priority:120},initialize:function(e){this.DetailsView=e.DetailsView,this.cancelText=e.cancelText,this.addText=e.addText,this.media=new wp.media.model.PostMedia(e.metadata),this.options.selection=new wp.media.model.Selection(this.media.attachment,{multiple:!1}),i.prototype.initialize.apply(this,arguments)},bindHandlers:function(){var e=this.defaults.menu;i.prototype.bindHandlers.apply(this,arguments),this.on("menu:create:"+e,this.createMenu,this),this.on("content:render:"+e,this.renderDetailsContent,this),this.on("menu:render:"+e,this.renderMenu,this),this.on("toolbar:render:"+e,this.renderDetailsToolbar,this)},renderDetailsContent:function(){var e=new this.DetailsView({controller:this,model:this.state().media,attachment:this.state().media.attachment}).render();this.content.set(e)},renderMenu:function(e){var t=this.lastState(),i=t&&t.id,a=this;e.set({cancel:{text:this.cancelText,priority:20,click:function(){i?a.setState(i):a.close()}},separateCancel:new wp.media.View({className:"separator",priority:40})})},setPrimaryButton:function(e,t){this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{button:{style:"primary",text:e,priority:80,click:function(){var e=this.controller;t.call(this,e,e.state()),e.setState(e.options.state),e.reset()}}}}))},renderDetailsToolbar:function(){this.setPrimaryButton(a.update,function(e,t){e.close(),t.trigger("update",e.media.toJSON())})},renderReplaceToolbar:function(){this.setPrimaryButton(a.replace,function(e,t){var i=t.get("selection").single();e.media.changeAttachment(i),t.trigger("replace",e.media.toJSON())})},renderAddSourceToolbar:function(){this.setPrimaryButton(this.addText,function(e,t){var i=t.get("selection").single();e.media.setSource(i),t.trigger("add-source",e.media.toJSON())})}});e.exports=s},"./src/js/media/views/frame/video-details.js":function(e,t){var i=wp.media.view.MediaFrame.MediaDetails,a=wp.media.controller.MediaLibrary,s=wp.media.view.l10n,o=i.extend({defaults:{id:"video",url:"",menu:"video-details",content:"video-details",toolbar:"video-details",type:"link",title:s.videoDetailsTitle,priority:120},initialize:function(e){e.DetailsView=wp.media.view.VideoDetails,e.cancelText=s.videoDetailsCancel,e.addText=s.videoAddSourceTitle,i.prototype.initialize.call(this,e)},bindHandlers:function(){i.prototype.bindHandlers.apply(this,arguments),this.on("toolbar:render:replace-video",this.renderReplaceToolbar,this),this.on("toolbar:render:add-video-source",this.renderAddSourceToolbar,this),this.on("toolbar:render:select-poster-image",this.renderSelectPosterImageToolbar,this),this.on("toolbar:render:add-track",this.renderAddTrackToolbar,this)},createStates:function(){this.states.add([new wp.media.controller.VideoDetails({media:this.media}),new a({type:"video",id:"replace-video",title:s.videoReplaceTitle,toolbar:"replace-video",media:this.media,menu:"video-details"}),new a({type:"video",id:"add-video-source",title:s.videoAddSourceTitle,toolbar:"add-video-source",media:this.media,menu:!1}),new a({type:"image",id:"select-poster-image",title:s.videoSelectPosterImageTitle,toolbar:"select-poster-image",media:this.media,menu:"video-details"}),new a({type:"text",id:"add-track",title:s.videoAddTrackTitle,toolbar:"add-track",media:this.media,menu:"video-details"})])},renderSelectPosterImageToolbar:function(){this.setPrimaryButton(s.videoSelectPosterImageTitle,function(t,e){var i=[],a=e.get("selection").single();t.media.set("poster",a.get("url")),e.trigger("set-poster-image",t.media.toJSON()),_.each(wp.media.view.settings.embedExts,function(e){t.media.get(e)&&i.push(t.media.get(e))}),wp.ajax.send("set-attachment-thumbnail",{data:{urls:i,thumbnail_id:a.get("id")}})})},renderAddTrackToolbar:function(){this.setPrimaryButton(s.videoAddTrackTitle,function(e,t){var i=t.get("selection").single(),a=e.media.get("content");-1===a.indexOf(i.get("url"))&&(a+=[''].join(""),e.media.set("content",a)),t.trigger("add-track",e.media.toJSON())})}});e.exports=o},"./src/js/media/views/media-details.js":function(e,t){var i=wp.media.view.Settings.AttachmentDisplay,a=jQuery,s=i.extend({initialize:function(){_.bindAll(this,"success"),this.players=[],this.listenTo(this.controller.states,"close",wp.media.mixin.unsetPlayers),this.on("ready",this.setPlayer),this.on("media:setting:remove",wp.media.mixin.unsetPlayers,this),this.on("media:setting:remove",this.render),this.on("media:setting:remove",this.setPlayer),i.prototype.initialize.apply(this,arguments)},events:function(){return _.extend({"click .remove-setting":"removeSetting","change .content-track":"setTracks","click .remove-track":"setTracks","click .add-media-source":"addSource"},i.prototype.events)},prepare:function(){return _.defaults({model:this.model.toJSON()},this.options)},removeSetting:function(e){var t=a(e.currentTarget).parent(),e=t.find("input").data("setting");e&&(this.model.unset(e),this.trigger("media:setting:remove",this)),t.remove()},setTracks:function(){var t="";_.each(this.$(".content-track"),function(e){t+=a(e).val()}),this.model.set("content",t),this.trigger("media:setting:remove",this)},addSource:function(e){this.controller.lastMime=a(e.currentTarget).data("mime"),this.controller.setState("add-"+this.controller.defaults.id+"-source")},loadPlayer:function(){this.players.push(new MediaElementPlayer(this.media,this.settings)),this.scriptXhr=!1},setPlayer:function(){var e;this.players.length||!this.media||this.scriptXhr||((e=this.model.get("src"))&&-1'].join(""),e.media.set("content",a)),t.trigger("add-track",e.media.toJSON())})}});e.exports=n},pMD9:function(e,t,i){var a=wp.media,o=window._wpmejsSettings||{},n=window._wpMediaViewsL10n||{};wp.media.mixin={mejsSettings:o,removeAllPlayers:function(){if(window.mejs&&window.mejs.players)for(var e in window.mejs.players)window.mejs.players[e].pause(),this.removePlayer(window.mejs.players[e])},removePlayer:function(e){var t,i;if(e.options){for(t in e.options.features)if(e["clean"+(i=e.options.features[t])])try{e["clean"+i](e)}catch(e){}e.isDynamic||e.node.remove(),"html5"!==e.media.rendererName&&e.media.remove(),delete window.mejs.players[e.id],e.container.remove(),e.globalUnbind("resize",e.globalResizeCallback),e.globalUnbind("keydown",e.globalKeydownCallback),e.globalUnbind("click",e.globalClickCallback),delete e.media.player}},unsetPlayers:function(){this.players&&this.players.length&&(_.each(this.players,function(e){e.pause(),wp.media.mixin.removePlayer(e)}),this.players=[])}},wp.media.playlist=new wp.media.collection({tag:"playlist",editTitle:n.editPlaylistTitle,defaults:{id:wp.media.view.settings.post.id,style:"light",tracklist:!0,tracknumbers:!0,images:!0,artists:!0,type:"audio"}}),wp.media.audio={coerce:wp.media.coerce,defaults:{id:wp.media.view.settings.post.id,src:"",loop:!1,autoplay:!1,preload:"none",width:400},edit:function(e){e=wp.shortcode.next("audio",e).shortcode;return wp.media({frame:"audio",state:"audio-details",metadata:_.defaults(e.attrs.named,this.defaults)})},shortcode:function(i){var e;return _.each(this.defaults,function(e,t){i[t]=this.coerce(i,t),e===i[t]&&delete i[t]},this),e=i.content,delete i.content,new wp.shortcode({tag:"audio",attrs:i,content:e})}},wp.media.video={coerce:wp.media.coerce,defaults:{id:wp.media.view.settings.post.id,src:"",poster:"",loop:!1,autoplay:!1,preload:"metadata",content:"",width:640,height:360},edit:function(e){var t=wp.shortcode.next("video",e).shortcode,e=t.attrs.named;return e.content=t.content,wp.media({frame:"video",state:"video-details",metadata:_.defaults(e,this.defaults)})},shortcode:function(i){var e;return _.each(this.defaults,function(e,t){i[t]=this.coerce(i,t),e===i[t]&&delete i[t]},this),e=i.content,delete i.content,new wp.shortcode({tag:"video",attrs:i,content:e})}},a.model.PostMedia=i("+RYg"),a.controller.AudioDetails=i("6pp6"),a.controller.VideoDetails=i("Xcj4"),a.view.MediaFrame.MediaDetails=i("RQe2"),a.view.MediaFrame.AudioDetails=i("Bdio"),a.view.MediaFrame.VideoDetails=i("m85o"),a.view.MediaDetails=i("/4UI"),a.view.AudioDetails=i("LX3m"),a.view.VideoDetails=i("MT9K")}}); \ No newline at end of file diff --git a/wp-includes/js/media-grid.js b/wp-includes/js/media-grid.js index 8a64f9ceff..84fc90be52 100644 --- a/wp-includes/js/media-grid.js +++ b/wp-includes/js/media-grid.js @@ -86,187 +86,15 @@ /************************************************************************/ /******/ ({ -/***/ "./src/js/_enqueues/wp/media/grid.js": +/***/ 1: /***/ (function(module, exports, __webpack_require__) { -/** - * @output wp-includes/js/media-grid.js - */ - -var media = wp.media; - -media.controller.EditAttachmentMetadata = __webpack_require__( "./src/js/media/controllers/edit-attachment-metadata.js" ); -media.view.MediaFrame.Manage = __webpack_require__( "./src/js/media/views/frame/manage.js" ); -media.view.Attachment.Details.TwoColumn = __webpack_require__( "./src/js/media/views/attachment/details-two-column.js" ); -media.view.MediaFrame.Manage.Router = __webpack_require__( "./src/js/media/routers/manage.js" ); -media.view.EditImage.Details = __webpack_require__( "./src/js/media/views/edit-image-details.js" ); -media.view.MediaFrame.EditAttachments = __webpack_require__( "./src/js/media/views/frame/edit-attachments.js" ); -media.view.SelectModeToggleButton = __webpack_require__( "./src/js/media/views/button/select-mode-toggle.js" ); -media.view.DeleteSelectedButton = __webpack_require__( "./src/js/media/views/button/delete-selected.js" ); -media.view.DeleteSelectedPermanentlyButton = __webpack_require__( "./src/js/media/views/button/delete-selected-permanently.js" ); +module.exports = __webpack_require__("LRQ5"); /***/ }), -/***/ "./src/js/media/controllers/edit-attachment-metadata.js": -/***/ (function(module, exports) { - -var l10n = wp.media.view.l10n, - EditAttachmentMetadata; - -/** - * wp.media.controller.EditAttachmentMetadata - * - * A state for editing an attachment's metadata. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.State - * @augments Backbone.Model - */ -EditAttachmentMetadata = wp.media.controller.State.extend(/** @lends wp.media.controller.EditAttachmentMetadata.prototype */{ - defaults: { - id: 'edit-attachment', - // Title string passed to the frame's title region view. - title: l10n.attachmentDetails, - // Region mode defaults. - content: 'edit-metadata', - menu: false, - toolbar: false, - router: false - } -}); - -module.exports = EditAttachmentMetadata; - - -/***/ }), - -/***/ "./src/js/media/routers/manage.js": -/***/ (function(module, exports) { - -/** - * wp.media.view.MediaFrame.Manage.Router - * - * A router for handling the browser history and application state. - * - * @memberOf wp.media.view.MediaFrame.Manage - * - * @class - * @augments Backbone.Router - */ -var Router = Backbone.Router.extend(/** @lends wp.media.view.MediaFrame.Manage.Router.prototype */{ - routes: { - 'upload.php?item=:slug&mode=edit': 'editItem', - 'upload.php?item=:slug': 'showItem', - 'upload.php?search=:query': 'search', - 'upload.php': 'reset' - }, - - // Map routes against the page URL. - baseUrl: function( url ) { - return 'upload.php' + url; - }, - - reset: function() { - var frame = wp.media.frames.edit; - - if ( frame ) { - frame.close(); - } - }, - - // Respond to the search route by filling the search field and triggering the input event. - search: function( query ) { - jQuery( '#media-search-input' ).val( query ).trigger( 'input' ); - }, - - // Show the modal with a specific item. - showItem: function( query ) { - var media = wp.media, - frame = media.frames.browse, - library = frame.state().get('library'), - item; - - // Trigger the media frame to open the correct item. - item = library.findWhere( { id: parseInt( query, 10 ) } ); - - if ( item ) { - item.set( 'skipHistory', true ); - frame.trigger( 'edit:attachment', item ); - } else { - item = media.attachment( query ); - frame.listenTo( item, 'change', function( model ) { - frame.stopListening( item ); - frame.trigger( 'edit:attachment', model ); - } ); - item.fetch(); - } - }, - - // Show the modal in edit mode with a specific item. - editItem: function( query ) { - this.showItem( query ); - wp.media.frames.edit.content.mode( 'edit-details' ); - } -}); - -module.exports = Router; - - -/***/ }), - -/***/ "./src/js/media/views/attachment/details-two-column.js": -/***/ (function(module, exports) { - -var Details = wp.media.view.Attachment.Details, - TwoColumn; - -/** - * wp.media.view.Attachment.Details.TwoColumn - * - * A similar view to media.view.Attachment.Details - * for use in the Edit Attachment modal. - * - * @memberOf wp.media.view.Attachment.Details - * - * @class - * @augments wp.media.view.Attachment.Details - * @augments wp.media.view.Attachment - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -TwoColumn = Details.extend(/** @lends wp.media.view.Attachment.Details.TowColumn.prototype */{ - template: wp.template( 'attachment-details-two-column' ), - - initialize: function() { - this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) ); - - Details.prototype.initialize.apply( this, arguments ); - }, - - editAttachment: function( event ) { - if ( event ) { - event.preventDefault(); - } - this.controller.content.mode( 'edit-image' ); - }, - - /** - * Noop this from parent class, doesn't apply here. - */ - toggleSelectionHandler: function() {} - -}); - -module.exports = TwoColumn; - - -/***/ }), - -/***/ "./src/js/media/views/button/delete-selected-permanently.js": +/***/ "1lLZ": /***/ (function(module, exports) { var Button = wp.media.view.Button, @@ -320,68 +148,56 @@ module.exports = DeleteSelectedPermanently; /***/ }), -/***/ "./src/js/media/views/button/delete-selected.js": +/***/ "FcM5": /***/ (function(module, exports) { -var Button = wp.media.view.Button, - l10n = wp.media.view.l10n, - DeleteSelected; +var Details = wp.media.view.Attachment.Details, + TwoColumn; /** - * wp.media.view.DeleteSelectedButton + * wp.media.view.Attachment.Details.TwoColumn * - * A button that handles bulk Delete/Trash logic + * A similar view to media.view.Attachment.Details + * for use in the Edit Attachment modal. * - * @memberOf wp.media.view + * @memberOf wp.media.view.Attachment.Details * * @class - * @augments wp.media.view.Button + * @augments wp.media.view.Attachment.Details + * @augments wp.media.view.Attachment * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View */ -DeleteSelected = Button.extend(/** @lends wp.media.view.DeleteSelectedButton.prototype */{ +TwoColumn = Details.extend(/** @lends wp.media.view.Attachment.Details.TowColumn.prototype */{ + template: wp.template( 'attachment-details-two-column' ), + initialize: function() { - Button.prototype.initialize.apply( this, arguments ); - if ( this.options.filters ) { - this.options.filters.model.on( 'change', this.filterChange, this ); - } - this.controller.on( 'selection:toggle', this.toggleDisabled, this ); - this.controller.on( 'select:activate', this.toggleDisabled, this ); + this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) ); + + Details.prototype.initialize.apply( this, arguments ); }, - filterChange: function( model ) { - if ( 'trash' === model.get( 'status' ) ) { - this.model.set( 'text', l10n.restoreSelected ); - } else if ( wp.media.view.settings.mediaTrash ) { - this.model.set( 'text', l10n.trashSelected ); - } else { - this.model.set( 'text', l10n.deletePermanently ); + editAttachment: function( event ) { + if ( event ) { + event.preventDefault(); } + this.controller.content.mode( 'edit-image' ); }, - toggleDisabled: function() { - this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length ); - }, + /** + * Noop this from parent class, doesn't apply here. + */ + toggleSelectionHandler: function() {} - render: function() { - Button.prototype.render.apply( this, arguments ); - if ( this.controller.isModeActive( 'select' ) ) { - this.$el.addClass( 'delete-selected-button' ); - } else { - this.$el.addClass( 'delete-selected-button hidden' ); - } - this.toggleDisabled(); - return this; - } }); -module.exports = DeleteSelected; +module.exports = TwoColumn; /***/ }), -/***/ "./src/js/media/views/button/select-mode-toggle.js": +/***/ "Ffsb": /***/ (function(module, exports) { @@ -465,7 +281,7 @@ module.exports = SelectModeToggle; /***/ }), -/***/ "./src/js/media/views/edit-image-details.js": +/***/ "HUrf": /***/ (function(module, exports) { var View = wp.media.View, @@ -507,289 +323,138 @@ module.exports = Details; /***/ }), -/***/ "./src/js/media/views/frame/edit-attachments.js": -/***/ (function(module, exports) { - -var Frame = wp.media.view.Frame, - MediaFrame = wp.media.view.MediaFrame, - - $ = jQuery, - EditAttachments; +/***/ "LRQ5": +/***/ (function(module, exports, __webpack_require__) { /** - * wp.media.view.MediaFrame.EditAttachments - * - * A frame for editing the details of a specific media item. - * - * Opens in a modal by default. - * - * Requires an attachment model to be passed in the options hash under `model`. - * - * @memberOf wp.media.view.MediaFrame - * - * @class - * @augments wp.media.view.Frame - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - * @mixes wp.media.controller.StateMachine + * @output wp-includes/js/media-grid.js */ -EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAttachments.prototype */{ - className: 'edit-attachment-frame', - template: wp.template( 'edit-attachment-frame' ), - regions: [ 'title', 'content' ], +var media = wp.media; - events: { - 'click .left': 'previousMediaItem', - 'click .right': 'nextMediaItem' - }, - - initialize: function() { - Frame.prototype.initialize.apply( this, arguments ); - - _.defaults( this.options, { - modal: true, - state: 'edit-attachment' - }); - - this.controller = this.options.controller; - this.gridRouter = this.controller.gridRouter; - this.library = this.options.library; - - if ( this.options.model ) { - this.model = this.options.model; - } - - this.bindHandlers(); - this.createStates(); - this.createModal(); - - this.title.mode( 'default' ); - this.toggleNav(); - }, - - bindHandlers: function() { - // Bind default title creation. - this.on( 'title:create:default', this.createTitle, this ); - - this.on( 'content:create:edit-metadata', this.editMetadataMode, this ); - this.on( 'content:create:edit-image', this.editImageMode, this ); - this.on( 'content:render:edit-image', this.editImageModeRender, this ); - this.on( 'refresh', this.rerender, this ); - this.on( 'close', this.detach ); - - this.bindModelHandlers(); - this.listenTo( this.gridRouter, 'route:search', this.close, this ); - }, - - bindModelHandlers: function() { - // Close the modal if the attachment is deleted. - this.listenTo( this.model, 'change:status destroy', this.close, this ); - }, - - createModal: function() { - // Initialize modal container view. - if ( this.options.modal ) { - this.modal = new wp.media.view.Modal({ - controller: this, - title: this.options.title, - hasCloseButton: false - }); - - this.modal.on( 'open', _.bind( function () { - $( 'body' ).on( 'keydown.media-modal', _.bind( this.keyEvent, this ) ); - }, this ) ); - - // Completely destroy the modal DOM element when closing it. - this.modal.on( 'close', _.bind( function() { - // Remove the keydown event. - $( 'body' ).off( 'keydown.media-modal' ); - // Move focus back to the original item in the grid if possible. - $( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).trigger( 'focus' ); - this.resetRoute(); - }, this ) ); - - // Set this frame as the modal's content. - this.modal.content( this ); - this.modal.open(); - } - }, - - /** - * Add the default states to the frame. - */ - createStates: function() { - this.states.add([ - new wp.media.controller.EditAttachmentMetadata({ - model: this.model, - library: this.library - }) - ]); - }, - - /** - * Content region rendering callback for the `edit-metadata` mode. - * - * @param {Object} contentRegion Basic object with a `view` property, which - * should be set with the proper region view. - */ - editMetadataMode: function( contentRegion ) { - contentRegion.view = new wp.media.view.Attachment.Details.TwoColumn({ - controller: this, - model: this.model - }); - - /** - * Attach a subview to display fields added via the - * `attachment_fields_to_edit` filter. - */ - contentRegion.view.views.set( '.attachment-compat', new wp.media.view.AttachmentCompat({ - controller: this, - model: this.model - }) ); - - // Update browser url when navigating media details, except on load. - if ( this.model && ! this.model.get( 'skipHistory' ) ) { - this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) ); - } - }, - - /** - * Render the EditImage view into the frame's content region. - * - * @param {Object} contentRegion Basic object with a `view` property, which - * should be set with the proper region view. - */ - editImageMode: function( contentRegion ) { - var editImageController = new wp.media.controller.EditImage( { - model: this.model, - frame: this - } ); - // Noop some methods. - editImageController._toolbar = function() {}; - editImageController._router = function() {}; - editImageController._menu = function() {}; - - contentRegion.view = new wp.media.view.EditImage.Details( { - model: this.model, - frame: this, - controller: editImageController - } ); - - this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) ); - - }, - - editImageModeRender: function( view ) { - view.on( 'ready', view.loadEditor ); - }, - - toggleNav: function() { - this.$( '.left' ).prop( 'disabled', ! this.hasPrevious() ); - this.$( '.right' ).prop( 'disabled', ! this.hasNext() ); - }, - - /** - * Rerender the view. - */ - rerender: function( model ) { - this.stopListening( this.model ); - - this.model = model; - - this.bindModelHandlers(); - - // Only rerender the `content` region. - if ( this.content.mode() !== 'edit-metadata' ) { - this.content.mode( 'edit-metadata' ); - } else { - this.content.render(); - } - - this.toggleNav(); - }, - - /** - * Click handler to switch to the previous media item. - */ - previousMediaItem: function() { - if ( ! this.hasPrevious() ) { - return; - } - - this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) ); - // Move focus to the Previous button. When there are no more items, to the Next button. - this.focusNavButton( this.hasPrevious() ? '.left' : '.right' ); - }, - - /** - * Click handler to switch to the next media item. - */ - nextMediaItem: function() { - if ( ! this.hasNext() ) { - return; - } - - this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) ); - // Move focus to the Next button. When there are no more items, to the Previous button. - this.focusNavButton( this.hasNext() ? '.right' : '.left' ); - }, - - /** - * Set focus to the navigation buttons depending on the browsing direction. - * - * @since 5.3.0 - * - * @param {string} which A CSS selector to target the button to focus. - */ - focusNavButton: function( which ) { - $( which ).trigger( 'focus' ); - }, - - getCurrentIndex: function() { - return this.library.indexOf( this.model ); - }, - - hasNext: function() { - return ( this.getCurrentIndex() + 1 ) < this.library.length; - }, - - hasPrevious: function() { - return ( this.getCurrentIndex() - 1 ) > -1; - }, - /** - * Respond to the keyboard events: right arrow, left arrow, except when - * focus is in a textarea or input field. - */ - keyEvent: function( event ) { - if ( ( 'INPUT' === event.target.nodeName || 'TEXTAREA' === event.target.nodeName ) && ! ( event.target.readOnly || event.target.disabled ) ) { - return; - } - - // The right arrow key. - if ( 39 === event.keyCode ) { - this.nextMediaItem(); - } - // The left arrow key. - if ( 37 === event.keyCode ) { - this.previousMediaItem(); - } - }, - - resetRoute: function() { - var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(), - url = '' !== searchTerm ? '?search=' + searchTerm : ''; - this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); - } -}); - -module.exports = EditAttachments; +media.controller.EditAttachmentMetadata = __webpack_require__( "ZJBI" ); +media.view.MediaFrame.Manage = __webpack_require__( "lH8y" ); +media.view.Attachment.Details.TwoColumn = __webpack_require__( "FcM5" ); +media.view.MediaFrame.Manage.Router = __webpack_require__( "OMfl" ); +media.view.EditImage.Details = __webpack_require__( "HUrf" ); +media.view.MediaFrame.EditAttachments = __webpack_require__( "wQX5" ); +media.view.SelectModeToggleButton = __webpack_require__( "Ffsb" ); +media.view.DeleteSelectedButton = __webpack_require__( "nD7t" ); +media.view.DeleteSelectedPermanentlyButton = __webpack_require__( "1lLZ" ); /***/ }), -/***/ "./src/js/media/views/frame/manage.js": +/***/ "OMfl": +/***/ (function(module, exports) { + +/** + * wp.media.view.MediaFrame.Manage.Router + * + * A router for handling the browser history and application state. + * + * @memberOf wp.media.view.MediaFrame.Manage + * + * @class + * @augments Backbone.Router + */ +var Router = Backbone.Router.extend(/** @lends wp.media.view.MediaFrame.Manage.Router.prototype */{ + routes: { + 'upload.php?item=:slug&mode=edit': 'editItem', + 'upload.php?item=:slug': 'showItem', + 'upload.php?search=:query': 'search', + 'upload.php': 'reset' + }, + + // Map routes against the page URL. + baseUrl: function( url ) { + return 'upload.php' + url; + }, + + reset: function() { + var frame = wp.media.frames.edit; + + if ( frame ) { + frame.close(); + } + }, + + // Respond to the search route by filling the search field and triggering the input event. + search: function( query ) { + jQuery( '#media-search-input' ).val( query ).trigger( 'input' ); + }, + + // Show the modal with a specific item. + showItem: function( query ) { + var media = wp.media, + frame = media.frames.browse, + library = frame.state().get('library'), + item; + + // Trigger the media frame to open the correct item. + item = library.findWhere( { id: parseInt( query, 10 ) } ); + + if ( item ) { + item.set( 'skipHistory', true ); + frame.trigger( 'edit:attachment', item ); + } else { + item = media.attachment( query ); + frame.listenTo( item, 'change', function( model ) { + frame.stopListening( item ); + frame.trigger( 'edit:attachment', model ); + } ); + item.fetch(); + } + }, + + // Show the modal in edit mode with a specific item. + editItem: function( query ) { + this.showItem( query ); + wp.media.frames.edit.content.mode( 'edit-details' ); + } +}); + +module.exports = Router; + + +/***/ }), + +/***/ "ZJBI": +/***/ (function(module, exports) { + +var l10n = wp.media.view.l10n, + EditAttachmentMetadata; + +/** + * wp.media.controller.EditAttachmentMetadata + * + * A state for editing an attachment's metadata. + * + * @memberOf wp.media.controller + * + * @class + * @augments wp.media.controller.State + * @augments Backbone.Model + */ +EditAttachmentMetadata = wp.media.controller.State.extend(/** @lends wp.media.controller.EditAttachmentMetadata.prototype */{ + defaults: { + id: 'edit-attachment', + // Title string passed to the frame's title region view. + title: l10n.attachmentDetails, + // Region mode defaults. + content: 'edit-metadata', + menu: false, + toolbar: false, + router: false + } +}); + +module.exports = EditAttachmentMetadata; + + +/***/ }), + +/***/ "lH8y": /***/ (function(module, exports) { var MediaFrame = wp.media.view.MediaFrame, @@ -1083,10 +748,345 @@ module.exports = Manage; /***/ }), -/***/ 1: -/***/ (function(module, exports, __webpack_require__) { +/***/ "nD7t": +/***/ (function(module, exports) { -module.exports = __webpack_require__("./src/js/_enqueues/wp/media/grid.js"); +var Button = wp.media.view.Button, + l10n = wp.media.view.l10n, + DeleteSelected; + +/** + * wp.media.view.DeleteSelectedButton + * + * A button that handles bulk Delete/Trash logic + * + * @memberOf wp.media.view + * + * @class + * @augments wp.media.view.Button + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + */ +DeleteSelected = Button.extend(/** @lends wp.media.view.DeleteSelectedButton.prototype */{ + initialize: function() { + Button.prototype.initialize.apply( this, arguments ); + if ( this.options.filters ) { + this.options.filters.model.on( 'change', this.filterChange, this ); + } + this.controller.on( 'selection:toggle', this.toggleDisabled, this ); + this.controller.on( 'select:activate', this.toggleDisabled, this ); + }, + + filterChange: function( model ) { + if ( 'trash' === model.get( 'status' ) ) { + this.model.set( 'text', l10n.restoreSelected ); + } else if ( wp.media.view.settings.mediaTrash ) { + this.model.set( 'text', l10n.trashSelected ); + } else { + this.model.set( 'text', l10n.deletePermanently ); + } + }, + + toggleDisabled: function() { + this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length ); + }, + + render: function() { + Button.prototype.render.apply( this, arguments ); + if ( this.controller.isModeActive( 'select' ) ) { + this.$el.addClass( 'delete-selected-button' ); + } else { + this.$el.addClass( 'delete-selected-button hidden' ); + } + this.toggleDisabled(); + return this; + } +}); + +module.exports = DeleteSelected; + + +/***/ }), + +/***/ "wQX5": +/***/ (function(module, exports) { + +var Frame = wp.media.view.Frame, + MediaFrame = wp.media.view.MediaFrame, + + $ = jQuery, + EditAttachments; + +/** + * wp.media.view.MediaFrame.EditAttachments + * + * A frame for editing the details of a specific media item. + * + * Opens in a modal by default. + * + * Requires an attachment model to be passed in the options hash under `model`. + * + * @memberOf wp.media.view.MediaFrame + * + * @class + * @augments wp.media.view.Frame + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + * @mixes wp.media.controller.StateMachine + */ +EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAttachments.prototype */{ + + className: 'edit-attachment-frame', + template: wp.template( 'edit-attachment-frame' ), + regions: [ 'title', 'content' ], + + events: { + 'click .left': 'previousMediaItem', + 'click .right': 'nextMediaItem' + }, + + initialize: function() { + Frame.prototype.initialize.apply( this, arguments ); + + _.defaults( this.options, { + modal: true, + state: 'edit-attachment' + }); + + this.controller = this.options.controller; + this.gridRouter = this.controller.gridRouter; + this.library = this.options.library; + + if ( this.options.model ) { + this.model = this.options.model; + } + + this.bindHandlers(); + this.createStates(); + this.createModal(); + + this.title.mode( 'default' ); + this.toggleNav(); + }, + + bindHandlers: function() { + // Bind default title creation. + this.on( 'title:create:default', this.createTitle, this ); + + this.on( 'content:create:edit-metadata', this.editMetadataMode, this ); + this.on( 'content:create:edit-image', this.editImageMode, this ); + this.on( 'content:render:edit-image', this.editImageModeRender, this ); + this.on( 'refresh', this.rerender, this ); + this.on( 'close', this.detach ); + + this.bindModelHandlers(); + this.listenTo( this.gridRouter, 'route:search', this.close, this ); + }, + + bindModelHandlers: function() { + // Close the modal if the attachment is deleted. + this.listenTo( this.model, 'change:status destroy', this.close, this ); + }, + + createModal: function() { + // Initialize modal container view. + if ( this.options.modal ) { + this.modal = new wp.media.view.Modal({ + controller: this, + title: this.options.title, + hasCloseButton: false + }); + + this.modal.on( 'open', _.bind( function () { + $( 'body' ).on( 'keydown.media-modal', _.bind( this.keyEvent, this ) ); + }, this ) ); + + // Completely destroy the modal DOM element when closing it. + this.modal.on( 'close', _.bind( function() { + // Remove the keydown event. + $( 'body' ).off( 'keydown.media-modal' ); + // Move focus back to the original item in the grid if possible. + $( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).trigger( 'focus' ); + this.resetRoute(); + }, this ) ); + + // Set this frame as the modal's content. + this.modal.content( this ); + this.modal.open(); + } + }, + + /** + * Add the default states to the frame. + */ + createStates: function() { + this.states.add([ + new wp.media.controller.EditAttachmentMetadata({ + model: this.model, + library: this.library + }) + ]); + }, + + /** + * Content region rendering callback for the `edit-metadata` mode. + * + * @param {Object} contentRegion Basic object with a `view` property, which + * should be set with the proper region view. + */ + editMetadataMode: function( contentRegion ) { + contentRegion.view = new wp.media.view.Attachment.Details.TwoColumn({ + controller: this, + model: this.model + }); + + /** + * Attach a subview to display fields added via the + * `attachment_fields_to_edit` filter. + */ + contentRegion.view.views.set( '.attachment-compat', new wp.media.view.AttachmentCompat({ + controller: this, + model: this.model + }) ); + + // Update browser url when navigating media details, except on load. + if ( this.model && ! this.model.get( 'skipHistory' ) ) { + this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) ); + } + }, + + /** + * Render the EditImage view into the frame's content region. + * + * @param {Object} contentRegion Basic object with a `view` property, which + * should be set with the proper region view. + */ + editImageMode: function( contentRegion ) { + var editImageController = new wp.media.controller.EditImage( { + model: this.model, + frame: this + } ); + // Noop some methods. + editImageController._toolbar = function() {}; + editImageController._router = function() {}; + editImageController._menu = function() {}; + + contentRegion.view = new wp.media.view.EditImage.Details( { + model: this.model, + frame: this, + controller: editImageController + } ); + + this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) ); + + }, + + editImageModeRender: function( view ) { + view.on( 'ready', view.loadEditor ); + }, + + toggleNav: function() { + this.$( '.left' ).prop( 'disabled', ! this.hasPrevious() ); + this.$( '.right' ).prop( 'disabled', ! this.hasNext() ); + }, + + /** + * Rerender the view. + */ + rerender: function( model ) { + this.stopListening( this.model ); + + this.model = model; + + this.bindModelHandlers(); + + // Only rerender the `content` region. + if ( this.content.mode() !== 'edit-metadata' ) { + this.content.mode( 'edit-metadata' ); + } else { + this.content.render(); + } + + this.toggleNav(); + }, + + /** + * Click handler to switch to the previous media item. + */ + previousMediaItem: function() { + if ( ! this.hasPrevious() ) { + return; + } + + this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) ); + // Move focus to the Previous button. When there are no more items, to the Next button. + this.focusNavButton( this.hasPrevious() ? '.left' : '.right' ); + }, + + /** + * Click handler to switch to the next media item. + */ + nextMediaItem: function() { + if ( ! this.hasNext() ) { + return; + } + + this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) ); + // Move focus to the Next button. When there are no more items, to the Previous button. + this.focusNavButton( this.hasNext() ? '.right' : '.left' ); + }, + + /** + * Set focus to the navigation buttons depending on the browsing direction. + * + * @since 5.3.0 + * + * @param {string} which A CSS selector to target the button to focus. + */ + focusNavButton: function( which ) { + $( which ).trigger( 'focus' ); + }, + + getCurrentIndex: function() { + return this.library.indexOf( this.model ); + }, + + hasNext: function() { + return ( this.getCurrentIndex() + 1 ) < this.library.length; + }, + + hasPrevious: function() { + return ( this.getCurrentIndex() - 1 ) > -1; + }, + /** + * Respond to the keyboard events: right arrow, left arrow, except when + * focus is in a textarea or input field. + */ + keyEvent: function( event ) { + if ( ( 'INPUT' === event.target.nodeName || 'TEXTAREA' === event.target.nodeName ) && ! ( event.target.readOnly || event.target.disabled ) ) { + return; + } + + // The right arrow key. + if ( 39 === event.keyCode ) { + this.nextMediaItem(); + } + // The left arrow key. + if ( 37 === event.keyCode ) { + this.previousMediaItem(); + } + }, + + resetRoute: function() { + var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(), + url = '' !== searchTerm ? '?search=' + searchTerm : ''; + this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); + } +}); + +module.exports = EditAttachments; /***/ }) diff --git a/wp-includes/js/media-grid.min.js b/wp-includes/js/media-grid.min.js index dd931c3292..9b5211cf1d 100644 --- a/wp-includes/js/media-grid.min.js +++ b/wp-includes/js/media-grid.min.js @@ -1,2 +1,2 @@ /*! This file is auto-generated */ -!function(i){var s={};function o(e){if(s[e])return s[e].exports;var t=s[e]={i:e,l:!1,exports:{}};return i[e].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.m=i,o.c=s,o.d=function(e,t,i){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(o.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var s in t)o.d(i,s,function(e){return t[e]}.bind(null,s));return i},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=1)}({"./src/js/_enqueues/wp/media/grid.js":function(e,t,i){var s=wp.media;s.controller.EditAttachmentMetadata=i("./src/js/media/controllers/edit-attachment-metadata.js"),s.view.MediaFrame.Manage=i("./src/js/media/views/frame/manage.js"),s.view.Attachment.Details.TwoColumn=i("./src/js/media/views/attachment/details-two-column.js"),s.view.MediaFrame.Manage.Router=i("./src/js/media/routers/manage.js"),s.view.EditImage.Details=i("./src/js/media/views/edit-image-details.js"),s.view.MediaFrame.EditAttachments=i("./src/js/media/views/frame/edit-attachments.js"),s.view.SelectModeToggleButton=i("./src/js/media/views/button/select-mode-toggle.js"),s.view.DeleteSelectedButton=i("./src/js/media/views/button/delete-selected.js"),s.view.DeleteSelectedPermanentlyButton=i("./src/js/media/views/button/delete-selected-permanently.js")},"./src/js/media/controllers/edit-attachment-metadata.js":function(e,t){var i=wp.media.view.l10n,i=wp.media.controller.State.extend({defaults:{id:"edit-attachment",title:i.attachmentDetails,content:"edit-metadata",menu:!1,toolbar:!1,router:!1}});e.exports=i},"./src/js/media/routers/manage.js":function(e,t){var i=Backbone.Router.extend({routes:{"upload.php?item=:slug&mode=edit":"editItem","upload.php?item=:slug":"showItem","upload.php?search=:query":"search","upload.php":"reset"},baseUrl:function(e){return"upload.php"+e},reset:function(){var e=wp.media.frames.edit;e&&e.close()},search:function(e){jQuery("#media-search-input").val(e).trigger("input")},showItem:function(e){var t=wp.media,i=t.frames.browse,s=i.state().get("library").findWhere({id:parseInt(e,10)});s?(s.set("skipHistory",!0),i.trigger("edit:attachment",s)):(s=t.attachment(e),i.listenTo(s,"change",function(e){i.stopListening(s),i.trigger("edit:attachment",e)}),s.fetch())},editItem:function(e){this.showItem(e),wp.media.frames.edit.content.mode("edit-details")}});e.exports=i},"./src/js/media/views/attachment/details-two-column.js":function(e,t){var i=wp.media.view.Attachment.Details,s=i.extend({template:wp.template("attachment-details-two-column"),initialize:function(){this.controller.on("content:activate:edit-details",_.bind(this.editAttachment,this)),i.prototype.initialize.apply(this,arguments)},editAttachment:function(e){e&&e.preventDefault(),this.controller.content.mode("edit-image")},toggleSelectionHandler:function(){}});e.exports=s},"./src/js/media/views/button/delete-selected-permanently.js":function(e,t){var i=wp.media.view.Button,s=wp.media.view.DeleteSelectedButton,o=s.extend({initialize:function(){s.prototype.initialize.apply(this,arguments),this.controller.on("select:activate",this.selectActivate,this),this.controller.on("select:deactivate",this.selectDeactivate,this)},filterChange:function(e){this.canShow="trash"===e.get("status")},selectActivate:function(){this.toggleDisabled(),this.$el.toggleClass("hidden",!this.canShow)},selectDeactivate:function(){this.toggleDisabled(),this.$el.addClass("hidden")},render:function(){return i.prototype.render.apply(this,arguments),this.selectActivate(),this}});e.exports=o},"./src/js/media/views/button/delete-selected.js":function(e,t){var i=wp.media.view.Button,s=wp.media.view.l10n,o=i.extend({initialize:function(){i.prototype.initialize.apply(this,arguments),this.options.filters&&this.options.filters.model.on("change",this.filterChange,this),this.controller.on("selection:toggle",this.toggleDisabled,this),this.controller.on("select:activate",this.toggleDisabled,this)},filterChange:function(e){"trash"===e.get("status")?this.model.set("text",s.restoreSelected):wp.media.view.settings.mediaTrash?this.model.set("text",s.trashSelected):this.model.set("text",s.deletePermanently)},toggleDisabled:function(){this.model.set("disabled",!this.controller.state().get("selection").length)},render:function(){return i.prototype.render.apply(this,arguments),this.controller.isModeActive("select")?this.$el.addClass("delete-selected-button"):this.$el.addClass("delete-selected-button hidden"),this.toggleDisabled(),this}});e.exports=o},"./src/js/media/views/button/select-mode-toggle.js":function(e,t){var i=wp.media.view.Button,s=wp.media.view.l10n,o=i.extend({initialize:function(){_.defaults(this.options,{size:""}),i.prototype.initialize.apply(this,arguments),this.controller.on("select:activate select:deactivate",this.toggleBulkEditHandler,this),this.controller.on("selection:action:done",this.back,this)},back:function(){this.controller.deactivateMode("select").activateMode("edit")},click:function(){i.prototype.click.apply(this,arguments),this.controller.isModeActive("select")?this.back():this.controller.deactivateMode("edit").activateMode("select")},render:function(){return i.prototype.render.apply(this,arguments),this.$el.addClass("select-mode-toggle-button"),this},toggleBulkEditHandler:function(){var e=this.controller.content.get().toolbar,t=e.$(".media-toolbar-secondary > *, .media-toolbar-primary > *");this.controller.isModeActive("select")?(this.model.set({size:"large",text:s.cancel}),t.not(".spinner, .media-button").hide(),this.$el.show(),e.$el.addClass("media-toolbar-mode-select"),e.$(".delete-selected-button").removeClass("hidden")):(this.model.set({size:"",text:s.bulkSelect}),this.controller.content.get().$el.removeClass("fixed"),e.$el.css("width",""),e.$el.removeClass("media-toolbar-mode-select"),e.$(".delete-selected-button").addClass("hidden"),t.not(".media-button").show(),this.controller.state().get("selection").reset())}});e.exports=o},"./src/js/media/views/edit-image-details.js":function(e,t){var i=wp.media.View,s=wp.media.view.EditImage.extend({initialize:function(e){this.editor=window.imageEdit,this.frame=e.frame,this.controller=e.controller,i.prototype.initialize.apply(this,arguments)},back:function(){this.frame.content.mode("edit-metadata")},save:function(){this.model.fetch().done(_.bind(function(){this.frame.content.mode("edit-metadata")},this))}});e.exports=s},"./src/js/media/views/frame/edit-attachments.js":function(e,t){var i=wp.media.view.Frame,s=wp.media.view.MediaFrame,o=jQuery,s=s.extend({className:"edit-attachment-frame",template:wp.template("edit-attachment-frame"),regions:["title","content"],events:{"click .left":"previousMediaItem","click .right":"nextMediaItem"},initialize:function(){i.prototype.initialize.apply(this,arguments),_.defaults(this.options,{modal:!0,state:"edit-attachment"}),this.controller=this.options.controller,this.gridRouter=this.controller.gridRouter,this.library=this.options.library,this.options.model&&(this.model=this.options.model),this.bindHandlers(),this.createStates(),this.createModal(),this.title.mode("default"),this.toggleNav()},bindHandlers:function(){this.on("title:create:default",this.createTitle,this),this.on("content:create:edit-metadata",this.editMetadataMode,this),this.on("content:create:edit-image",this.editImageMode,this),this.on("content:render:edit-image",this.editImageModeRender,this),this.on("refresh",this.rerender,this),this.on("close",this.detach),this.bindModelHandlers(),this.listenTo(this.gridRouter,"route:search",this.close,this)},bindModelHandlers:function(){this.listenTo(this.model,"change:status destroy",this.close,this)},createModal:function(){this.options.modal&&(this.modal=new wp.media.view.Modal({controller:this,title:this.options.title,hasCloseButton:!1}),this.modal.on("open",_.bind(function(){o("body").on("keydown.media-modal",_.bind(this.keyEvent,this))},this)),this.modal.on("close",_.bind(function(){o("body").off("keydown.media-modal"),o('li.attachment[data-id="'+this.model.get("id")+'"]').trigger("focus"),this.resetRoute()},this)),this.modal.content(this),this.modal.open())},createStates:function(){this.states.add([new wp.media.controller.EditAttachmentMetadata({model:this.model,library:this.library})])},editMetadataMode:function(e){e.view=new wp.media.view.Attachment.Details.TwoColumn({controller:this,model:this.model}),e.view.views.set(".attachment-compat",new wp.media.view.AttachmentCompat({controller:this,model:this.model})),this.model&&!this.model.get("skipHistory")&&this.gridRouter.navigate(this.gridRouter.baseUrl("?item="+this.model.id))},editImageMode:function(e){var t=new wp.media.controller.EditImage({model:this.model,frame:this});t._toolbar=function(){},t._router=function(){},t._menu=function(){},e.view=new wp.media.view.EditImage.Details({model:this.model,frame:this,controller:t}),this.gridRouter.navigate(this.gridRouter.baseUrl("?item="+this.model.id+"&mode=edit"))},editImageModeRender:function(e){e.on("ready",e.loadEditor)},toggleNav:function(){this.$(".left").prop("disabled",!this.hasPrevious()),this.$(".right").prop("disabled",!this.hasNext())},rerender:function(e){this.stopListening(this.model),this.model=e,this.bindModelHandlers(),"edit-metadata"!==this.content.mode()?this.content.mode("edit-metadata"):this.content.render(),this.toggleNav()},previousMediaItem:function(){this.hasPrevious()&&(this.trigger("refresh",this.library.at(this.getCurrentIndex()-1)),this.focusNavButton(this.hasPrevious()?".left":".right"))},nextMediaItem:function(){this.hasNext()&&(this.trigger("refresh",this.library.at(this.getCurrentIndex()+1)),this.focusNavButton(this.hasNext()?".right":".left"))},focusNavButton:function(e){o(e).trigger("focus")},getCurrentIndex:function(){return this.library.indexOf(this.model)},hasNext:function(){return this.getCurrentIndex()+1 *, .media-toolbar-primary > *");this.controller.isModeActive("select")?(this.model.set({size:"large",text:o.cancel}),e.not(".spinner, .media-button").hide(),this.$el.show(),t.$el.addClass("media-toolbar-mode-select"),t.$(".delete-selected-button").removeClass("hidden")):(this.model.set({size:"",text:o.bulkSelect}),this.controller.content.get().$el.removeClass("fixed"),t.$el.css("width",""),t.$el.removeClass("media-toolbar-mode-select"),t.$(".delete-selected-button").addClass("hidden"),e.not(".media-button").show(),this.controller.state().get("selection").reset())}});t.exports=n},HUrf:function(t,e){var i=wp.media.View,o=wp.media.view.EditImage.extend({initialize:function(t){this.editor=window.imageEdit,this.frame=t.frame,this.controller=t.controller,i.prototype.initialize.apply(this,arguments)},back:function(){this.frame.content.mode("edit-metadata")},save:function(){this.model.fetch().done(_.bind(function(){this.frame.content.mode("edit-metadata")},this))}});t.exports=o},LRQ5:function(t,e,i){var o=wp.media;o.controller.EditAttachmentMetadata=i("ZJBI"),o.view.MediaFrame.Manage=i("lH8y"),o.view.Attachment.Details.TwoColumn=i("FcM5"),o.view.MediaFrame.Manage.Router=i("OMfl"),o.view.EditImage.Details=i("HUrf"),o.view.MediaFrame.EditAttachments=i("wQX5"),o.view.SelectModeToggleButton=i("Ffsb"),o.view.DeleteSelectedButton=i("nD7t"),o.view.DeleteSelectedPermanentlyButton=i("1lLZ")},OMfl:function(t,e){var i=Backbone.Router.extend({routes:{"upload.php?item=:slug&mode=edit":"editItem","upload.php?item=:slug":"showItem","upload.php?search=:query":"search","upload.php":"reset"},baseUrl:function(t){return"upload.php"+t},reset:function(){var t=wp.media.frames.edit;t&&t.close()},search:function(t){jQuery("#media-search-input").val(t).trigger("input")},showItem:function(t){var e=wp.media,i=e.frames.browse,o=i.state().get("library").findWhere({id:parseInt(t,10)});o?(o.set("skipHistory",!0),i.trigger("edit:attachment",o)):(o=e.attachment(t),i.listenTo(o,"change",function(t){i.stopListening(o),i.trigger("edit:attachment",t)}),o.fetch())},editItem:function(t){this.showItem(t),wp.media.frames.edit.content.mode("edit-details")}});t.exports=i},ZJBI:function(t,e){var i=wp.media.view.l10n,i=wp.media.controller.State.extend({defaults:{id:"edit-attachment",title:i.attachmentDetails,content:"edit-metadata",menu:!1,toolbar:!1,router:!1}});t.exports=i},lH8y:function(t,e){var i=wp.media.view.MediaFrame,o=wp.media.controller.Library,n=Backbone.$,s=i.extend({initialize:function(){_.defaults(this.options,{title:"",modal:!1,selection:[],library:{},multiple:"add",state:"library",uploader:!0,mode:["grid","edit"]}),this.$body=n(document.body),this.$window=n(window),this.$adminBar=n("#wpadminbar"),this.$uploaderToggler=n(".page-title-action").attr("aria-expanded","false").on("click",_.bind(this.addNewClickHandler,this)),this.$window.on("scroll resize",_.debounce(_.bind(this.fixPosition,this),15)),this.$el.addClass("wp-core-ui"),!wp.Uploader.limitExceeded&&wp.Uploader.browser.supported||(this.options.uploader=!1),this.options.uploader&&(this.uploader=new wp.media.view.UploaderWindow({controller:this,uploader:{dropzone:document.body,container:document.body}}).render(),this.uploader.ready(),n("body").append(this.uploader.el),this.options.uploader=!1),this.gridRouter=new wp.media.view.MediaFrame.Manage.Router,i.prototype.initialize.apply(this,arguments),this.$el.appendTo(this.options.container),this.createStates(),this.bindRegionModeHandlers(),this.render(),this.bindSearchHandler(),wp.media.frames.browse=this},bindSearchHandler:function(){var t=this.$("#media-search-input"),e=this.browserView.toolbar.get("search").$el,i=this.$(".view-list"),o=_.throttle(function(t){var e=n(t.currentTarget).val(),t="";e&&this.gridRouter.navigate(this.gridRouter.baseUrl(t+="?search="+e),{replace:!0})},1e3);t.on("input",_.bind(o,this)),this.gridRouter.on("route:search",function(){var t=window.location.href;-1 bc ? -1 : 1); - } else { - return a > b ? -1 : 1; - } -}; - -_.extend( media, /** @lends wp.media */{ - /** - * media.template( id ) - * - * Fetch a JavaScript template for an id, and return a templating function for it. - * - * See wp.template() in `wp-includes/js/wp-util.js`. - * - * @borrows wp.template as template - */ - template: wp.template, - - /** - * media.post( [action], [data] ) - * - * Sends a POST request to WordPress. - * See wp.ajax.post() in `wp-includes/js/wp-util.js`. - * - * @borrows wp.ajax.post as post - */ - post: wp.ajax.post, - - /** - * media.ajax( [action], [options] ) - * - * Sends an XHR request to WordPress. - * See wp.ajax.send() in `wp-includes/js/wp-util.js`. - * - * @borrows wp.ajax.send as ajax - */ - ajax: wp.ajax.send, - - /** - * Scales a set of dimensions to fit within bounding dimensions. - * - * @param {Object} dimensions - * @return {Object} - */ - fit: function( dimensions ) { - var width = dimensions.width, - height = dimensions.height, - maxWidth = dimensions.maxWidth, - maxHeight = dimensions.maxHeight, - constraint; - - /* - * Compare ratios between the two values to determine - * which max to constrain by. If a max value doesn't exist, - * then the opposite side is the constraint. - */ - if ( ! _.isUndefined( maxWidth ) && ! _.isUndefined( maxHeight ) ) { - constraint = ( width / height > maxWidth / maxHeight ) ? 'width' : 'height'; - } else if ( _.isUndefined( maxHeight ) ) { - constraint = 'width'; - } else if ( _.isUndefined( maxWidth ) && height > maxHeight ) { - constraint = 'height'; - } - - // If the value of the constrained side is larger than the max, - // then scale the values. Otherwise return the originals; they fit. - if ( 'width' === constraint && width > maxWidth ) { - return { - width : maxWidth, - height: Math.round( maxWidth * height / width ) - }; - } else if ( 'height' === constraint && height > maxHeight ) { - return { - width : Math.round( maxHeight * width / height ), - height: maxHeight - }; - } else { - return { - width : width, - height: height - }; - } - }, - /** - * Truncates a string by injecting an ellipsis into the middle. - * Useful for filenames. - * - * @param {string} string - * @param {number} [length=30] - * @param {string} [replacement=…] - * @return {string} The string, unless length is greater than string.length. - */ - truncate: function( string, length, replacement ) { - length = length || 30; - replacement = replacement || '…'; - - if ( string.length <= length ) { - return string; - } - - return string.substr( 0, length / 2 ) + replacement + string.substr( -1 * length / 2 ); - } -}); - -/** - * ======================================================================== - * MODELS - * ======================================================================== - */ -/** - * wp.media.attachment - * - * @static - * @param {string} id A string used to identify a model. - * @return {wp.media.model.Attachment} - */ -media.attachment = function( id ) { - return Attachment.get( id ); -}; - -/** - * A collection of all attachments that have been fetched from the server. - * - * @static - * @member {wp.media.model.Attachments} - */ -Attachments.all = new Attachments(); - -/** - * wp.media.query - * - * Shorthand for creating a new Attachments Query. - * - * @param {Object} [props] - * @return {wp.media.model.Attachments} - */ -media.query = function( props ) { - return new Attachments( null, { - props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } ) - }); -}; - -// Clean up. Prevents mobile browsers caching. -$(window).on('unload', function(){ - window.wp = null; -}); - - -/***/ }), - -/***/ "./src/js/media/models/attachment.js": +/***/ "0Ym0": /***/ (function(module, exports) { var $ = Backbone.$, @@ -513,7 +262,119 @@ module.exports = Attachment; /***/ }), -/***/ "./src/js/media/models/attachments.js": +/***/ 2: +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__("dx5j"); + + +/***/ }), + +/***/ "Io+g": +/***/ (function(module, exports) { + +var Attachments = wp.media.model.Attachments, + Selection; + +/** + * wp.media.model.Selection + * + * A selection of attachments. + * + * @memberOf wp.media.model + * + * @class + * @augments wp.media.model.Attachments + * @augments Backbone.Collection + */ +Selection = Attachments.extend(/** @lends wp.media.model.Selection.prototype */{ + /** + * Refresh the `single` model whenever the selection changes. + * Binds `single` instead of using the context argument to ensure + * it receives no parameters. + * + * @param {Array} [models=[]] Array of models used to populate the collection. + * @param {Object} [options={}] + */ + initialize: function( models, options ) { + /** + * call 'initialize' directly on the parent class + */ + Attachments.prototype.initialize.apply( this, arguments ); + this.multiple = options && options.multiple; + + this.on( 'add remove reset', _.bind( this.single, this, false ) ); + }, + + /** + * If the workflow does not support multi-select, clear out the selection + * before adding a new attachment to it. + * + * @param {Array} models + * @param {Object} options + * @return {wp.media.model.Attachment[]} + */ + add: function( models, options ) { + if ( ! this.multiple ) { + this.remove( this.models ); + } + /** + * call 'add' directly on the parent class + */ + return Attachments.prototype.add.call( this, models, options ); + }, + + /** + * Fired when toggling (clicking on) an attachment in the modal. + * + * @param {undefined|boolean|wp.media.model.Attachment} model + * + * @fires wp.media.model.Selection#selection:single + * @fires wp.media.model.Selection#selection:unsingle + * + * @return {Backbone.Model} + */ + single: function( model ) { + var previous = this._single; + + // If a `model` is provided, use it as the single model. + if ( model ) { + this._single = model; + } + // If the single model isn't in the selection, remove it. + if ( this._single && ! this.get( this._single.cid ) ) { + delete this._single; + } + + this._single = this._single || this.last(); + + // If single has changed, fire an event. + if ( this._single !== previous ) { + if ( previous ) { + previous.trigger( 'selection:unsingle', previous, this ); + + // If the model was already removed, trigger the collection + // event manually. + if ( ! this.get( previous.cid ) ) { + this.trigger( 'selection:unsingle', previous, this ); + } + } + if ( this._single ) { + this._single.trigger( 'selection:single', this._single, this ); + } + } + + // Return the single model, or the last model as a fallback. + return this._single; + } +}); + +module.exports = Selection; + + +/***/ }), + +/***/ "K0z/": /***/ (function(module, exports) { /** @@ -1100,168 +961,258 @@ module.exports = Attachments; /***/ }), -/***/ "./src/js/media/models/post-image.js": -/***/ (function(module, exports) { +/***/ "dx5j": +/***/ (function(module, exports, __webpack_require__) { /** - * wp.media.model.PostImage - * - * An instance of an image that's been embedded into a post. - * - * Used in the embedded image attachment display settings modal - @see wp.media.view.MediaFrame.ImageDetails. - * - * @memberOf wp.media.model - * - * @class - * @augments Backbone.Model - * - * @param {int} [attributes] Initial model attributes. - * @param {int} [attributes.attachment_id] ID of the attachment. - **/ -var PostImage = Backbone.Model.extend(/** @lends wp.media.model.PostImage.prototype */{ + * @output wp-includes/js/media-models.js + */ - initialize: function( attributes ) { - var Attachment = wp.media.model.Attachment; - this.attachment = false; +var $ = jQuery, + Attachment, Attachments, l10n, media; - if ( attributes.attachment_id ) { - this.attachment = Attachment.get( attributes.attachment_id ); - if ( this.attachment.get( 'url' ) ) { - this.dfd = jQuery.Deferred(); - this.dfd.resolve(); - } else { - this.dfd = this.attachment.fetch(); - } - this.bindAttachmentListeners(); +/** @namespace wp */ +window.wp = window.wp || {}; + +/** + * Create and return a media frame. + * + * Handles the default media experience. + * + * @alias wp.media + * @memberOf wp + * @namespace + * + * @param {Object} attributes The properties passed to the main media controller. + * @return {wp.media.view.MediaFrame} A media workflow. + */ +media = wp.media = function( attributes ) { + var MediaFrame = media.view.MediaFrame, + frame; + + if ( ! MediaFrame ) { + return; + } + + attributes = _.defaults( attributes || {}, { + frame: 'select' + }); + + if ( 'select' === attributes.frame && MediaFrame.Select ) { + frame = new MediaFrame.Select( attributes ); + } else if ( 'post' === attributes.frame && MediaFrame.Post ) { + frame = new MediaFrame.Post( attributes ); + } else if ( 'manage' === attributes.frame && MediaFrame.Manage ) { + frame = new MediaFrame.Manage( attributes ); + } else if ( 'image' === attributes.frame && MediaFrame.ImageDetails ) { + frame = new MediaFrame.ImageDetails( attributes ); + } else if ( 'audio' === attributes.frame && MediaFrame.AudioDetails ) { + frame = new MediaFrame.AudioDetails( attributes ); + } else if ( 'video' === attributes.frame && MediaFrame.VideoDetails ) { + frame = new MediaFrame.VideoDetails( attributes ); + } else if ( 'edit-attachments' === attributes.frame && MediaFrame.EditAttachments ) { + frame = new MediaFrame.EditAttachments( attributes ); + } + + delete attributes.frame; + + media.frame = frame; + + return frame; +}; + +/** @namespace wp.media.model */ +/** @namespace wp.media.view */ +/** @namespace wp.media.controller */ +/** @namespace wp.media.frames */ +_.extend( media, { model: {}, view: {}, controller: {}, frames: {} }); + +// Link any localized strings. +l10n = media.model.l10n = window._wpMediaModelsL10n || {}; + +// Link any settings. +media.model.settings = l10n.settings || {}; +delete l10n.settings; + +Attachment = media.model.Attachment = __webpack_require__( "0Ym0" ); +Attachments = media.model.Attachments = __webpack_require__( "K0z/" ); + +media.model.Query = __webpack_require__( "efdO" ); +media.model.PostImage = __webpack_require__( "r1z7" ); +media.model.Selection = __webpack_require__( "Io+g" ); + +/** + * ======================================================================== + * UTILITIES + * ======================================================================== + */ + +/** + * A basic equality comparator for Backbone models. + * + * Used to order models within a collection - @see wp.media.model.Attachments.comparator(). + * + * @param {mixed} a The primary parameter to compare. + * @param {mixed} b The primary parameter to compare. + * @param {string} ac The fallback parameter to compare, a's cid. + * @param {string} bc The fallback parameter to compare, b's cid. + * @return {number} -1: a should come before b. + * 0: a and b are of the same rank. + * 1: b should come before a. + */ +media.compare = function( a, b, ac, bc ) { + if ( _.isEqual( a, b ) ) { + return ac === bc ? 0 : (ac > bc ? -1 : 1); + } else { + return a > b ? -1 : 1; + } +}; + +_.extend( media, /** @lends wp.media */{ + /** + * media.template( id ) + * + * Fetch a JavaScript template for an id, and return a templating function for it. + * + * See wp.template() in `wp-includes/js/wp-util.js`. + * + * @borrows wp.template as template + */ + template: wp.template, + + /** + * media.post( [action], [data] ) + * + * Sends a POST request to WordPress. + * See wp.ajax.post() in `wp-includes/js/wp-util.js`. + * + * @borrows wp.ajax.post as post + */ + post: wp.ajax.post, + + /** + * media.ajax( [action], [options] ) + * + * Sends an XHR request to WordPress. + * See wp.ajax.send() in `wp-includes/js/wp-util.js`. + * + * @borrows wp.ajax.send as ajax + */ + ajax: wp.ajax.send, + + /** + * Scales a set of dimensions to fit within bounding dimensions. + * + * @param {Object} dimensions + * @return {Object} + */ + fit: function( dimensions ) { + var width = dimensions.width, + height = dimensions.height, + maxWidth = dimensions.maxWidth, + maxHeight = dimensions.maxHeight, + constraint; + + /* + * Compare ratios between the two values to determine + * which max to constrain by. If a max value doesn't exist, + * then the opposite side is the constraint. + */ + if ( ! _.isUndefined( maxWidth ) && ! _.isUndefined( maxHeight ) ) { + constraint = ( width / height > maxWidth / maxHeight ) ? 'width' : 'height'; + } else if ( _.isUndefined( maxHeight ) ) { + constraint = 'width'; + } else if ( _.isUndefined( maxWidth ) && height > maxHeight ) { + constraint = 'height'; } - // Keep URL in sync with changes to the type of link. - this.on( 'change:link', this.updateLinkUrl, this ); - this.on( 'change:size', this.updateSize, this ); - - this.setLinkTypeFromUrl(); - this.setAspectRatio(); - - this.set( 'originalUrl', attributes.url ); - }, - - bindAttachmentListeners: function() { - this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl ); - this.listenTo( this.attachment, 'sync', this.setAspectRatio ); - this.listenTo( this.attachment, 'change', this.updateSize ); - }, - - changeAttachment: function( attachment, props ) { - this.stopListening( this.attachment ); - this.attachment = attachment; - this.bindAttachmentListeners(); - - this.set( 'attachment_id', this.attachment.get( 'id' ) ); - this.set( 'caption', this.attachment.get( 'caption' ) ); - this.set( 'alt', this.attachment.get( 'alt' ) ); - this.set( 'size', props.get( 'size' ) ); - this.set( 'align', props.get( 'align' ) ); - this.set( 'link', props.get( 'link' ) ); - this.updateLinkUrl(); - this.updateSize(); - }, - - setLinkTypeFromUrl: function() { - var linkUrl = this.get( 'linkUrl' ), - type; - - if ( ! linkUrl ) { - this.set( 'link', 'none' ); - return; - } - - // Default to custom if there is a linkUrl. - type = 'custom'; - - if ( this.attachment ) { - if ( this.attachment.get( 'url' ) === linkUrl ) { - type = 'file'; - } else if ( this.attachment.get( 'link' ) === linkUrl ) { - type = 'post'; - } + // If the value of the constrained side is larger than the max, + // then scale the values. Otherwise return the originals; they fit. + if ( 'width' === constraint && width > maxWidth ) { + return { + width : maxWidth, + height: Math.round( maxWidth * height / width ) + }; + } else if ( 'height' === constraint && height > maxHeight ) { + return { + width : Math.round( maxHeight * width / height ), + height: maxHeight + }; } else { - if ( this.get( 'url' ) === linkUrl ) { - type = 'file'; - } - } - - this.set( 'link', type ); - }, - - updateLinkUrl: function() { - var link = this.get( 'link' ), - url; - - switch( link ) { - case 'file': - if ( this.attachment ) { - url = this.attachment.get( 'url' ); - } else { - url = this.get( 'url' ); - } - this.set( 'linkUrl', url ); - break; - case 'post': - this.set( 'linkUrl', this.attachment.get( 'link' ) ); - break; - case 'none': - this.set( 'linkUrl', '' ); - break; + return { + width : width, + height: height + }; } }, + /** + * Truncates a string by injecting an ellipsis into the middle. + * Useful for filenames. + * + * @param {string} string + * @param {number} [length=30] + * @param {string} [replacement=…] + * @return {string} The string, unless length is greater than string.length. + */ + truncate: function( string, length, replacement ) { + length = length || 30; + replacement = replacement || '…'; - updateSize: function() { - var size; - - if ( ! this.attachment ) { - return; + if ( string.length <= length ) { + return string; } - if ( this.get( 'size' ) === 'custom' ) { - this.set( 'width', this.get( 'customWidth' ) ); - this.set( 'height', this.get( 'customHeight' ) ); - this.set( 'url', this.get( 'originalUrl' ) ); - return; - } - - size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ]; - - if ( ! size ) { - return; - } - - this.set( 'url', size.url ); - this.set( 'width', size.width ); - this.set( 'height', size.height ); - }, - - setAspectRatio: function() { - var full; - - if ( this.attachment && this.attachment.get( 'sizes' ) ) { - full = this.attachment.get( 'sizes' ).full; - - if ( full ) { - this.set( 'aspectRatio', full.width / full.height ); - return; - } - } - - this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) ); + return string.substr( 0, length / 2 ) + replacement + string.substr( -1 * length / 2 ); } }); -module.exports = PostImage; +/** + * ======================================================================== + * MODELS + * ======================================================================== + */ +/** + * wp.media.attachment + * + * @static + * @param {string} id A string used to identify a model. + * @return {wp.media.model.Attachment} + */ +media.attachment = function( id ) { + return Attachment.get( id ); +}; + +/** + * A collection of all attachments that have been fetched from the server. + * + * @static + * @member {wp.media.model.Attachments} + */ +Attachments.all = new Attachments(); + +/** + * wp.media.query + * + * Shorthand for creating a new Attachments Query. + * + * @param {Object} [props] + * @return {wp.media.model.Attachments} + */ +media.query = function( props ) { + return new Attachments( null, { + props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } ) + }); +}; + +// Clean up. Prevents mobile browsers caching. +$(window).on('unload', function(){ + window.wp = null; +}); /***/ }), -/***/ "./src/js/media/models/query.js": +/***/ "efdO": /***/ (function(module, exports) { var Attachments = wp.media.model.Attachments, @@ -1574,114 +1525,163 @@ module.exports = Query; /***/ }), -/***/ "./src/js/media/models/selection.js": +/***/ "r1z7": /***/ (function(module, exports) { -var Attachments = wp.media.model.Attachments, - Selection; - /** - * wp.media.model.Selection + * wp.media.model.PostImage * - * A selection of attachments. + * An instance of an image that's been embedded into a post. + * + * Used in the embedded image attachment display settings modal - @see wp.media.view.MediaFrame.ImageDetails. * * @memberOf wp.media.model * * @class - * @augments wp.media.model.Attachments - * @augments Backbone.Collection - */ -Selection = Attachments.extend(/** @lends wp.media.model.Selection.prototype */{ - /** - * Refresh the `single` model whenever the selection changes. - * Binds `single` instead of using the context argument to ensure - * it receives no parameters. - * - * @param {Array} [models=[]] Array of models used to populate the collection. - * @param {Object} [options={}] - */ - initialize: function( models, options ) { - /** - * call 'initialize' directly on the parent class - */ - Attachments.prototype.initialize.apply( this, arguments ); - this.multiple = options && options.multiple; + * @augments Backbone.Model + * + * @param {int} [attributes] Initial model attributes. + * @param {int} [attributes.attachment_id] ID of the attachment. + **/ +var PostImage = Backbone.Model.extend(/** @lends wp.media.model.PostImage.prototype */{ - this.on( 'add remove reset', _.bind( this.single, this, false ) ); + initialize: function( attributes ) { + var Attachment = wp.media.model.Attachment; + this.attachment = false; + + if ( attributes.attachment_id ) { + this.attachment = Attachment.get( attributes.attachment_id ); + if ( this.attachment.get( 'url' ) ) { + this.dfd = jQuery.Deferred(); + this.dfd.resolve(); + } else { + this.dfd = this.attachment.fetch(); + } + this.bindAttachmentListeners(); + } + + // Keep URL in sync with changes to the type of link. + this.on( 'change:link', this.updateLinkUrl, this ); + this.on( 'change:size', this.updateSize, this ); + + this.setLinkTypeFromUrl(); + this.setAspectRatio(); + + this.set( 'originalUrl', attributes.url ); }, - /** - * If the workflow does not support multi-select, clear out the selection - * before adding a new attachment to it. - * - * @param {Array} models - * @param {Object} options - * @return {wp.media.model.Attachment[]} - */ - add: function( models, options ) { - if ( ! this.multiple ) { - this.remove( this.models ); - } - /** - * call 'add' directly on the parent class - */ - return Attachments.prototype.add.call( this, models, options ); + bindAttachmentListeners: function() { + this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl ); + this.listenTo( this.attachment, 'sync', this.setAspectRatio ); + this.listenTo( this.attachment, 'change', this.updateSize ); }, - /** - * Fired when toggling (clicking on) an attachment in the modal. - * - * @param {undefined|boolean|wp.media.model.Attachment} model - * - * @fires wp.media.model.Selection#selection:single - * @fires wp.media.model.Selection#selection:unsingle - * - * @return {Backbone.Model} - */ - single: function( model ) { - var previous = this._single; + changeAttachment: function( attachment, props ) { + this.stopListening( this.attachment ); + this.attachment = attachment; + this.bindAttachmentListeners(); - // If a `model` is provided, use it as the single model. - if ( model ) { - this._single = model; - } - // If the single model isn't in the selection, remove it. - if ( this._single && ! this.get( this._single.cid ) ) { - delete this._single; + this.set( 'attachment_id', this.attachment.get( 'id' ) ); + this.set( 'caption', this.attachment.get( 'caption' ) ); + this.set( 'alt', this.attachment.get( 'alt' ) ); + this.set( 'size', props.get( 'size' ) ); + this.set( 'align', props.get( 'align' ) ); + this.set( 'link', props.get( 'link' ) ); + this.updateLinkUrl(); + this.updateSize(); + }, + + setLinkTypeFromUrl: function() { + var linkUrl = this.get( 'linkUrl' ), + type; + + if ( ! linkUrl ) { + this.set( 'link', 'none' ); + return; } - this._single = this._single || this.last(); + // Default to custom if there is a linkUrl. + type = 'custom'; - // If single has changed, fire an event. - if ( this._single !== previous ) { - if ( previous ) { - previous.trigger( 'selection:unsingle', previous, this ); + if ( this.attachment ) { + if ( this.attachment.get( 'url' ) === linkUrl ) { + type = 'file'; + } else if ( this.attachment.get( 'link' ) === linkUrl ) { + type = 'post'; + } + } else { + if ( this.get( 'url' ) === linkUrl ) { + type = 'file'; + } + } - // If the model was already removed, trigger the collection - // event manually. - if ( ! this.get( previous.cid ) ) { - this.trigger( 'selection:unsingle', previous, this ); + this.set( 'link', type ); + }, + + updateLinkUrl: function() { + var link = this.get( 'link' ), + url; + + switch( link ) { + case 'file': + if ( this.attachment ) { + url = this.attachment.get( 'url' ); + } else { + url = this.get( 'url' ); } - } - if ( this._single ) { - this._single.trigger( 'selection:single', this._single, this ); + this.set( 'linkUrl', url ); + break; + case 'post': + this.set( 'linkUrl', this.attachment.get( 'link' ) ); + break; + case 'none': + this.set( 'linkUrl', '' ); + break; + } + }, + + updateSize: function() { + var size; + + if ( ! this.attachment ) { + return; + } + + if ( this.get( 'size' ) === 'custom' ) { + this.set( 'width', this.get( 'customWidth' ) ); + this.set( 'height', this.get( 'customHeight' ) ); + this.set( 'url', this.get( 'originalUrl' ) ); + return; + } + + size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ]; + + if ( ! size ) { + return; + } + + this.set( 'url', size.url ); + this.set( 'width', size.width ); + this.set( 'height', size.height ); + }, + + setAspectRatio: function() { + var full; + + if ( this.attachment && this.attachment.get( 'sizes' ) ) { + full = this.attachment.get( 'sizes' ).full; + + if ( full ) { + this.set( 'aspectRatio', full.width / full.height ); + return; } } - // Return the single model, or the last model as a fallback. - return this._single; + this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) ); } }); -module.exports = Selection; - - -/***/ }), - -/***/ 2: -/***/ (function(module, exports, __webpack_require__) { - -module.exports = __webpack_require__("./src/js/_enqueues/wp/media/models.js"); +module.exports = PostImage; /***/ }) diff --git a/wp-includes/js/media-models.min.js b/wp-includes/js/media-models.min.js index 0412e8f9c7..9077a52c0d 100644 --- a/wp-includes/js/media-models.min.js +++ b/wp-includes/js/media-models.min.js @@ -1,2 +1,2 @@ /*! This file is auto-generated */ -!function(i){var s={};function r(t){if(s[t])return s[t].exports;var e=s[t]={i:t,l:!1,exports:{}};return i[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}r.m=i,r.c=s,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)r.d(i,s,function(t){return e[t]}.bind(null,s));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=2)}({"./src/js/_enqueues/wp/media/models.js":function(t,e,i){var s,r,n,a,o=jQuery;window.wp=window.wp||{},a=wp.media=function(t){var e,i=a.view.MediaFrame;if(i)return"select"===(t=_.defaults(t||{},{frame:"select"})).frame&&i.Select?e=new i.Select(t):"post"===t.frame&&i.Post?e=new i.Post(t):"manage"===t.frame&&i.Manage?e=new i.Manage(t):"image"===t.frame&&i.ImageDetails?e=new i.ImageDetails(t):"audio"===t.frame&&i.AudioDetails?e=new i.AudioDetails(t):"video"===t.frame&&i.VideoDetails?e=new i.VideoDetails(t):"edit-attachments"===t.frame&&i.EditAttachments&&(e=new i.EditAttachments(t)),delete t.frame,a.frame=e},_.extend(a,{model:{},view:{},controller:{},frames:{}}),n=a.model.l10n=window._wpMediaModelsL10n||{},a.model.settings=n.settings||{},delete n.settings,s=a.model.Attachment=i("./src/js/media/models/attachment.js"),r=a.model.Attachments=i("./src/js/media/models/attachments.js"),a.model.Query=i("./src/js/media/models/query.js"),a.model.PostImage=i("./src/js/media/models/post-image.js"),a.model.Selection=i("./src/js/media/models/selection.js"),a.compare=function(t,e,i,s){return _.isEqual(t,e)?i===s?0:s=this.created)},i=["s","order","orderby","posts_per_page","post_mime_type","post_parent","author"],wp.Uploader&&_(this.args).chain().keys().difference(i).isEmpty().value()&&this.observe(wp.Uploader.queue)},hasMore:function(){return this._hasMore},more:function(t){var e=this;return this._more&&"pending"===this._more.state()?this._more:this.hasMore()?((t=t||{}).remove=!1,this._more=this.fetch(t).done(function(t){t=t.attachments;(_.isEmpty(t)||-1===this.args.posts_per_page||t.length=this.created)},i=["s","order","orderby","posts_per_page","post_mime_type","post_parent","author"],wp.Uploader&&_(this.args).chain().keys().difference(i).isEmpty().value()&&this.observe(wp.Uploader.queue)},hasMore:function(){return this._hasMore},more:function(t){var e=this;return this._more&&"pending"===this._more.state()?this._more:this.hasMore()?((t=t||{}).remove=!1,this._more=this.fetch(t).done(function(t){t=t.attachments;(_.isEmpty(t)||-1===this.args.posts_per_page||t.length' + dragInfoText + '' )[0], - priority: -40 - }) ); - } - - // Add the 'Reverse order' button to the toolbar. - attachmentsBrowserView.toolbar.set( 'reverse', { - text: l10n.reverseOrder, - priority: 80, - - click: function() { - library.reset( library.toArray().reverse() ); - } - }); - } -}); - -module.exports = CollectionEdit; - - -/***/ }), - -/***/ "./src/js/media/controllers/cropper.js": -/***/ (function(module, exports) { - -var l10n = wp.media.view.l10n, - Cropper; - -/** - * wp.media.controller.Cropper - * - * A class for cropping an image when called from the header media customization panel. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.State - * @augments Backbone.Model - */ -Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Cropper.prototype */{ - defaults: { - id: 'cropper', - title: l10n.cropImage, - // Region mode defaults. - toolbar: 'crop', - content: 'crop', - router: false, - canSkipCrop: false, - - // Default doCrop Ajax arguments to allow the Customizer (for example) to inject state. - doCropArgs: {} - }, - - /** - * Shows the crop image window when called from the Add new image button. - * - * @since 4.2.0 - * - * @return {void} - */ - activate: function() { - this.frame.on( 'content:create:crop', this.createCropContent, this ); - this.frame.on( 'close', this.removeCropper, this ); - this.set('selection', new Backbone.Collection(this.frame._selection.single)); - }, - - /** - * Changes the state of the toolbar window to browse mode. - * - * @since 4.2.0 - * - * @return {void} - */ - deactivate: function() { - this.frame.toolbar.mode('browse'); - }, - - /** - * Creates the crop image window. - * - * Initialized when clicking on the Select and Crop button. - * - * @since 4.2.0 - * - * @fires crop window - * - * @return {void} - */ - createCropContent: function() { - this.cropperView = new wp.media.view.Cropper({ - controller: this, - attachment: this.get('selection').first() - }); - this.cropperView.on('image-loaded', this.createCropToolbar, this); - this.frame.content.set(this.cropperView); - - }, - - /** - * Removes the image selection and closes the cropping window. - * - * @since 4.2.0 - * - * @return {void} - */ - removeCropper: function() { - this.imgSelect.cancelSelection(); - this.imgSelect.setOptions({remove: true}); - this.imgSelect.update(); - this.cropperView.remove(); - }, - - /** - * Checks if cropping can be skipped and creates crop toolbar accordingly. - * - * @since 4.2.0 - * - * @return {void} - */ - createCropToolbar: function() { - var canSkipCrop, toolbarOptions; - - canSkipCrop = this.get('canSkipCrop') || false; - - toolbarOptions = { - controller: this.frame, - items: { - insert: { - style: 'primary', - text: l10n.cropImage, - priority: 80, - requires: { library: false, selection: false }, - - click: function() { - var controller = this.controller, - selection; - - selection = controller.state().get('selection').first(); - selection.set({cropDetails: controller.state().imgSelect.getSelection()}); - - this.$el.text(l10n.cropping); - this.$el.attr('disabled', true); - - controller.state().doCrop( selection ).done( function( croppedImage ) { - controller.trigger('cropped', croppedImage ); - controller.close(); - }).fail( function() { - controller.trigger('content:error:crop'); - }); - } - } - } - }; - - if ( canSkipCrop ) { - _.extend( toolbarOptions.items, { - skip: { - style: 'secondary', - text: l10n.skipCropping, - priority: 70, - requires: { library: false, selection: false }, - click: function() { - var selection = this.controller.state().get('selection').first(); - this.controller.state().cropperView.remove(); - this.controller.trigger('skippedcrop', selection); - this.controller.close(); - } - } - }); - } - - this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) ); - }, - - /** - * Creates an object with the image attachment and crop properties. - * - * @since 4.2.0 - * - * @return {$.promise} A jQuery promise with the custom header crop details. - */ - doCrop: function( attachment ) { - return wp.ajax.post( 'custom-header-crop', _.extend( - {}, - this.defaults.doCropArgs, - { - nonce: attachment.get( 'nonces' ).edit, - id: attachment.get( 'id' ), - cropDetails: attachment.get( 'cropDetails' ) - } - ) ); - } -}); - -module.exports = Cropper; - - -/***/ }), - -/***/ "./src/js/media/controllers/customize-image-cropper.js": -/***/ (function(module, exports) { - -var Controller = wp.media.controller, - CustomizeImageCropper; - -/** - * A state for cropping an image in the customizer. - * - * @since 4.3.0 - * - * @constructs wp.media.controller.CustomizeImageCropper - * @memberOf wp.media.controller - * @augments wp.media.controller.CustomizeImageCropper.Cropper - * @inheritDoc - */ -CustomizeImageCropper = Controller.Cropper.extend(/** @lends wp.media.controller.CustomizeImageCropper.prototype */{ - /** - * Posts the crop details to the admin. - * - * Uses crop measurements when flexible in both directions. - * Constrains flexible side based on image ratio and size of the fixed side. - * - * @since 4.3.0 - * - * @param {Object} attachment The attachment to crop. - * - * @return {$.promise} A jQuery promise that represents the crop image request. - */ - doCrop: function( attachment ) { - var cropDetails = attachment.get( 'cropDetails' ), - control = this.get( 'control' ), - ratio = cropDetails.width / cropDetails.height; - - // Use crop measurements when flexible in both directions. - if ( control.params.flex_width && control.params.flex_height ) { - cropDetails.dst_width = cropDetails.width; - cropDetails.dst_height = cropDetails.height; - - // Constrain flexible side based on image ratio and size of the fixed side. - } else { - cropDetails.dst_width = control.params.flex_width ? control.params.height * ratio : control.params.width; - cropDetails.dst_height = control.params.flex_height ? control.params.width / ratio : control.params.height; - } - - return wp.ajax.post( 'crop-image', { - wp_customize: 'on', - nonce: attachment.get( 'nonces' ).edit, - id: attachment.get( 'id' ), - context: control.id, - cropDetails: cropDetails - } ); - } -}); - -module.exports = CustomizeImageCropper; - - -/***/ }), - -/***/ "./src/js/media/controllers/edit-image.js": -/***/ (function(module, exports) { - -var l10n = wp.media.view.l10n, - EditImage; - -/** - * wp.media.controller.EditImage - * - * A state for editing (cropping, etc.) an image. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.State - * @augments Backbone.Model - * - * @param {object} attributes The attributes hash passed to the state. - * @param {wp.media.model.Attachment} attributes.model The attachment. - * @param {string} [attributes.id=edit-image] Unique identifier. - * @param {string} [attributes.title=Edit Image] Title for the state. Displays in the media menu and the frame's title region. - * @param {string} [attributes.content=edit-image] Initial mode for the content region. - * @param {string} [attributes.toolbar=edit-image] Initial mode for the toolbar region. - * @param {string} [attributes.menu=false] Initial mode for the menu region. - * @param {string} [attributes.url] Unused. @todo Consider removal. - */ -EditImage = wp.media.controller.State.extend(/** @lends wp.media.controller.EditImage.prototype */{ - defaults: { - id: 'edit-image', - title: l10n.editImage, - menu: false, - toolbar: 'edit-image', - content: 'edit-image', - url: '' - }, - - /** - * Activates a frame for editing a featured image. - * - * @since 3.9.0 - * - * @return {void} - */ - activate: function() { - this.frame.on( 'toolbar:render:edit-image', _.bind( this.toolbar, this ) ); - }, - - /** - * Deactivates a frame for editing a featured image. - * - * @since 3.9.0 - * - * @return {void} - */ - deactivate: function() { - this.frame.off( 'toolbar:render:edit-image' ); - }, - - /** - * Adds a toolbar with a back button. - * - * When the back button is pressed it checks whether there is a previous state. - * In case there is a previous state it sets that previous state otherwise it - * closes the frame. - * - * @since 3.9.0 - * - * @return {void} - */ - toolbar: function() { - var frame = this.frame, - lastState = frame.lastState(), - previous = lastState && lastState.id; - - frame.toolbar.set( new wp.media.view.Toolbar({ - controller: frame, - items: { - back: { - style: 'primary', - text: l10n.back, - priority: 20, - click: function() { - if ( previous ) { - frame.setState( previous ); - } else { - frame.close(); - } - } - } - } - }) ); - } -}); - -module.exports = EditImage; - - -/***/ }), - -/***/ "./src/js/media/controllers/embed.js": -/***/ (function(module, exports) { - -var l10n = wp.media.view.l10n, - $ = Backbone.$, - Embed; - -/** - * wp.media.controller.Embed - * - * A state for embedding media from a URL. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.State - * @augments Backbone.Model - * - * @param {object} attributes The attributes hash passed to the state. - * @param {string} [attributes.id=embed] Unique identifier. - * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region. - * @param {string} [attributes.content=embed] Initial mode for the content region. - * @param {string} [attributes.menu=default] Initial mode for the menu region. - * @param {string} [attributes.toolbar=main-embed] Initial mode for the toolbar region. - * @param {string} [attributes.menu=false] Initial mode for the menu region. - * @param {int} [attributes.priority=120] The priority for the state link in the media menu. - * @param {string} [attributes.type=link] The type of embed. Currently only link is supported. - * @param {string} [attributes.url] The embed URL. - * @param {object} [attributes.metadata={}] Properties of the embed, which will override attributes.url if set. - */ -Embed = wp.media.controller.State.extend(/** @lends wp.media.controller.Embed.prototype */{ - defaults: { - id: 'embed', - title: l10n.insertFromUrlTitle, - content: 'embed', - menu: 'default', - toolbar: 'main-embed', - priority: 120, - type: 'link', - url: '', - metadata: {} - }, - - // The amount of time used when debouncing the scan. - sensitivity: 400, - - initialize: function(options) { - this.metadata = options.metadata; - this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity ); - this.props = new Backbone.Model( this.metadata || { url: '' }); - this.props.on( 'change:url', this.debouncedScan, this ); - this.props.on( 'change:url', this.refresh, this ); - this.on( 'scan', this.scanImage, this ); - }, - - /** - * Trigger a scan of the embedded URL's content for metadata required to embed. - * - * @fires wp.media.controller.Embed#scan - */ - scan: function() { - var scanners, - embed = this, - attributes = { - type: 'link', - scanners: [] - }; - - /* - * Scan is triggered with the list of `attributes` to set on the - * state, useful for the 'type' attribute and 'scanners' attribute, - * an array of promise objects for asynchronous scan operations. - */ - if ( this.props.get('url') ) { - this.trigger( 'scan', attributes ); - } - - if ( attributes.scanners.length ) { - scanners = attributes.scanners = $.when.apply( $, attributes.scanners ); - scanners.always( function() { - if ( embed.get('scanners') === scanners ) { - embed.set( 'loading', false ); - } - }); - } else { - attributes.scanners = null; - } - - attributes.loading = !! attributes.scanners; - this.set( attributes ); - }, - /** - * Try scanning the embed as an image to discover its dimensions. - * - * @param {Object} attributes - */ - scanImage: function( attributes ) { - var frame = this.frame, - state = this, - url = this.props.get('url'), - image = new Image(), - deferred = $.Deferred(); - - attributes.scanners.push( deferred.promise() ); - - // Try to load the image and find its width/height. - image.onload = function() { - deferred.resolve(); - - if ( state !== frame.state() || url !== state.props.get('url') ) { - return; - } - - state.set({ - type: 'image' - }); - - state.props.set({ - width: image.width, - height: image.height - }); - }; - - image.onerror = deferred.reject; - image.src = url; - }, - - refresh: function() { - this.frame.toolbar.get().refresh(); - }, - - reset: function() { - this.props.clear().set({ url: '' }); - - if ( this.active ) { - this.refresh(); - } - } -}); - -module.exports = Embed; - - -/***/ }), - -/***/ "./src/js/media/controllers/featured-image.js": -/***/ (function(module, exports) { - -var Attachment = wp.media.model.Attachment, - Library = wp.media.controller.Library, - l10n = wp.media.view.l10n, - FeaturedImage; - -/** - * wp.media.controller.FeaturedImage - * - * A state for selecting a featured image for a post. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.Library - * @augments wp.media.controller.State - * @augments Backbone.Model - * - * @param {object} [attributes] The attributes hash passed to the state. - * @param {string} [attributes.id=featured-image] Unique identifier. - * @param {string} [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region. - * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. - * If one is not supplied, a collection of all images will be created. - * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. - * @param {string} [attributes.content=upload] Initial mode for the content region. - * Overridden by persistent user setting if 'contentUserSetting' is true. - * @param {string} [attributes.menu=default] Initial mode for the menu region. - * @param {string} [attributes.router=browse] Initial mode for the router region. - * @param {string} [attributes.toolbar=featured-image] Initial mode for the toolbar region. - * @param {int} [attributes.priority=60] The priority for the state link in the media menu. - * @param {boolean} [attributes.searchable=true] Whether the library is searchable. - * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown. - * Accepts 'all', 'uploaded', or 'unattached'. - * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. - * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. - * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. - * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. - * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. - */ -FeaturedImage = Library.extend(/** @lends wp.media.controller.FeaturedImage.prototype */{ - defaults: _.defaults({ - id: 'featured-image', - title: l10n.setFeaturedImageTitle, - multiple: false, - filterable: 'uploaded', - toolbar: 'featured-image', - priority: 60, - syncSelection: true - }, Library.prototype.defaults ), - - /** - * @since 3.5.0 - */ - initialize: function() { - var library, comparator; - - // If we haven't been provided a `library`, create a `Selection`. - if ( ! this.get('library') ) { - this.set( 'library', wp.media.query({ type: 'image' }) ); - } - - Library.prototype.initialize.apply( this, arguments ); - - library = this.get('library'); - comparator = library.comparator; - - // Overload the library's comparator to push items that are not in - // the mirrored query to the front of the aggregate collection. - library.comparator = function( a, b ) { - var aInQuery = !! this.mirroring.get( a.cid ), - bInQuery = !! this.mirroring.get( b.cid ); - - if ( ! aInQuery && bInQuery ) { - return -1; - } else if ( aInQuery && ! bInQuery ) { - return 1; - } else { - return comparator.apply( this, arguments ); - } - }; - - // Add all items in the selection to the library, so any featured - // images that are not initially loaded still appear. - library.observe( this.get('selection') ); - }, - - /** - * @since 3.5.0 - */ - activate: function() { - this.updateSelection(); - this.frame.on( 'open', this.updateSelection, this ); - - Library.prototype.activate.apply( this, arguments ); - }, - - /** - * @since 3.5.0 - */ - deactivate: function() { - this.frame.off( 'open', this.updateSelection, this ); - - Library.prototype.deactivate.apply( this, arguments ); - }, - - /** - * @since 3.5.0 - */ - updateSelection: function() { - var selection = this.get('selection'), - id = wp.media.view.settings.post.featuredImageId, - attachment; - - if ( '' !== id && -1 !== id ) { - attachment = Attachment.get( id ); - attachment.fetch(); - } - - selection.reset( attachment ? [ attachment ] : [] ); - } -}); - -module.exports = FeaturedImage; - - -/***/ }), - -/***/ "./src/js/media/controllers/gallery-add.js": -/***/ (function(module, exports) { - -var Selection = wp.media.model.Selection, - Library = wp.media.controller.Library, - l10n = wp.media.view.l10n, - GalleryAdd; - -/** - * wp.media.controller.GalleryAdd - * - * A state for selecting more images to add to a gallery. - * - * @since 3.5.0 - * - * @class - * @augments wp.media.controller.Library - * @augments wp.media.controller.State - * @augments Backbone.Model - * - * @memberof wp.media.controller - * - * @param {Object} [attributes] The attributes hash passed to the state. - * @param {string} [attributes.id=gallery-library] Unique identifier. - * @param {string} [attributes.title=Add to Gallery] Title for the state. Displays in the frame's title region. - * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean. - * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. - * If one is not supplied, a collection of all images will be created. - * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown. - * Accepts 'all', 'uploaded', or 'unattached'. - * @param {string} [attributes.menu=gallery] Initial mode for the menu region. - * @param {string} [attributes.content=upload] Initial mode for the content region. - * Overridden by persistent user setting if 'contentUserSetting' is true. - * @param {string} [attributes.router=browse] Initial mode for the router region. - * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region. - * @param {boolean} [attributes.searchable=true] Whether the library is searchable. - * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. - * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. - * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. - * @param {number} [attributes.priority=100] The priority for the state link in the media menu. - * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. - * Defaults to false because for this state, because the library of the Edit Gallery state is the selection. - */ -GalleryAdd = Library.extend(/** @lends wp.media.controller.GalleryAdd.prototype */{ - defaults: _.defaults({ - id: 'gallery-library', - title: l10n.addToGalleryTitle, - multiple: 'add', - filterable: 'uploaded', - menu: 'gallery', - toolbar: 'gallery-add', - priority: 100, - syncSelection: false - }, Library.prototype.defaults ), - - /** - * Initializes the library. Creates a library of images if a library isn't supplied. - * - * @since 3.5.0 - * - * @return {void} - */ - initialize: function() { - if ( ! this.get('library') ) { - this.set( 'library', wp.media.query({ type: 'image' }) ); - } - - Library.prototype.initialize.apply( this, arguments ); - }, - - /** - * Activates the library. - * - * Removes all event listeners if in edit mode. Creates a validator to check an attachment. - * Resets library and re-enables event listeners. Activates edit mode. Calls the parent's activate method. - * - * @since 3.5.0 - * - * @return {void} - */ - activate: function() { - var library = this.get('library'), - edit = this.frame.state('gallery-edit').get('library'); - - if ( this.editLibrary && this.editLibrary !== edit ) { - library.unobserve( this.editLibrary ); - } - - /* - * Accept attachments that exist in the original library but - * that do not exist in gallery's library yet. - */ - library.validator = function( attachment ) { - return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments ); - }; - - /* - * Reset the library to ensure that all attachments are re-added - * to the collection. Do so silently, as calling `observe` will - * trigger the `reset` event. - */ - library.reset( library.mirroring.models, { silent: true }); - library.observe( edit ); - this.editLibrary = edit; - - Library.prototype.activate.apply( this, arguments ); - } -}); - -module.exports = GalleryAdd; - - -/***/ }), - -/***/ "./src/js/media/controllers/gallery-edit.js": -/***/ (function(module, exports) { - -var Library = wp.media.controller.Library, - l10n = wp.media.view.l10n, - GalleryEdit; - -/** - * wp.media.controller.GalleryEdit - * - * A state for editing a gallery's images and settings. - * - * @since 3.5.0 - * - * @class - * @augments wp.media.controller.Library - * @augments wp.media.controller.State - * @augments Backbone.Model - * - * @memberOf wp.media.controller - * - * @param {Object} [attributes] The attributes hash passed to the state. - * @param {string} [attributes.id=gallery-edit] Unique identifier. - * @param {string} [attributes.title=Edit Gallery] Title for the state. Displays in the frame's title region. - * @param {wp.media.model.Attachments} [attributes.library] The collection of attachments in the gallery. - * If one is not supplied, an empty media.model.Selection collection is created. - * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. - * @param {boolean} [attributes.searchable=false] Whether the library is searchable. - * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. - * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar. - * @param {string|false} [attributes.content=browse] Initial mode for the content region. - * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region. - * @param {boolean} [attributes.describe=true] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. - * @param {boolean} [attributes.displaySettings=true] Whether to show the attachment display settings interface. - * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable. - * @param {number} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments. - * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance. - * @param {number} [attributes.priority=60] The priority for the state link in the media menu. - * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. - * Defaults to false for this state, because the library passed in *is* the selection. - * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`. - * If none supplied, defaults to wp.media.view.Attachment.EditLibrary. - */ -GalleryEdit = Library.extend(/** @lends wp.media.controller.GalleryEdit.prototype */{ - defaults: { - id: 'gallery-edit', - title: l10n.editGalleryTitle, - multiple: false, - searchable: false, - sortable: true, - date: false, - display: false, - content: 'browse', - toolbar: 'gallery-edit', - describe: true, - displaySettings: true, - dragInfo: true, - idealColumnWidth: 170, - editing: false, - priority: 60, - syncSelection: false - }, - - /** - * Initializes the library. - * - * Creates a selection if a library isn't supplied and creates an attachment - * view if no attachment view is supplied. - * - * @since 3.5.0 - * - * @return {void} - */ - initialize: function() { - // If we haven't been provided a `library`, create a `Selection`. - if ( ! this.get('library') ) { - this.set( 'library', new wp.media.model.Selection() ); - } - - // The single `Attachment` view to be used in the `Attachments` view. - if ( ! this.get('AttachmentView') ) { - this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary ); - } - - Library.prototype.initialize.apply( this, arguments ); - }, - - /** - * Activates the library. - * - * Limits the library to images, watches for uploaded attachments. Watches for - * the browse event on the frame and binds it to gallerySettings. - * - * @since 3.5.0 - * - * @return {void} - */ - activate: function() { - var library = this.get('library'); - - // Limit the library to images only. - library.props.set( 'type', 'image' ); - - // Watch for uploaded attachments. - this.get('library').observe( wp.Uploader.queue ); - - this.frame.on( 'content:render:browse', this.gallerySettings, this ); - - Library.prototype.activate.apply( this, arguments ); - }, - - /** - * Deactivates the library. - * - * Stops watching for uploaded attachments and browse events. - * - * @since 3.5.0 - * - * @return {void} - */ - deactivate: function() { - // Stop watching for uploaded attachments. - this.get('library').unobserve( wp.Uploader.queue ); - - this.frame.off( 'content:render:browse', this.gallerySettings, this ); - - Library.prototype.deactivate.apply( this, arguments ); - }, - - /** - * Adds the gallery settings to the sidebar and adds a reverse button to the - * toolbar. - * - * @since 3.5.0 - * - * @param {wp.media.view.Frame} browser The file browser. - * - * @return {void} - */ - gallerySettings: function( browser ) { - if ( ! this.get('displaySettings') ) { - return; - } - - var library = this.get('library'); - - if ( ! library || ! browser ) { - return; - } - - library.gallery = library.gallery || new Backbone.Model(); - - browser.sidebar.set({ - gallery: new wp.media.view.Settings.Gallery({ - controller: this, - model: library.gallery, - priority: 40 - }) - }); - - browser.toolbar.set( 'reverse', { - text: l10n.reverseOrder, - priority: 80, - - click: function() { - library.reset( library.toArray().reverse() ); - } - }); - } -}); - -module.exports = GalleryEdit; - - -/***/ }), - -/***/ "./src/js/media/controllers/image-details.js": -/***/ (function(module, exports) { - -var State = wp.media.controller.State, - Library = wp.media.controller.Library, - l10n = wp.media.view.l10n, - ImageDetails; - -/** - * wp.media.controller.ImageDetails - * - * A state for editing the attachment display settings of an image that's been - * inserted into the editor. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.State - * @augments Backbone.Model - * - * @param {object} [attributes] The attributes hash passed to the state. - * @param {string} [attributes.id=image-details] Unique identifier. - * @param {string} [attributes.title=Image Details] Title for the state. Displays in the frame's title region. - * @param {wp.media.model.Attachment} attributes.image The image's model. - * @param {string|false} [attributes.content=image-details] Initial mode for the content region. - * @param {string|false} [attributes.menu=false] Initial mode for the menu region. - * @param {string|false} [attributes.router=false] Initial mode for the router region. - * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region. - * @param {boolean} [attributes.editing=false] Unused. - * @param {int} [attributes.priority=60] Unused. - * - * @todo This state inherits some defaults from media.controller.Library.prototype.defaults, - * however this may not do anything. - */ -ImageDetails = State.extend(/** @lends wp.media.controller.ImageDetails.prototype */{ - defaults: _.defaults({ - id: 'image-details', - title: l10n.imageDetailsTitle, - content: 'image-details', - menu: false, - router: false, - toolbar: 'image-details', - editing: false, - priority: 60 - }, Library.prototype.defaults ), - - /** - * @since 3.9.0 - * - * @param options Attributes - */ - initialize: function( options ) { - this.image = options.image; - State.prototype.initialize.apply( this, arguments ); - }, - - /** - * @since 3.9.0 - */ - activate: function() { - this.frame.modal.$el.addClass('image-details'); - } -}); - -module.exports = ImageDetails; - - -/***/ }), - -/***/ "./src/js/media/controllers/library.js": -/***/ (function(module, exports) { - -var l10n = wp.media.view.l10n, - getUserSetting = window.getUserSetting, - setUserSetting = window.setUserSetting, - Library; - -/** - * wp.media.controller.Library - * - * A state for choosing an attachment or group of attachments from the media library. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.State - * @augments Backbone.Model - * @mixes media.selectionSync - * - * @param {object} [attributes] The attributes hash passed to the state. - * @param {string} [attributes.id=library] Unique identifier. - * @param {string} [attributes.title=Media library] Title for the state. Displays in the media menu and the frame's title region. - * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. - * If one is not supplied, a collection of all attachments will be created. - * @param {wp.media.model.Selection|object} [attributes.selection] A collection to contain attachment selections within the state. - * If the 'selection' attribute is a plain JS object, - * a Selection will be created using its values as the selection instance's `props` model. - * Otherwise, it will copy the library's `props` model. - * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. - * @param {string} [attributes.content=upload] Initial mode for the content region. - * Overridden by persistent user setting if 'contentUserSetting' is true. - * @param {string} [attributes.menu=default] Initial mode for the menu region. - * @param {string} [attributes.router=browse] Initial mode for the router region. - * @param {string} [attributes.toolbar=select] Initial mode for the toolbar region. - * @param {boolean} [attributes.searchable=true] Whether the library is searchable. - * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown. - * Accepts 'all', 'uploaded', or 'unattached'. - * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. - * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. - * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. - * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. - * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. - */ -Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Library.prototype */{ - defaults: { - id: 'library', - title: l10n.mediaLibraryTitle, - multiple: false, - content: 'upload', - menu: 'default', - router: 'browse', - toolbar: 'select', - searchable: true, - filterable: false, - sortable: true, - autoSelect: true, - describe: false, - contentUserSetting: true, - syncSelection: true - }, - - /** - * If a library isn't provided, query all media items. - * If a selection instance isn't provided, create one. - * - * @since 3.5.0 - */ - initialize: function() { - var selection = this.get('selection'), - props; - - if ( ! this.get('library') ) { - this.set( 'library', wp.media.query() ); - } - - if ( ! ( selection instanceof wp.media.model.Selection ) ) { - props = selection; - - if ( ! props ) { - props = this.get('library').props.toJSON(); - props = _.omit( props, 'orderby', 'query' ); - } - - this.set( 'selection', new wp.media.model.Selection( null, { - multiple: this.get('multiple'), - props: props - }) ); - } - - this.resetDisplays(); - }, - - /** - * @since 3.5.0 - */ - activate: function() { - this.syncSelection(); - - wp.Uploader.queue.on( 'add', this.uploading, this ); - - this.get('selection').on( 'add remove reset', this.refreshContent, this ); - - if ( this.get( 'router' ) && this.get('contentUserSetting') ) { - this.frame.on( 'content:activate', this.saveContentMode, this ); - this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) ); - } - }, - - /** - * @since 3.5.0 - */ - deactivate: function() { - this.recordSelection(); - - this.frame.off( 'content:activate', this.saveContentMode, this ); - - // Unbind all event handlers that use this state as the context - // from the selection. - this.get('selection').off( null, null, this ); - - wp.Uploader.queue.off( null, null, this ); - }, - - /** - * Reset the library to its initial state. - * - * @since 3.5.0 - */ - reset: function() { - this.get('selection').reset(); - this.resetDisplays(); - this.refreshContent(); - }, - - /** - * Reset the attachment display settings defaults to the site options. - * - * If site options don't define them, fall back to a persistent user setting. - * - * @since 3.5.0 - */ - resetDisplays: function() { - var defaultProps = wp.media.view.settings.defaultProps; - this._displays = []; - this._defaultDisplaySettings = { - align: getUserSetting( 'align', defaultProps.align ) || 'none', - size: getUserSetting( 'imgsize', defaultProps.size ) || 'medium', - link: getUserSetting( 'urlbutton', defaultProps.link ) || 'none' - }; - }, - - /** - * Create a model to represent display settings (alignment, etc.) for an attachment. - * - * @since 3.5.0 - * - * @param {wp.media.model.Attachment} attachment - * @return {Backbone.Model} - */ - display: function( attachment ) { - var displays = this._displays; - - if ( ! displays[ attachment.cid ] ) { - displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) ); - } - return displays[ attachment.cid ]; - }, - - /** - * Given an attachment, create attachment display settings properties. - * - * @since 3.6.0 - * - * @param {wp.media.model.Attachment} attachment - * @return {Object} - */ - defaultDisplaySettings: function( attachment ) { - var settings = _.clone( this._defaultDisplaySettings ); - - settings.canEmbed = this.canEmbed( attachment ); - if ( settings.canEmbed ) { - settings.link = 'embed'; - } else if ( ! this.isImageAttachment( attachment ) && settings.link === 'none' ) { - settings.link = 'file'; - } - - return settings; - }, - - /** - * Whether an attachment is image. - * - * @since 4.4.1 - * - * @param {wp.media.model.Attachment} attachment - * @return {boolean} - */ - isImageAttachment: function( attachment ) { - // If uploading, we know the filename but not the mime type. - if ( attachment.get('uploading') ) { - return /\.(jpe?g|png|gif)$/i.test( attachment.get('filename') ); - } - - return attachment.get('type') === 'image'; - }, - - /** - * Whether an attachment can be embedded (audio or video). - * - * @since 3.6.0 - * - * @param {wp.media.model.Attachment} attachment - * @return {boolean} - */ - canEmbed: function( attachment ) { - // If uploading, we know the filename but not the mime type. - if ( ! attachment.get('uploading') ) { - var type = attachment.get('type'); - if ( type !== 'audio' && type !== 'video' ) { - return false; - } - } - - return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() ); - }, - - - /** - * If the state is active, no items are selected, and the current - * content mode is not an option in the state's router (provided - * the state has a router), reset the content mode to the default. - * - * @since 3.5.0 - */ - refreshContent: function() { - var selection = this.get('selection'), - frame = this.frame, - router = frame.router.get(), - mode = frame.content.mode(); - - if ( this.active && ! selection.length && router && ! router.get( mode ) ) { - this.frame.content.render( this.get('content') ); - } - }, - - /** - * Callback handler when an attachment is uploaded. - * - * Switch to the Media Library if uploaded from the 'Upload Files' tab. - * - * Adds any uploading attachments to the selection. - * - * If the state only supports one attachment to be selected and multiple - * attachments are uploaded, the last attachment in the upload queue will - * be selected. - * - * @since 3.5.0 - * - * @param {wp.media.model.Attachment} attachment - */ - uploading: function( attachment ) { - var content = this.frame.content; - - if ( 'upload' === content.mode() ) { - this.frame.content.mode('browse'); - } - - if ( this.get( 'autoSelect' ) ) { - this.get('selection').add( attachment ); - this.frame.trigger( 'library:selection:add' ); - } - }, - - /** - * Persist the mode of the content region as a user setting. - * - * @since 3.5.0 - */ - saveContentMode: function() { - if ( 'browse' !== this.get('router') ) { - return; - } - - var mode = this.frame.content.mode(), - view = this.frame.router.get(); - - if ( view && view.get( mode ) ) { - setUserSetting( 'libraryContent', mode ); - } - } - -}); - -// Make selectionSync available on any Media Library state. -_.extend( Library.prototype, wp.media.selectionSync ); - -module.exports = Library; - - -/***/ }), - -/***/ "./src/js/media/controllers/media-library.js": -/***/ (function(module, exports) { - -/** - * wp.media.controller.MediaLibrary - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.Library - * @augments wp.media.controller.State - * @augments Backbone.Model - */ -var Library = wp.media.controller.Library, - MediaLibrary; - -MediaLibrary = Library.extend(/** @lends wp.media.controller.MediaLibrary.prototype */{ - defaults: _.defaults({ - // Attachments browser defaults. @see media.view.AttachmentsBrowser - filterable: 'uploaded', - - displaySettings: false, - priority: 80, - syncSelection: false - }, Library.prototype.defaults ), - - /** - * @since 3.9.0 - * - * @param options - */ - initialize: function( options ) { - this.media = options.media; - this.type = options.type; - this.set( 'library', wp.media.query({ type: this.type }) ); - - Library.prototype.initialize.apply( this, arguments ); - }, - - /** - * @since 3.9.0 - */ - activate: function() { - // @todo this should use this.frame. - if ( wp.media.frame.lastMime ) { - this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) ); - delete wp.media.frame.lastMime; - } - Library.prototype.activate.apply( this, arguments ); - } -}); - -module.exports = MediaLibrary; - - -/***/ }), - -/***/ "./src/js/media/controllers/region.js": -/***/ (function(module, exports) { - -/** - * wp.media.controller.Region - * - * A region is a persistent application layout area. - * - * A region assumes one mode at any time, and can be switched to another. - * - * When mode changes, events are triggered on the region's parent view. - * The parent view will listen to specific events and fill the region with an - * appropriate view depending on mode. For example, a frame listens for the - * 'browse' mode t be activated on the 'content' view and then fills the region - * with an AttachmentsBrowser view. - * - * @memberOf wp.media.controller - * - * @class - * - * @param {Object} options Options hash for the region. - * @param {string} options.id Unique identifier for the region. - * @param {Backbone.View} options.view A parent view the region exists within. - * @param {string} options.selector jQuery selector for the region within the parent view. - */ -var Region = function( options ) { - _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) ); -}; - -// Use Backbone's self-propagating `extend` inheritance method. -Region.extend = Backbone.Model.extend; - -_.extend( Region.prototype,/** @lends wp.media.controller.Region.prototype */{ - /** - * Activate a mode. - * - * @since 3.5.0 - * - * @param {string} mode - * - * @fires Region#activate - * @fires Region#deactivate - * - * @return {wp.media.controller.Region} Returns itself to allow chaining. - */ - mode: function( mode ) { - if ( ! mode ) { - return this._mode; - } - // Bail if we're trying to change to the current mode. - if ( mode === this._mode ) { - return this; - } - /** - * Region mode deactivation event. - * - * @event wp.media.controller.Region#deactivate + * Call `initialize` directly on parent class with passed arguments */ - this.trigger('deactivate'); - - this._mode = mode; - this.render( mode ); - - /** - * Region mode activation event. - * - * @event wp.media.controller.Region#activate - */ - this.trigger('activate'); - return this; - }, - /** - * Render a mode. - * - * @since 3.5.0 - * - * @param {string} mode - * - * @fires Region#create - * @fires Region#render - * - * @return {wp.media.controller.Region} Returns itself to allow chaining. - */ - render: function( mode ) { - // If the mode isn't active, activate it. - if ( mode && mode !== this._mode ) { - return this.mode( mode ); - } - - var set = { view: null }, - view; - - /** - * Create region view event. - * - * Region view creation takes place in an event callback on the frame. - * - * @event wp.media.controller.Region#create - * @type {object} - * @property {object} view - */ - this.trigger( 'create', set ); - view = set.view; - - /** - * Render region view event. - * - * Region view creation takes place in an event callback on the frame. - * - * @event wp.media.controller.Region#render - * @type {object} - */ - this.trigger( 'render', view ); - if ( view ) { - this.set( view ); - } - return this; + AttachmentDisplay.prototype.initialize.apply( this, arguments ); + this.listenTo( this.model, 'change:url', this.updateImage ); }, - /** - * Get the region's view. - * - * @since 3.5.0 - * - * @return {wp.media.View} - */ - get: function() { - return this.view.views.first( this.selector ); - }, - - /** - * Set the region's view as a subview of the frame. - * - * @since 3.5.0 - * - * @param {Array|Object} views - * @param {Object} [options={}] - * @return {wp.Backbone.Subviews} Subviews is returned to allow chaining. - */ - set: function( views, options ) { - if ( options ) { - options.add = false; - } - return this.view.views.set( this.selector, views, options ); - }, - - /** - * Trigger regional view events on the frame. - * - * @since 3.5.0 - * - * @param {string} event - * @return {undefined|wp.media.controller.Region} Returns itself to allow chaining. - */ - trigger: function( event ) { - var base, args; - - if ( ! this._mode ) { - return; - } - - args = _.toArray( arguments ); - base = this.id + ':' + event; - - // Trigger `{this.id}:{event}:{this._mode}` event on the frame. - args[0] = base + ':' + this._mode; - this.view.trigger.apply( this.view, args ); - - // Trigger `{this.id}:{event}` event on the frame. - args[0] = base; - this.view.trigger.apply( this.view, args ); - return this; + updateImage: function() { + this.$('img').attr( 'src', this.model.get('url') ); } }); -module.exports = Region; +module.exports = EmbedImage; /***/ }), -/***/ "./src/js/media/controllers/replace-image.js": -/***/ (function(module, exports) { - -var Library = wp.media.controller.Library, - l10n = wp.media.view.l10n, - ReplaceImage; - -/** - * wp.media.controller.ReplaceImage - * - * A state for replacing an image. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.Library - * @augments wp.media.controller.State - * @augments Backbone.Model - * - * @param {object} [attributes] The attributes hash passed to the state. - * @param {string} [attributes.id=replace-image] Unique identifier. - * @param {string} [attributes.title=Replace Image] Title for the state. Displays in the media menu and the frame's title region. - * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. - * If one is not supplied, a collection of all images will be created. - * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. - * @param {string} [attributes.content=upload] Initial mode for the content region. - * Overridden by persistent user setting if 'contentUserSetting' is true. - * @param {string} [attributes.menu=default] Initial mode for the menu region. - * @param {string} [attributes.router=browse] Initial mode for the router region. - * @param {string} [attributes.toolbar=replace] Initial mode for the toolbar region. - * @param {int} [attributes.priority=60] The priority for the state link in the media menu. - * @param {boolean} [attributes.searchable=true] Whether the library is searchable. - * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown. - * Accepts 'all', 'uploaded', or 'unattached'. - * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. - * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. - * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. - * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. - * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. - */ -ReplaceImage = Library.extend(/** @lends wp.media.controller.ReplaceImage.prototype */{ - defaults: _.defaults({ - id: 'replace-image', - title: l10n.replaceImageTitle, - multiple: false, - filterable: 'uploaded', - toolbar: 'replace', - menu: false, - priority: 60, - syncSelection: true - }, Library.prototype.defaults ), - - /** - * @since 3.9.0 - * - * @param options - */ - initialize: function( options ) { - var library, comparator; - - this.image = options.image; - // If we haven't been provided a `library`, create a `Selection`. - if ( ! this.get('library') ) { - this.set( 'library', wp.media.query({ type: 'image' }) ); - } - - Library.prototype.initialize.apply( this, arguments ); - - library = this.get('library'); - comparator = library.comparator; - - // Overload the library's comparator to push items that are not in - // the mirrored query to the front of the aggregate collection. - library.comparator = function( a, b ) { - var aInQuery = !! this.mirroring.get( a.cid ), - bInQuery = !! this.mirroring.get( b.cid ); - - if ( ! aInQuery && bInQuery ) { - return -1; - } else if ( aInQuery && ! bInQuery ) { - return 1; - } else { - return comparator.apply( this, arguments ); - } - }; - - // Add all items in the selection to the library, so any featured - // images that are not initially loaded still appear. - library.observe( this.get('selection') ); - }, - - /** - * @since 3.9.0 - */ - activate: function() { - this.updateSelection(); - Library.prototype.activate.apply( this, arguments ); - }, - - /** - * @since 3.9.0 - */ - updateSelection: function() { - var selection = this.get('selection'), - attachment = this.image.attachment; - - selection.reset( attachment ? [ attachment ] : [] ); - } -}); - -module.exports = ReplaceImage; - - -/***/ }), - -/***/ "./src/js/media/controllers/site-icon-cropper.js": -/***/ (function(module, exports) { - -var Controller = wp.media.controller, - SiteIconCropper; - -/** - * wp.media.controller.SiteIconCropper - * - * A state for cropping a Site Icon. - * - * @memberOf wp.media.controller - * - * @class - * @augments wp.media.controller.Cropper - * @augments wp.media.controller.State - * @augments Backbone.Model - */ -SiteIconCropper = Controller.Cropper.extend(/** @lends wp.media.controller.SiteIconCropper.prototype */{ - activate: function() { - this.frame.on( 'content:create:crop', this.createCropContent, this ); - this.frame.on( 'close', this.removeCropper, this ); - this.set('selection', new Backbone.Collection(this.frame._selection.single)); - }, - - createCropContent: function() { - this.cropperView = new wp.media.view.SiteIconCropper({ - controller: this, - attachment: this.get('selection').first() - }); - this.cropperView.on('image-loaded', this.createCropToolbar, this); - this.frame.content.set(this.cropperView); - - }, - - doCrop: function( attachment ) { - var cropDetails = attachment.get( 'cropDetails' ), - control = this.get( 'control' ); - - cropDetails.dst_width = control.params.width; - cropDetails.dst_height = control.params.height; - - return wp.ajax.post( 'crop-image', { - nonce: attachment.get( 'nonces' ).edit, - id: attachment.get( 'id' ), - context: 'site-icon', - cropDetails: cropDetails - } ); - } -}); - -module.exports = SiteIconCropper; - - -/***/ }), - -/***/ "./src/js/media/controllers/state-machine.js": -/***/ (function(module, exports) { - -/** - * wp.media.controller.StateMachine - * - * A state machine keeps track of state. It is in one state at a time, - * and can change from one state to another. - * - * States are stored as models in a Backbone collection. - * - * @memberOf wp.media.controller - * - * @since 3.5.0 - * - * @class - * @augments Backbone.Model - * @mixin - * @mixes Backbone.Events - */ -var StateMachine = function() { - return { - // Use Backbone's self-propagating `extend` inheritance method. - extend: Backbone.Model.extend - }; -}; - -_.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{ - /** - * Fetch a state. - * - * If no `id` is provided, returns the active state. - * - * Implicitly creates states. - * - * Ensure that the `states` collection exists so the `StateMachine` - * can be used as a mixin. - * - * @since 3.5.0 - * - * @param {string} id - * @return {wp.media.controller.State} Returns a State model from - * the StateMachine collection. - */ - state: function( id ) { - this.states = this.states || new Backbone.Collection(); - - // Default to the active state. - id = id || this._state; - - if ( id && ! this.states.get( id ) ) { - this.states.add({ id: id }); - } - return this.states.get( id ); - }, - - /** - * Sets the active state. - * - * Bail if we're trying to select the current state, if we haven't - * created the `states` collection, or are trying to select a state - * that does not exist. - * - * @since 3.5.0 - * - * @param {string} id - * - * @fires wp.media.controller.State#deactivate - * @fires wp.media.controller.State#activate - * - * @return {wp.media.controller.StateMachine} Returns itself to allow chaining. - */ - setState: function( id ) { - var previous = this.state(); - - if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) { - return this; - } - - if ( previous ) { - previous.trigger('deactivate'); - this._lastState = previous.id; - } - - this._state = id; - this.state().trigger('activate'); - - return this; - }, - - /** - * Returns the previous active state. - * - * Call the `state()` method with no parameters to retrieve the current - * active state. - * - * @since 3.5.0 - * - * @return {wp.media.controller.State} Returns a State model from - * the StateMachine collection. - */ - lastState: function() { - if ( this._lastState ) { - return this.state( this._lastState ); - } - } -}); - -// Map all event binding and triggering on a StateMachine to its `states` collection. -_.each([ 'on', 'off', 'trigger' ], function( method ) { - /** - * @function on - * @memberOf wp.media.controller.StateMachine - * @instance - * @return {wp.media.controller.StateMachine} Returns itself to allow chaining. - */ - /** - * @function off - * @memberOf wp.media.controller.StateMachine - * @instance - * @return {wp.media.controller.StateMachine} Returns itself to allow chaining. - */ - /** - * @function trigger - * @memberOf wp.media.controller.StateMachine - * @instance - * @return {wp.media.controller.StateMachine} Returns itself to allow chaining. - */ - StateMachine.prototype[ method ] = function() { - // Ensure that the `states` collection exists so the `StateMachine` - // can be used as a mixin. - this.states = this.states || new Backbone.Collection(); - // Forward the method to the `states` collection. - this.states[ method ].apply( this.states, arguments ); - return this; - }; -}); - -module.exports = StateMachine; - - -/***/ }), - -/***/ "./src/js/media/controllers/state.js": -/***/ (function(module, exports) { - -/** - * wp.media.controller.State - * - * A state is a step in a workflow that when set will trigger the controllers - * for the regions to be updated as specified in the frame. - * - * A state has an event-driven lifecycle: - * - * 'ready' triggers when a state is added to a state machine's collection. - * 'activate' triggers when a state is activated by a state machine. - * 'deactivate' triggers when a state is deactivated by a state machine. - * 'reset' is not triggered automatically. It should be invoked by the - * proper controller to reset the state to its default. - * - * @memberOf wp.media.controller - * - * @class - * @augments Backbone.Model - */ -var State = Backbone.Model.extend(/** @lends wp.media.controller.State.prototype */{ - /** - * Constructor. - * - * @since 3.5.0 - */ - constructor: function() { - this.on( 'activate', this._preActivate, this ); - this.on( 'activate', this.activate, this ); - this.on( 'activate', this._postActivate, this ); - this.on( 'deactivate', this._deactivate, this ); - this.on( 'deactivate', this.deactivate, this ); - this.on( 'reset', this.reset, this ); - this.on( 'ready', this._ready, this ); - this.on( 'ready', this.ready, this ); - /** - * Call parent constructor with passed arguments - */ - Backbone.Model.apply( this, arguments ); - this.on( 'change:menu', this._updateMenu, this ); - }, - /** - * Ready event callback. - * - * @abstract - * @since 3.5.0 - */ - ready: function() {}, - - /** - * Activate event callback. - * - * @abstract - * @since 3.5.0 - */ - activate: function() {}, - - /** - * Deactivate event callback. - * - * @abstract - * @since 3.5.0 - */ - deactivate: function() {}, - - /** - * Reset event callback. - * - * @abstract - * @since 3.5.0 - */ - reset: function() {}, - - /** - * @since 3.5.0 - * @access private - */ - _ready: function() { - this._updateMenu(); - }, - - /** - * @since 3.5.0 - * @access private - */ - _preActivate: function() { - this.active = true; - }, - - /** - * @since 3.5.0 - * @access private - */ - _postActivate: function() { - this.on( 'change:menu', this._menu, this ); - this.on( 'change:titleMode', this._title, this ); - this.on( 'change:content', this._content, this ); - this.on( 'change:toolbar', this._toolbar, this ); - - this.frame.on( 'title:render:default', this._renderTitle, this ); - - this._title(); - this._menu(); - this._toolbar(); - this._content(); - this._router(); - }, - - /** - * @since 3.5.0 - * @access private - */ - _deactivate: function() { - this.active = false; - - this.frame.off( 'title:render:default', this._renderTitle, this ); - - this.off( 'change:menu', this._menu, this ); - this.off( 'change:titleMode', this._title, this ); - this.off( 'change:content', this._content, this ); - this.off( 'change:toolbar', this._toolbar, this ); - }, - - /** - * @since 3.5.0 - * @access private - */ - _title: function() { - this.frame.title.render( this.get('titleMode') || 'default' ); - }, - - /** - * @since 3.5.0 - * @access private - */ - _renderTitle: function( view ) { - view.$el.text( this.get('title') || '' ); - }, - - /** - * @since 3.5.0 - * @access private - */ - _router: function() { - var router = this.frame.router, - mode = this.get('router'), - view; - - this.frame.$el.toggleClass( 'hide-router', ! mode ); - if ( ! mode ) { - return; - } - - this.frame.router.render( mode ); - - view = router.get(); - if ( view && view.select ) { - view.select( this.frame.content.mode() ); - } - }, - - /** - * @since 3.5.0 - * @access private - */ - _menu: function() { - var menu = this.frame.menu, - mode = this.get('menu'), - view; - - this.frame.$el.toggleClass( 'hide-menu', ! mode ); - if ( ! mode ) { - return; - } - - menu.mode( mode ); - - view = menu.get(); - if ( view && view.select ) { - view.select( this.id ); - } - }, - - /** - * @since 3.5.0 - * @access private - */ - _updateMenu: function() { - var previous = this.previous('menu'), - menu = this.get('menu'); - - if ( previous ) { - this.frame.off( 'menu:render:' + previous, this._renderMenu, this ); - } - - if ( menu ) { - this.frame.on( 'menu:render:' + menu, this._renderMenu, this ); - } - }, - - /** - * Create a view in the media menu for the state. - * - * @since 3.5.0 - * @access private - * - * @param {media.view.Menu} view The menu view. - */ - _renderMenu: function( view ) { - var menuItem = this.get('menuItem'), - title = this.get('title'), - priority = this.get('priority'); - - if ( ! menuItem && title ) { - menuItem = { text: title }; - - if ( priority ) { - menuItem.priority = priority; - } - } - - if ( ! menuItem ) { - return; - } - - view.set( this.id, menuItem ); - } -}); - -_.each(['toolbar','content'], function( region ) { - /** - * @access private - */ - State.prototype[ '_' + region ] = function() { - var mode = this.get( region ); - if ( mode ) { - this.frame[ region ].render( mode ); - } - }; -}); - -module.exports = State; - - -/***/ }), - -/***/ "./src/js/media/utils/selection-sync.js": -/***/ (function(module, exports) { - -/** - * wp.media.selectionSync - * - * Sync an attachments selection in a state with another state. - * - * Allows for selecting multiple images in the Add Media workflow, and then - * switching to the Insert Gallery workflow while preserving the attachments selection. - * - * @memberOf wp.media - * - * @mixin - */ -var selectionSync = { - /** - * @since 3.5.0 - */ - syncSelection: function() { - var selection = this.get('selection'), - manager = this.frame._selection; - - if ( ! this.get('syncSelection') || ! manager || ! selection ) { - return; - } - - /* - * If the selection supports multiple items, validate the stored - * attachments based on the new selection's conditions. Record - * the attachments that are not included; we'll maintain a - * reference to those. Other attachments are considered in flux. - */ - if ( selection.multiple ) { - selection.reset( [], { silent: true }); - selection.validateAll( manager.attachments ); - manager.difference = _.difference( manager.attachments.models, selection.models ); - } - - // Sync the selection's single item with the master. - selection.single( manager.single ); - }, - - /** - * Record the currently active attachments, which is a combination - * of the selection's attachments and the set of selected - * attachments that this specific selection considered invalid. - * Reset the difference and record the single attachment. - * - * @since 3.5.0 - */ - recordSelection: function() { - var selection = this.get('selection'), - manager = this.frame._selection; - - if ( ! this.get('syncSelection') || ! manager || ! selection ) { - return; - } - - if ( selection.multiple ) { - manager.attachments.reset( selection.toArray().concat( manager.difference ) ); - manager.difference = []; - } else { - manager.attachments.add( selection.toArray() ); - } - - manager.single = selection._single; - } -}; - -module.exports = selectionSync; - - -/***/ }), - -/***/ "./src/js/media/views/attachment-compat.js": +/***/ "+mQJ": /***/ (function(module, exports) { var View = wp.media.View, - AttachmentCompat; + $ = jQuery, + l10n = wp.media.view.l10n, + EmbedUrl; /** - * wp.media.view.AttachmentCompat - * - * A view to display fields added via the `attachment_fields_to_edit` filter. + * wp.media.view.EmbedUrl * * @memberOf wp.media.view * @@ -2690,81 +144,154 @@ var View = wp.media.View, * @augments wp.Backbone.View * @augments Backbone.View */ -AttachmentCompat = View.extend(/** @lends wp.media.view.AttachmentCompat.prototype */{ - tagName: 'form', - className: 'compat-item', +EmbedUrl = View.extend(/** @lends wp.media.view.EmbedUrl.prototype */{ + tagName: 'span', + className: 'embed-url', events: { - 'submit': 'preventDefault', - 'change input': 'save', - 'change select': 'save', - 'change textarea': 'save' + 'input': 'url' }, initialize: function() { - this.listenTo( this.model, 'change:compat', this.render ); - }, - /** - * @return {wp.media.view.AttachmentCompat} Returns itself to allow chaining. - */ - dispose: function() { - if ( this.$(':focus').length ) { - this.save(); + this.$input = $( '' ) + .attr( 'aria-label', l10n.insertFromUrlTitle ) + .val( this.model.get('url') ); + this.input = this.$input[0]; + + this.spinner = $('')[0]; + this.$el.append([ this.input, this.spinner ]); + + this.listenTo( this.model, 'change:url', this.render ); + + if ( this.model.get( 'url' ) ) { + _.delay( _.bind( function () { + this.model.trigger( 'change:url' ); + }, this ), 500 ); } - /** - * call 'dispose' directly on the parent class - */ - return View.prototype.dispose.apply( this, arguments ); }, /** - * @return {wp.media.view.AttachmentCompat} Returns itself to allow chaining. + * @return {wp.media.view.EmbedUrl} Returns itself to allow chaining. */ render: function() { - var compat = this.model.get('compat'); - if ( ! compat || ! compat.item ) { + var $input = this.$input; + + if ( $input.is(':focus') ) { return; } - this.views.detach(); - this.$el.html( compat.item ); - this.views.render(); + this.input.value = this.model.get('url') || 'http://'; + /** + * Call `render` directly on parent class with passed arguments + */ + View.prototype.render.apply( this, arguments ); return this; }, - /** - * @param {Object} event - */ - preventDefault: function( event ) { - event.preventDefault(); - }, - /** - * @param {Object} event - */ - save: function( event ) { - var data = {}; - if ( event ) { - event.preventDefault(); - } - - _.each( this.$el.serializeArray(), function( pair ) { - data[ pair.name ] = pair.value; - }); - - this.controller.trigger( 'attachment:compat:waiting', ['waiting'] ); - this.model.saveCompat( data ).always( _.bind( this.postSave, this ) ); - }, - - postSave: function() { - this.controller.trigger( 'attachment:compat:ready', ['ready'] ); + url: function( event ) { + var url = event.target.value || ''; + this.model.set( 'url', url.trim() ); } }); -module.exports = AttachmentCompat; +module.exports = EmbedUrl; /***/ }), -/***/ "./src/js/media/views/attachment-filters.js": +/***/ "04Ix": +/***/ (function(module, exports) { + +var _n = wp.i18n._n, + sprintf = wp.i18n.sprintf, + Selection; + +/** + * wp.media.view.Selection + * + * @memberOf wp.media.view + * + * @class + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + */ +Selection = wp.media.View.extend(/** @lends wp.media.view.Selection.prototype */{ + tagName: 'div', + className: 'media-selection', + template: wp.template('media-selection'), + + events: { + 'click .edit-selection': 'edit', + 'click .clear-selection': 'clear' + }, + + initialize: function() { + _.defaults( this.options, { + editable: false, + clearable: true + }); + + /** + * @member {wp.media.view.Attachments.Selection} + */ + this.attachments = new wp.media.view.Attachments.Selection({ + controller: this.controller, + collection: this.collection, + selection: this.collection, + model: new Backbone.Model() + }); + + this.views.set( '.selection-view', this.attachments ); + this.collection.on( 'add remove reset', this.refresh, this ); + this.controller.on( 'content:activate', this.refresh, this ); + }, + + ready: function() { + this.refresh(); + }, + + refresh: function() { + // If the selection hasn't been rendered, bail. + if ( ! this.$el.children().length ) { + return; + } + + var collection = this.collection, + editing = 'edit-selection' === this.controller.content.mode(); + + // If nothing is selected, display nothing. + this.$el.toggleClass( 'empty', ! collection.length ); + this.$el.toggleClass( 'one', 1 === collection.length ); + this.$el.toggleClass( 'editing', editing ); + + this.$( '.count' ).text( + /* translators: %s: Number of selected media attachments. */ + sprintf( _n( '%s item selected', '%s items selected', collection.length ), collection.length ) + ); + }, + + edit: function( event ) { + event.preventDefault(); + if ( this.options.editable ) { + this.options.editable.call( this, this.collection ); + } + }, + + clear: function( event ) { + event.preventDefault(); + this.collection.reset(); + + // Move focus to the modal. + this.controller.modal.focusManager.focus(); + } +}); + +module.exports = Selection; + + +/***/ }), + +/***/ "1S4+": /***/ (function(module, exports) { var $ = jQuery, @@ -2848,263 +375,116 @@ module.exports = AttachmentFilters; /***/ }), -/***/ "./src/js/media/views/attachment-filters/all.js": +/***/ "2AvB": /***/ (function(module, exports) { -var l10n = wp.media.view.l10n, - All; +var Settings = wp.media.view.Settings, + AttachmentDisplay; /** - * wp.media.view.AttachmentFilters.All + * wp.media.view.Settings.AttachmentDisplay * - * @memberOf wp.media.view.AttachmentFilters + * @memberOf wp.media.view.Settings * * @class - * @augments wp.media.view.AttachmentFilters + * @augments wp.media.view.Settings * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View */ -All = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.All.prototype */{ - createFilters: function() { - var filters = {}, - uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0; +AttachmentDisplay = Settings.extend(/** @lends wp.media.view.Settings.AttachmentDisplay.prototype */{ + className: 'attachment-display-settings', + template: wp.template('attachment-display-settings'), - _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) { - filters[ key ] = { - text: text, - props: { - status: null, - type: key, - uploadedTo: null, - orderby: 'date', - order: 'DESC', - author: null - } - }; + initialize: function() { + var attachment = this.options.attachment; + + _.defaults( this.options, { + userSettings: false }); + // Call 'initialize' directly on the parent class. + Settings.prototype.initialize.apply( this, arguments ); + this.listenTo( this.model, 'change:link', this.updateLinkTo ); - filters.all = { - text: l10n.allMediaItems, - props: { - status: null, - type: null, - uploadedTo: null, - orderby: 'date', - order: 'DESC', - author: null - }, - priority: 10 - }; + if ( attachment ) { + attachment.on( 'change:uploading', this.render, this ); + } + }, - if ( wp.media.view.settings.post.id ) { - filters.uploaded = { - text: l10n.uploadedToThisPost, - props: { - status: null, - type: null, - uploadedTo: wp.media.view.settings.post.id, - orderby: 'menuOrder', - order: 'ASC', - author: null - }, - priority: 20 - }; + dispose: function() { + var attachment = this.options.attachment; + if ( attachment ) { + attachment.off( null, null, this ); + } + /** + * call 'dispose' directly on the parent class + */ + Settings.prototype.dispose.apply( this, arguments ); + }, + /** + * @return {wp.media.view.AttachmentDisplay} Returns itself to allow chaining. + */ + render: function() { + var attachment = this.options.attachment; + if ( attachment ) { + _.extend( this.options, { + sizes: attachment.get('sizes'), + type: attachment.get('type') + }); + } + /** + * call 'render' directly on the parent class + */ + Settings.prototype.render.call( this ); + this.updateLinkTo(); + return this; + }, + + updateLinkTo: function() { + var linkTo = this.model.get('link'), + $input = this.$('.link-to-custom'), + attachment = this.options.attachment; + + if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) { + $input.closest( '.setting' ).addClass( 'hidden' ); + return; } - filters.unattached = { - text: l10n.unattached, - props: { - status: null, - uploadedTo: 0, - type: null, - orderby: 'menuOrder', - order: 'ASC', - author: null - }, - priority: 50 - }; - - if ( uid ) { - filters.mine = { - text: l10n.mine, - props: { - status: null, - type: null, - uploadedTo: null, - orderby: 'date', - order: 'DESC', - author: uid - }, - priority: 50 - }; - } - - if ( wp.media.view.settings.mediaTrash && - this.controller.isModeActive( 'grid' ) ) { - - filters.trash = { - text: l10n.trash, - props: { - uploadedTo: null, - status: 'trash', - type: null, - orderby: 'date', - order: 'DESC', - author: null - }, - priority: 50 - }; - } - - this.filters = filters; - } -}); - -module.exports = All; - - -/***/ }), - -/***/ "./src/js/media/views/attachment-filters/date.js": -/***/ (function(module, exports) { - -var l10n = wp.media.view.l10n, - DateFilter; - -/** - * A filter dropdown for month/dates. - * - * @memberOf wp.media.view.AttachmentFilters - * - * @class - * @augments wp.media.view.AttachmentFilters - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -DateFilter = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Date.prototype */{ - id: 'media-attachment-date-filters', - - createFilters: function() { - var filters = {}; - _.each( wp.media.view.settings.months || {}, function( value, index ) { - filters[ index ] = { - text: value.text, - props: { - year: value.year, - monthnum: value.month - } - }; - }); - filters.all = { - text: l10n.allDates, - props: { - monthnum: false, - year: false - }, - priority: 10 - }; - this.filters = filters; - } -}); - -module.exports = DateFilter; - - -/***/ }), - -/***/ "./src/js/media/views/attachment-filters/uploaded.js": -/***/ (function(module, exports) { - -var l10n = wp.media.view.l10n, - Uploaded; - -/** - * wp.media.view.AttachmentFilters.Uploaded - * - * @memberOf wp.media.view.AttachmentFilters - * - * @class - * @augments wp.media.view.AttachmentFilters - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -Uploaded = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Uploaded.prototype */{ - createFilters: function() { - var type = this.model.get('type'), - types = wp.media.view.settings.mimeTypes, - uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0, - text; - - if ( types && type ) { - text = types[ type ]; - } - - this.filters = { - all: { - text: text || l10n.allMediaItems, - props: { - uploadedTo: null, - orderby: 'date', - order: 'DESC', - author: null - }, - priority: 10 - }, - - uploaded: { - text: l10n.uploadedToThisPost, - props: { - uploadedTo: wp.media.view.settings.post.id, - orderby: 'menuOrder', - order: 'ASC', - author: null - }, - priority: 20 - }, - - unattached: { - text: l10n.unattached, - props: { - uploadedTo: 0, - orderby: 'menuOrder', - order: 'ASC', - author: null - }, - priority: 50 + if ( attachment ) { + if ( 'post' === linkTo ) { + $input.val( attachment.get('link') ); + } else if ( 'file' === linkTo ) { + $input.val( attachment.get('url') ); + } else if ( ! this.model.get('linkUrl') ) { + $input.val('http://'); } - }; - if ( uid ) { - this.filters.mine = { - text: l10n.mine, - props: { - orderby: 'date', - order: 'DESC', - author: uid - }, - priority: 50 - }; + $input.prop( 'readonly', 'custom' !== linkTo ); + } + + $input.closest( '.setting' ).removeClass( 'hidden' ); + if ( $input.length ) { + $input[0].scrollIntoView(); } } }); -module.exports = Uploaded; +module.exports = AttachmentDisplay; /***/ }), -/***/ "./src/js/media/views/attachment.js": +/***/ "2NU8": /***/ (function(module, exports) { var View = wp.media.View, - $ = jQuery, - Attachment; + Toolbar; /** - * wp.media.view.Attachment + * wp.media.view.Toolbar + * + * A toolbar which consists of a primary and a secondary section. Each sections + * can be filled with views. * * @memberOf wp.media.view * @@ -3113,919 +493,157 @@ var View = wp.media.View, * @augments wp.Backbone.View * @augments Backbone.View */ -Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{ - tagName: 'li', - className: 'attachment', - template: wp.template('attachment'), - - attributes: function() { - return { - 'tabIndex': 0, - 'role': 'checkbox', - 'aria-label': this.model.get( 'title' ), - 'aria-checked': false, - 'data-id': this.model.get( 'id' ) - }; - }, - - events: { - 'click': 'toggleSelectionHandler', - 'change [data-setting]': 'updateSetting', - 'change [data-setting] input': 'updateSetting', - 'change [data-setting] select': 'updateSetting', - 'change [data-setting] textarea': 'updateSetting', - 'click .attachment-close': 'removeFromLibrary', - 'click .check': 'checkClickHandler', - 'keydown': 'toggleSelectionHandler' - }, - - buttons: {}, +Toolbar = View.extend(/** @lends wp.media.view.Toolbar.prototype */{ + tagName: 'div', + className: 'media-toolbar', initialize: function() { - var selection = this.options.selection, - options = _.defaults( this.options, { - rerenderOnModelChange: true - } ); + var state = this.controller.state(), + selection = this.selection = state.get('selection'), + library = this.library = state.get('library'); - if ( options.rerenderOnModelChange ) { - this.listenTo( this.model, 'change', this.render ); - } else { - this.listenTo( this.model, 'change:percent', this.progress ); + this._views = {}; + + // The toolbar is composed of two `PriorityList` views. + this.primary = new wp.media.view.PriorityList(); + this.secondary = new wp.media.view.PriorityList(); + this.primary.$el.addClass('media-toolbar-primary search-form'); + this.secondary.$el.addClass('media-toolbar-secondary'); + + this.views.set([ this.secondary, this.primary ]); + + if ( this.options.items ) { + this.set( this.options.items, { silent: true }); + } + + if ( ! this.options.silent ) { + this.render(); } - this.listenTo( this.model, 'change:title', this._syncTitle ); - this.listenTo( this.model, 'change:caption', this._syncCaption ); - this.listenTo( this.model, 'change:artist', this._syncArtist ); - this.listenTo( this.model, 'change:album', this._syncAlbum ); - // Update the selection. - this.listenTo( this.model, 'add', this.select ); - this.listenTo( this.model, 'remove', this.deselect ); if ( selection ) { - selection.on( 'reset', this.updateSelect, this ); - // Update the model's details view. - this.listenTo( this.model, 'selection:single selection:unsingle', this.details ); - this.details( this.model, this.controller.state().get('selection') ); + selection.on( 'add remove reset', this.refresh, this ); } - this.listenTo( this.controller.states, 'attachment:compat:waiting attachment:compat:ready', this.updateSave ); + if ( library ) { + library.on( 'add remove reset', this.refresh, this ); + } }, /** - * @return {wp.media.view.Attachment} Returns itself to allow chaining. + * @return {wp.media.view.Toolbar} Returns itsef to allow chaining */ dispose: function() { - var selection = this.options.selection; + if ( this.selection ) { + this.selection.off( null, null, this ); + } - // Make sure all settings are saved before removing the view. - this.updateAll(); - - if ( selection ) { - selection.off( null, null, this ); + if ( this.library ) { + this.library.off( null, null, this ); } /** * call 'dispose' directly on the parent class */ - View.prototype.dispose.apply( this, arguments ); - return this; + return View.prototype.dispose.apply( this, arguments ); }, + + ready: function() { + this.refresh(); + }, + /** - * @return {wp.media.view.Attachment} Returns itself to allow chaining. + * @param {string} id + * @param {Backbone.View|Object} view + * @param {Object} [options={}] + * @return {wp.media.view.Toolbar} Returns itself to allow chaining. */ - render: function() { - var options = _.defaults( this.model.toJSON(), { - orientation: 'landscape', - uploading: false, - type: '', - subtype: '', - icon: '', - filename: '', - caption: '', - title: '', - dateFormatted: '', - width: '', - height: '', - compat: false, - alt: '', - description: '' - }, this.options ); + set: function( id, view, options ) { + var list; + options = options || {}; - options.buttons = this.buttons; - options.describe = this.controller.state().get('describe'); + // Accept an object with an `id` : `view` mapping. + if ( _.isObject( id ) ) { + _.each( id, function( view, id ) { + this.set( id, view, { silent: true }); + }, this ); - if ( 'image' === options.type ) { - options.size = this.imageSize(); - } - - options.can = {}; - if ( options.nonces ) { - options.can.remove = !! options.nonces['delete']; - options.can.save = !! options.nonces.update; - } - - if ( this.controller.state().get('allowLocalEdits') ) { - options.allowLocalEdits = true; - } - - if ( options.uploading && ! options.percent ) { - options.percent = 0; - } - - this.views.detach(); - this.$el.html( this.template( options ) ); - - this.$el.toggleClass( 'uploading', options.uploading ); - - if ( options.uploading ) { - this.$bar = this.$('.media-progress-bar div'); } else { - delete this.$bar; + if ( ! ( view instanceof Backbone.View ) ) { + view.classes = [ 'media-button-' + id ].concat( view.classes || [] ); + view = new wp.media.view.Button( view ).render(); + } + + view.controller = view.controller || this.controller; + + this._views[ id ] = view; + + list = view.options.priority < 0 ? 'secondary' : 'primary'; + this[ list ].set( id, view, options ); } - // Check if the model is selected. - this.updateSelect(); - - // Update the save status. - this.updateSave(); - - this.views.render(); + if ( ! options.silent ) { + this.refresh(); + } return this; }, - - progress: function() { - if ( this.$bar && this.$bar.length ) { - this.$bar.width( this.model.get('percent') + '%' ); - } - }, - /** - * @param {Object} event + * @param {string} id + * @return {wp.media.view.Button} */ - toggleSelectionHandler: function( event ) { - var method; - - // Don't do anything inside inputs and on the attachment check and remove buttons. - if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName ) { - return; - } - - // Catch arrow events. - if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) { - this.controller.trigger( 'attachment:keydown:arrow', event ); - return; - } - - // Catch enter and space events. - if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) { - return; - } - - event.preventDefault(); - - // In the grid view, bubble up an edit:attachment event to the controller. - if ( this.controller.isModeActive( 'grid' ) ) { - if ( this.controller.isModeActive( 'edit' ) ) { - // Pass the current target to restore focus when closing. - this.controller.trigger( 'edit:attachment', this.model, event.currentTarget ); - return; - } - - if ( this.controller.isModeActive( 'select' ) ) { - method = 'toggle'; - } - } - - if ( event.shiftKey ) { - method = 'between'; - } else if ( event.ctrlKey || event.metaKey ) { - method = 'toggle'; - } - - this.toggleSelection({ - method: method - }); - - this.controller.trigger( 'selection:toggle' ); + get: function( id ) { + return this._views[ id ]; }, /** + * @param {string} id * @param {Object} options + * @return {wp.media.view.Toolbar} Returns itself to allow chaining. */ - toggleSelection: function( options ) { - var collection = this.collection, - selection = this.options.selection, - model = this.model, - method = options && options.method, - single, models, singleIndex, modelIndex; + unset: function( id, options ) { + delete this._views[ id ]; + this.primary.unset( id, options ); + this.secondary.unset( id, options ); - if ( ! selection ) { - return; + if ( ! options || ! options.silent ) { + this.refresh(); } - - single = selection.single(); - method = _.isUndefined( method ) ? selection.multiple : method; - - // If the `method` is set to `between`, select all models that - // exist between the current and the selected model. - if ( 'between' === method && single && selection.multiple ) { - // If the models are the same, short-circuit. - if ( single === model ) { - return; - } - - singleIndex = collection.indexOf( single ); - modelIndex = collection.indexOf( this.model ); - - if ( singleIndex < modelIndex ) { - models = collection.models.slice( singleIndex, modelIndex + 1 ); - } else { - models = collection.models.slice( modelIndex, singleIndex + 1 ); - } - - selection.add( models ); - selection.single( model ); - return; - - // If the `method` is set to `toggle`, just flip the selection - // status, regardless of whether the model is the single model. - } else if ( 'toggle' === method ) { - selection[ this.selected() ? 'remove' : 'add' ]( model ); - selection.single( model ); - return; - } else if ( 'add' === method ) { - selection.add( model ); - selection.single( model ); - return; - } - - // Fixes bug that loses focus when selecting a featured image. - if ( ! method ) { - method = 'add'; - } - - if ( method !== 'add' ) { - method = 'reset'; - } - - if ( this.selected() ) { - /* - * If the model is the single model, remove it. - * If it is not the same as the single model, - * it now becomes the single model. - */ - selection[ single === model ? 'remove' : 'single' ]( model ); - } else { - /* - * If the model is not selected, run the `method` on the - * selection. By default, we `reset` the selection, but the - * `method` can be set to `add` the model to the selection. - */ - selection[ method ]( model ); - selection.single( model ); - } - }, - - updateSelect: function() { - this[ this.selected() ? 'select' : 'deselect' ](); - }, - /** - * @return {unresolved|boolean} - */ - selected: function() { - var selection = this.options.selection; - if ( selection ) { - return !! selection.get( this.model.cid ); - } - }, - /** - * @param {Backbone.Model} model - * @param {Backbone.Collection} collection - */ - select: function( model, collection ) { - var selection = this.options.selection, - controller = this.controller; - - /* - * Check if a selection exists and if it's the collection provided. - * If they're not the same collection, bail; we're in another - * selection's event loop. - */ - if ( ! selection || ( collection && collection !== selection ) ) { - return; - } - - // Bail if the model is already selected. - if ( this.$el.hasClass( 'selected' ) ) { - return; - } - - // Add 'selected' class to model, set aria-checked to true. - this.$el.addClass( 'selected' ).attr( 'aria-checked', true ); - // Make the checkbox tabable, except in media grid (bulk select mode). - if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) { - this.$( '.check' ).attr( 'tabindex', '0' ); - } - }, - /** - * @param {Backbone.Model} model - * @param {Backbone.Collection} collection - */ - deselect: function( model, collection ) { - var selection = this.options.selection; - - /* - * Check if a selection exists and if it's the collection provided. - * If they're not the same collection, bail; we're in another - * selection's event loop. - */ - if ( ! selection || ( collection && collection !== selection ) ) { - return; - } - this.$el.removeClass( 'selected' ).attr( 'aria-checked', false ) - .find( '.check' ).attr( 'tabindex', '-1' ); - }, - /** - * @param {Backbone.Model} model - * @param {Backbone.Collection} collection - */ - details: function( model, collection ) { - var selection = this.options.selection, - details; - - if ( selection !== collection ) { - return; - } - - details = selection.single(); - this.$el.toggleClass( 'details', details === this.model ); - }, - /** - * @param {string} size - * @return {Object} - */ - imageSize: function( size ) { - var sizes = this.model.get('sizes'), matched = false; - - size = size || 'medium'; - - // Use the provided image size if possible. - if ( sizes ) { - if ( sizes[ size ] ) { - matched = sizes[ size ]; - } else if ( sizes.large ) { - matched = sizes.large; - } else if ( sizes.thumbnail ) { - matched = sizes.thumbnail; - } else if ( sizes.full ) { - matched = sizes.full; - } - - if ( matched ) { - return _.clone( matched ); - } - } - - return { - url: this.model.get('url'), - width: this.model.get('width'), - height: this.model.get('height'), - orientation: this.model.get('orientation') - }; - }, - /** - * @param {Object} event - */ - updateSetting: function( event ) { - var $setting = $( event.target ).closest('[data-setting]'), - setting, value; - - if ( ! $setting.length ) { - return; - } - - setting = $setting.data('setting'); - value = event.target.value; - - if ( this.model.get( setting ) !== value ) { - this.save( setting, value ); - } - }, - - /** - * Pass all the arguments to the model's save method. - * - * Records the aggregate status of all save requests and updates the - * view's classes accordingly. - */ - save: function() { - var view = this, - save = this._save = this._save || { status: 'ready' }, - request = this.model.save.apply( this.model, arguments ), - requests = save.requests ? $.when( request, save.requests ) : request; - - // If we're waiting to remove 'Saved.', stop. - if ( save.savedTimer ) { - clearTimeout( save.savedTimer ); - } - - this.updateSave('waiting'); - save.requests = requests; - requests.always( function() { - // If we've performed another request since this one, bail. - if ( save.requests !== requests ) { - return; - } - - view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' ); - save.savedTimer = setTimeout( function() { - view.updateSave('ready'); - delete save.savedTimer; - }, 2000 ); - }); - }, - /** - * @param {string} status - * @return {wp.media.view.Attachment} Returns itself to allow chaining. - */ - updateSave: function( status ) { - var save = this._save = this._save || { status: 'ready' }; - - if ( status && status !== save.status ) { - this.$el.removeClass( 'save-' + save.status ); - save.status = status; - } - - this.$el.addClass( 'save-' + save.status ); return this; }, - updateAll: function() { - var $settings = this.$('[data-setting]'), - model = this.model, - changed; + refresh: function() { + var state = this.controller.state(), + library = state.get('library'), + selection = state.get('selection'); - changed = _.chain( $settings ).map( function( el ) { - var $input = $('input, textarea, select, [value]', el ), - setting, value; - - if ( ! $input.length ) { + _.each( this._views, function( button ) { + if ( ! button.model || ! button.options || ! button.options.requires ) { return; } - setting = $(el).data('setting'); - value = $input.val(); + var requires = button.options.requires, + disabled = false; - // Record the value if it changed. - if ( model.get( setting ) !== value ) { - return [ setting, value ]; + // Prevent insertion of attachments if any of them are still uploading. + if ( selection && selection.models ) { + disabled = _.some( selection.models, function( attachment ) { + return attachment.get('uploading') === true; + }); } - }).compact().object().value(); - if ( ! _.isEmpty( changed ) ) { - model.save( changed ); - } - }, - /** - * @param {Object} event - */ - removeFromLibrary: function( event ) { - // Catch enter and space events. - if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) { - return; - } - - // Stop propagation so the model isn't selected. - event.stopPropagation(); - - this.collection.remove( this.model ); - }, - - /** - * Add the model if it isn't in the selection, if it is in the selection, - * remove it. - * - * @param {[type]} event [description] - * @return {[type]} [description] - */ - checkClickHandler: function ( event ) { - var selection = this.options.selection; - if ( ! selection ) { - return; - } - event.stopPropagation(); - if ( selection.where( { id: this.model.get( 'id' ) } ).length ) { - selection.remove( this.model ); - // Move focus back to the attachment tile (from the check). - this.$el.focus(); - } else { - selection.add( this.model ); - } - - // Trigger an action button update. - this.controller.trigger( 'selection:toggle' ); - } -}); - -// Ensure settings remain in sync between attachment views. -_.each({ - caption: '_syncCaption', - title: '_syncTitle', - artist: '_syncArtist', - album: '_syncAlbum' -}, function( method, setting ) { - /** - * @function _syncCaption - * @memberOf wp.media.view.Attachment - * @instance - * - * @param {Backbone.Model} model - * @param {string} value - * @return {wp.media.view.Attachment} Returns itself to allow chaining. - */ - /** - * @function _syncTitle - * @memberOf wp.media.view.Attachment - * @instance - * - * @param {Backbone.Model} model - * @param {string} value - * @return {wp.media.view.Attachment} Returns itself to allow chaining. - */ - /** - * @function _syncArtist - * @memberOf wp.media.view.Attachment - * @instance - * - * @param {Backbone.Model} model - * @param {string} value - * @return {wp.media.view.Attachment} Returns itself to allow chaining. - */ - /** - * @function _syncAlbum - * @memberOf wp.media.view.Attachment - * @instance - * - * @param {Backbone.Model} model - * @param {string} value - * @return {wp.media.view.Attachment} Returns itself to allow chaining. - */ - Attachment.prototype[ method ] = function( model, value ) { - var $setting = this.$('[data-setting="' + setting + '"]'); - - if ( ! $setting.length ) { - return this; - } - - /* - * If the updated value is in sync with the value in the DOM, there - * is no need to re-render. If we're currently editing the value, - * it will automatically be in sync, suppressing the re-render for - * the view we're editing, while updating any others. - */ - if ( value === $setting.find('input, textarea, select, [value]').val() ) { - return this; - } - - return this.render(); - }; -}); - -module.exports = Attachment; - - -/***/ }), - -/***/ "./src/js/media/views/attachment/details.js": -/***/ (function(module, exports) { - -/* global ClipboardJS */ -var Attachment = wp.media.view.Attachment, - l10n = wp.media.view.l10n, - $ = jQuery, - Details, - __ = wp.i18n.__; - -Details = Attachment.extend(/** @lends wp.media.view.Attachment.Details.prototype */{ - tagName: 'div', - className: 'attachment-details', - template: wp.template('attachment-details'), - - /* - * Reset all the attributes inherited from Attachment including role=checkbox, - * tabindex, etc., as they are inappropriate for this view. See #47458 and [30483] / #30390. - */ - attributes: {}, - - events: { - 'change [data-setting]': 'updateSetting', - 'change [data-setting] input': 'updateSetting', - 'change [data-setting] select': 'updateSetting', - 'change [data-setting] textarea': 'updateSetting', - 'click .delete-attachment': 'deleteAttachment', - 'click .trash-attachment': 'trashAttachment', - 'click .untrash-attachment': 'untrashAttachment', - 'click .edit-attachment': 'editAttachment', - 'keydown': 'toggleSelectionHandler' - }, - - /** - * Copies the attachment URL to the clipboard. - * - * @since 5.5.0 - * - * @param {MouseEvent} event A click event. - * - * @return {void} - */ - copyAttachmentDetailsURLClipboard: function() { - var clipboard = new ClipboardJS( '.copy-attachment-url' ), - successTimeout; - - clipboard.on( 'success', function( event ) { - var triggerElement = $( event.trigger ), - successElement = $( '.success', triggerElement.closest( '.copy-to-clipboard-container' ) ); - - // Clear the selection and move focus back to the trigger. - event.clearSelection(); - // Handle ClipboardJS focus bug, see https://github.com/zenorocha/clipboard.js/issues/680 - triggerElement.trigger( 'focus' ); - - // Show success visual feedback. - clearTimeout( successTimeout ); - successElement.removeClass( 'hidden' ); - - // Hide success visual feedback after 3 seconds since last success. - successTimeout = setTimeout( function() { - successElement.addClass( 'hidden' ); - }, 3000 ); - - // Handle success audible feedback. - wp.a11y.speak( __( 'The file URL has been copied to your clipboard' ) ); - } ); - }, - - /** - * Shows the details of an attachment. - * - * @since 3.5.0 - * - * @constructs wp.media.view.Attachment.Details - * @augments wp.media.view.Attachment - * - * @return {void} - */ - initialize: function() { - this.options = _.defaults( this.options, { - rerenderOnModelChange: false + if ( requires.selection && selection && ! selection.length ) { + disabled = true; + } else if ( requires.library && library && ! library.length ) { + disabled = true; + } + button.model.set( 'disabled', disabled ); }); - - // Call 'initialize' directly on the parent class. - Attachment.prototype.initialize.apply( this, arguments ); - - this.copyAttachmentDetailsURLClipboard(); - }, - - /** - * Gets the focusable elements to move focus to. - * - * @since 5.3.0 - */ - getFocusableElements: function() { - var editedAttachment = $( 'li[data-id="' + this.model.id + '"]' ); - - this.previousAttachment = editedAttachment.prev(); - this.nextAttachment = editedAttachment.next(); - }, - - /** - * Moves focus to the previous or next attachment in the grid. - * Fallbacks to the upload button or media frame when there are no attachments. - * - * @since 5.3.0 - */ - moveFocus: function() { - if ( this.previousAttachment.length ) { - this.previousAttachment.trigger( 'focus' ); - return; - } - - if ( this.nextAttachment.length ) { - this.nextAttachment.trigger( 'focus' ); - return; - } - - // Fallback: move focus to the "Select Files" button in the media modal. - if ( this.controller.uploader && this.controller.uploader.$browser ) { - this.controller.uploader.$browser.trigger( 'focus' ); - return; - } - - // Last fallback. - this.moveFocusToLastFallback(); - }, - - /** - * Moves focus to the media frame as last fallback. - * - * @since 5.3.0 - */ - moveFocusToLastFallback: function() { - // Last fallback: make the frame focusable and move focus to it. - $( '.media-frame' ) - .attr( 'tabindex', '-1' ) - .trigger( 'focus' ); - }, - - /** - * Deletes an attachment. - * - * Deletes an attachment after asking for confirmation. After deletion, - * keeps focus in the modal. - * - * @since 3.5.0 - * - * @param {MouseEvent} event A click event. - * - * @return {void} - */ - deleteAttachment: function( event ) { - event.preventDefault(); - - this.getFocusableElements(); - - if ( window.confirm( l10n.warnDelete ) ) { - this.model.destroy(); - this.moveFocus(); - } - }, - - /** - * Sets the Trash state on an attachment, or destroys the model itself. - * - * If the mediaTrash setting is set to true, trashes the attachment. - * Otherwise, the model itself is destroyed. - * - * @since 3.9.0 - * - * @param {MouseEvent} event A click event. - * - * @return {void} - */ - trashAttachment: function( event ) { - var library = this.controller.library, - self = this; - event.preventDefault(); - - this.getFocusableElements(); - - // When in the Media Library and the Media Trash is enabled. - if ( wp.media.view.settings.mediaTrash && - 'edit-metadata' === this.controller.content.mode() ) { - - this.model.set( 'status', 'trash' ); - this.model.save().done( function() { - library._requery( true ); - /* - * @todo We need to move focus back to the previous, next, or first - * attachment but the library gets re-queried and refreshed. - * Thus, the references to the previous attachments are lost. - * We need an alternate method. - */ - self.moveFocusToLastFallback(); - } ); - } else { - this.model.destroy(); - this.moveFocus(); - } - }, - - /** - * Untrashes an attachment. - * - * @since 4.0.0 - * - * @param {MouseEvent} event A click event. - * - * @return {void} - */ - untrashAttachment: function( event ) { - var library = this.controller.library; - event.preventDefault(); - - this.model.set( 'status', 'inherit' ); - this.model.save().done( function() { - library._requery( true ); - } ); - }, - - /** - * Opens the edit page for a specific attachment. - * - * @since 3.5.0 - * - * @param {MouseEvent} event A click event. - * - * @return {void} - */ - editAttachment: function( event ) { - var editState = this.controller.states.get( 'edit-image' ); - if ( window.imageEdit && editState ) { - event.preventDefault(); - - editState.set( 'image', this.model ); - this.controller.setState( 'edit-image' ); - } else { - this.$el.addClass('needs-refresh'); - } - }, - - /** - * Triggers an event on the controller when reverse tabbing (shift+tab). - * - * This event can be used to make sure to move the focus correctly. - * - * @since 4.0.0 - * - * @fires wp.media.controller.MediaLibrary#attachment:details:shift-tab - * @fires wp.media.controller.MediaLibrary#attachment:keydown:arrow - * - * @param {KeyboardEvent} event A keyboard event. - * - * @return {boolean|void} Returns false or undefined. - */ - toggleSelectionHandler: function( event ) { - if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) { - this.controller.trigger( 'attachment:details:shift-tab', event ); - return false; - } - }, - - render: function() { - Attachment.prototype.render.apply( this, arguments ); - - wp.media.mixin.removeAllPlayers(); - this.$( 'audio, video' ).each( function (i, elem) { - var el = wp.media.view.MediaDetails.prepareSrc( elem ); - new window.MediaElementPlayer( el, wp.media.mixin.mejsSettings ); - } ); } }); -module.exports = Details; +module.exports = Toolbar; /***/ }), -/***/ "./src/js/media/views/attachment/edit-library.js": -/***/ (function(module, exports) { - -/** - * wp.media.view.Attachment.EditLibrary - * - * @memberOf wp.media.view.Attachment - * - * @class - * @augments wp.media.view.Attachment - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -var EditLibrary = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.EditLibrary.prototype */{ - buttons: { - close: true - } -}); - -module.exports = EditLibrary; - - -/***/ }), - -/***/ "./src/js/media/views/attachment/edit-selection.js": -/***/ (function(module, exports) { - -/** - * wp.media.view.Attachment.EditSelection - * - * @memberOf wp.media.view.Attachment - * - * @class - * @augments wp.media.view.Attachment.Selection - * @augments wp.media.view.Attachment - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -var EditSelection = wp.media.view.Attachment.Selection.extend(/** @lends wp.media.view.Attachment.EditSelection.prototype */{ - buttons: { - close: true - } -}); - -module.exports = EditSelection; - - -/***/ }), - -/***/ "./src/js/media/views/attachment/library.js": +/***/ "2jku": /***/ (function(module, exports) { /** @@ -4050,1875 +668,15 @@ module.exports = Library; /***/ }), -/***/ "./src/js/media/views/attachment/selection.js": -/***/ (function(module, exports) { +/***/ 3: +/***/ (function(module, exports, __webpack_require__) { -/** - * wp.media.view.Attachment.Selection - * - * @memberOf wp.media.view.Attachment - * - * @class - * @augments wp.media.view.Attachment - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ -var Selection = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Selection.prototype */{ - className: 'attachment selection', - - // On click, just select the model, instead of removing the model from - // the selection. - toggleSelection: function() { - this.options.selection.single( this.model ); - } -}); - -module.exports = Selection; +module.exports = __webpack_require__("tg/Y"); /***/ }), -/***/ "./src/js/media/views/attachments.js": -/***/ (function(module, exports) { - -var View = wp.media.View, - $ = jQuery, - Attachments, - infiniteScrolling = wp.media.view.settings.infiniteScrolling; - -Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ - tagName: 'ul', - className: 'attachments', - - attributes: { - tabIndex: -1 - }, - - /** - * Represents the overview of attachments in the Media Library. - * - * The constructor binds events to the collection this view represents when - * adding or removing attachments or resetting the entire collection. - * - * @since 3.5.0 - * - * @constructs - * @memberof wp.media.view - * - * @augments wp.media.View - * - * @listens collection:add - * @listens collection:remove - * @listens collection:reset - * @listens controller:library:selection:add - * @listens scrollElement:scroll - * @listens this:ready - * @listens controller:open - */ - initialize: function() { - this.el.id = _.uniqueId('__attachments-view-'); - - /** - * @since 5.8.0 Added the `infiniteScrolling` parameter. - * - * @param infiniteScrolling Whether to enable infinite scrolling or use - * the default "load more" button. - * @param refreshSensitivity The time in milliseconds to throttle the scroll - * handler. - * @param refreshThreshold The amount of pixels that should be scrolled before - * loading more attachments from the server. - * @param AttachmentView The view class to be used for models in the - * collection. - * @param sortable A jQuery sortable options object - * ( http://api.jqueryui.com/sortable/ ). - * @param resize A boolean indicating whether or not to listen to - * resize events. - * @param idealColumnWidth The width in pixels which a column should have when - * calculating the total number of columns. - */ - _.defaults( this.options, { - infiniteScrolling: infiniteScrolling || false, - refreshSensitivity: wp.media.isTouchDevice ? 300 : 200, - refreshThreshold: 3, - AttachmentView: wp.media.view.Attachment, - sortable: false, - resize: true, - idealColumnWidth: $( window ).width() < 640 ? 135 : 150 - }); - - this._viewsByCid = {}; - this.$window = $( window ); - this.resizeEvent = 'resize.media-modal-columns'; - - this.collection.on( 'add', function( attachment ) { - this.views.add( this.createAttachmentView( attachment ), { - at: this.collection.indexOf( attachment ) - }); - }, this ); - - /* - * Find the view to be removed, delete it and call the remove function to clear - * any set event handlers. - */ - this.collection.on( 'remove', function( attachment ) { - var view = this._viewsByCid[ attachment.cid ]; - delete this._viewsByCid[ attachment.cid ]; - - if ( view ) { - view.remove(); - } - }, this ); - - this.collection.on( 'reset', this.render, this ); - - this.controller.on( 'library:selection:add', this.attachmentFocus, this ); - - if ( this.options.infiniteScrolling ) { - // Throttle the scroll handler and bind this. - this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value(); - - this.options.scrollElement = this.options.scrollElement || this.el; - $( this.options.scrollElement ).on( 'scroll', this.scroll ); - } - - this.initSortable(); - - _.bindAll( this, 'setColumns' ); - - if ( this.options.resize ) { - this.on( 'ready', this.bindEvents ); - this.controller.on( 'open', this.setColumns ); - - /* - * Call this.setColumns() after this view has been rendered in the - * DOM so attachments get proper width applied. - */ - _.defer( this.setColumns, this ); - } - }, - - /** - * Listens to the resizeEvent on the window. - * - * Adjusts the amount of columns accordingly. First removes any existing event - * handlers to prevent duplicate listeners. - * - * @since 4.0.0 - * - * @listens window:resize - * - * @return {void} - */ - bindEvents: function() { - this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) ); - }, - - /** - * Focuses the first item in the collection. - * - * @since 4.0.0 - * - * @return {void} - */ - attachmentFocus: function() { - /* - * @todo When uploading new attachments, this tries to move focus to - * the attachments grid. Actually, a progress bar gets initially displayed - * and then updated when uploading completes, so focus is lost. - * Additionally: this view is used for both the attachments list and - * the list of selected attachments in the bottom media toolbar. Thus, when - * uploading attachments, it is called twice and returns two different `this`. - * `this.columns` is truthy within the modal. - */ - if ( this.columns ) { - // Move focus to the grid list within the modal. - this.$el.focus(); - } - }, - - /** - * Restores focus to the selected item in the collection. - * - * Moves focus back to the first selected attachment in the grid. Used when - * tabbing backwards from the attachment details sidebar. - * See media.view.AttachmentsBrowser. - * - * @since 4.0.0 - * - * @return {void} - */ - restoreFocus: function() { - this.$( 'li.selected:first' ).focus(); - }, - - /** - * Handles events for arrow key presses. - * - * Focuses the attachment in the direction of the used arrow key if it exists. - * - * @since 4.0.0 - * - * @param {KeyboardEvent} event The keyboard event that triggered this function. - * - * @return {void} - */ - arrowEvent: function( event ) { - var attachments = this.$el.children( 'li' ), - perRow = this.columns, - index = attachments.filter( ':focus' ).index(), - row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow ); - - if ( index === -1 ) { - return; - } - - // Left arrow = 37. - if ( 37 === event.keyCode ) { - if ( 0 === index ) { - return; - } - attachments.eq( index - 1 ).focus(); - } - - // Up arrow = 38. - if ( 38 === event.keyCode ) { - if ( 1 === row ) { - return; - } - attachments.eq( index - perRow ).focus(); - } - - // Right arrow = 39. - if ( 39 === event.keyCode ) { - if ( attachments.length === index ) { - return; - } - attachments.eq( index + 1 ).focus(); - } - - // Down arrow = 40. - if ( 40 === event.keyCode ) { - if ( Math.ceil( attachments.length / perRow ) === row ) { - return; - } - attachments.eq( index + perRow ).focus(); - } - }, - - /** - * Clears any set event handlers. - * - * @since 3.5.0 - * - * @return {void} - */ - dispose: function() { - this.collection.props.off( null, null, this ); - if ( this.options.resize ) { - this.$window.off( this.resizeEvent ); - } - - // Call 'dispose' directly on the parent class. - View.prototype.dispose.apply( this, arguments ); - }, - - /** - * Calculates the amount of columns. - * - * Calculates the amount of columns and sets it on the data-columns attribute - * of .media-frame-content. - * - * @since 4.0.0 - * - * @return {void} - */ - setColumns: function() { - var prev = this.columns, - width = this.$el.width(); - - if ( width ) { - this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1; - - if ( ! prev || prev !== this.columns ) { - this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns ); - } - } - }, - - /** - * Initializes jQuery sortable on the attachment list. - * - * Fails gracefully if jQuery sortable doesn't exist or isn't passed - * in the options. - * - * @since 3.5.0 - * - * @fires collection:reset - * - * @return {void} - */ - initSortable: function() { - var collection = this.collection; - - if ( ! this.options.sortable || ! $.fn.sortable ) { - return; - } - - this.$el.sortable( _.extend({ - // If the `collection` has a `comparator`, disable sorting. - disabled: !! collection.comparator, - - /* - * Change the position of the attachment as soon as the mouse pointer - * overlaps a thumbnail. - */ - tolerance: 'pointer', - - // Record the initial `index` of the dragged model. - start: function( event, ui ) { - ui.item.data('sortableIndexStart', ui.item.index()); - }, - - /* - * Update the model's index in the collection. Do so silently, as the view - * is already accurate. - */ - update: function( event, ui ) { - var model = collection.at( ui.item.data('sortableIndexStart') ), - comparator = collection.comparator; - - // Temporarily disable the comparator to prevent `add` - // from re-sorting. - delete collection.comparator; - - // Silently shift the model to its new index. - collection.remove( model, { - silent: true - }); - collection.add( model, { - silent: true, - at: ui.item.index() - }); - - // Restore the comparator. - collection.comparator = comparator; - - // Fire the `reset` event to ensure other collections sync. - collection.trigger( 'reset', collection ); - - // If the collection is sorted by menu order, update the menu order. - collection.saveMenuOrder(); - } - }, this.options.sortable ) ); - - /* - * If the `orderby` property is changed on the `collection`, - * check to see if we have a `comparator`. If so, disable sorting. - */ - collection.props.on( 'change:orderby', function() { - this.$el.sortable( 'option', 'disabled', !! collection.comparator ); - }, this ); - - this.collection.props.on( 'change:orderby', this.refreshSortable, this ); - this.refreshSortable(); - }, - - /** - * Disables jQuery sortable if collection has a comparator or collection.orderby - * equals menuOrder. - * - * @since 3.5.0 - * - * @return {void} - */ - refreshSortable: function() { - if ( ! this.options.sortable || ! $.fn.sortable ) { - return; - } - - var collection = this.collection, - orderby = collection.props.get('orderby'), - enabled = 'menuOrder' === orderby || ! collection.comparator; - - this.$el.sortable( 'option', 'disabled', ! enabled ); - }, - - /** - * Creates a new view for an attachment and adds it to _viewsByCid. - * - * @since 3.5.0 - * - * @param {wp.media.model.Attachment} attachment - * - * @return {wp.media.View} The created view. - */ - createAttachmentView: function( attachment ) { - var view = new this.options.AttachmentView({ - controller: this.controller, - model: attachment, - collection: this.collection, - selection: this.options.selection - }); - - return this._viewsByCid[ attachment.cid ] = view; - }, - - /** - * Prepares view for display. - * - * Creates views for every attachment in collection if the collection is not - * empty, otherwise clears all views and loads more attachments. - * - * @since 3.5.0 - * - * @return {void} - */ - prepare: function() { - if ( this.collection.length ) { - this.views.set( this.collection.map( this.createAttachmentView, this ) ); - } else { - this.views.unset(); - if ( this.options.infiniteScrolling ) { - this.collection.more().done( this.scroll ); - } - } - }, - - /** - * Triggers the scroll function to check if we should query for additional - * attachments right away. - * - * @since 3.5.0 - * - * @return {void} - */ - ready: function() { - if ( this.options.infiniteScrolling ) { - this.scroll(); - } - }, - - /** - * Handles scroll events. - * - * Shows the spinner if we're close to the bottom. Loads more attachments from - * server if we're {refreshThreshold} times away from the bottom. - * - * @since 3.5.0 - * - * @return {void} - */ - scroll: function() { - var view = this, - el = this.options.scrollElement, - scrollTop = el.scrollTop, - toolbar; - - /* - * The scroll event occurs on the document, but the element that should be - * checked is the document body. - */ - if ( el === document ) { - el = document.body; - scrollTop = $(document).scrollTop(); - } - - if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) { - return; - } - - toolbar = this.views.parent.toolbar; - - // Show the spinner only if we are close to the bottom. - if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) { - toolbar.get('spinner').show(); - } - - if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) { - this.collection.more().done(function() { - view.scroll(); - toolbar.get('spinner').hide(); - }); - } - } -}); - -module.exports = Attachments; - - -/***/ }), - -/***/ "./src/js/media/views/attachments/browser.js": -/***/ (function(module, exports) { - -var View = wp.media.View, - mediaTrash = wp.media.view.settings.mediaTrash, - l10n = wp.media.view.l10n, - $ = jQuery, - AttachmentsBrowser, - infiniteScrolling = wp.media.view.settings.infiniteScrolling, - __ = wp.i18n.__, - sprintf = wp.i18n.sprintf; - -/** - * wp.media.view.AttachmentsBrowser - * - * @memberOf wp.media.view - * - * @class - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - * - * @param {object} [options] The options hash passed to the view. - * @param {boolean|string} [options.filters=false] Which filters to show in the browser's toolbar. - * Accepts 'uploaded' and 'all'. - * @param {boolean} [options.search=true] Whether to show the search interface in the - * browser's toolbar. - * @param {boolean} [options.date=true] Whether to show the date filter in the - * browser's toolbar. - * @param {boolean} [options.display=false] Whether to show the attachments display settings - * view in the sidebar. - * @param {boolean|string} [options.sidebar=true] Whether to create a sidebar for the browser. - * Accepts true, false, and 'errors'. - */ -AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.prototype */{ - tagName: 'div', - className: 'attachments-browser', - - initialize: function() { - _.defaults( this.options, { - filters: false, - search: true, - date: true, - display: false, - sidebar: true, - AttachmentView: wp.media.view.Attachment.Library - }); - - this.controller.on( 'toggle:upload:attachment', this.toggleUploader, this ); - this.controller.on( 'edit:selection', this.editSelection ); - - // In the Media Library, the sidebar is used to display errors before the attachments grid. - if ( this.options.sidebar && 'errors' === this.options.sidebar ) { - this.createSidebar(); - } - - /* - * In the grid mode (the Media Library), place the Inline Uploader before - * other sections so that the visual order and the DOM order match. This way, - * the Inline Uploader in the Media Library is right after the "Add New" - * button, see ticket #37188. - */ - if ( this.controller.isModeActive( 'grid' ) ) { - this.createUploader(); - - /* - * Create a multi-purpose toolbar. Used as main toolbar in the Media Library - * and also for other things, for example the "Drag and drop to reorder" and - * "Suggested dimensions" info in the media modal. - */ - this.createToolbar(); - } else { - this.createToolbar(); - this.createUploader(); - } - - // Add a heading before the attachments list. - this.createAttachmentsHeading(); - - // Create the attachments wrapper view. - this.createAttachmentsWrapperView(); - - if ( ! infiniteScrolling ) { - this.$el.addClass( 'has-load-more' ); - this.createLoadMoreView(); - } - - // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909. - if ( this.options.sidebar && 'errors' !== this.options.sidebar ) { - this.createSidebar(); - } - - this.updateContent(); - - if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) { - this.$el.addClass( 'hide-sidebar' ); - - if ( 'errors' === this.options.sidebar ) { - this.$el.addClass( 'sidebar-for-errors' ); - } - } - - this.collection.on( 'add remove reset', this.updateContent, this ); - - if ( ! infiniteScrolling ) { - this.collection.on( 'add remove reset', this.updateLoadMoreView, this ); - } - - // The non-cached or cached attachments query has completed. - this.collection.on( 'attachments:received', this.announceSearchResults, this ); - }, - - /** - * Updates the `wp.a11y.speak()` ARIA live region with a message to communicate - * the number of search results to screen reader users. This function is - * debounced because the collection updates multiple times. - * - * @since 5.3.0 - * - * @return {void} - */ - announceSearchResults: _.debounce( function() { - var count, - /* translators: Accessibility text. %d: Number of attachments found in a search. */ - mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Click load more for more results.' ); - - if ( infiniteScrolling ) { - /* translators: Accessibility text. %d: Number of attachments found in a search. */ - mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Scroll the page for more results.' ); - } - - if ( this.collection.mirroring.args.s ) { - count = this.collection.length; - - if ( 0 === count ) { - wp.a11y.speak( l10n.noMediaTryNewSearch ); - return; - } - - if ( this.collection.hasMore() ) { - wp.a11y.speak( mediaFoundHasMoreResultsMessage.replace( '%d', count ) ); - return; - } - - wp.a11y.speak( l10n.mediaFound.replace( '%d', count ) ); - } - }, 200 ), - - editSelection: function( modal ) { - // When editing a selection, move focus to the "Go to library" button. - modal.$( '.media-button-backToLibrary' ).focus(); - }, - - /** - * @return {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining. - */ - dispose: function() { - this.options.selection.off( null, null, this ); - View.prototype.dispose.apply( this, arguments ); - return this; - }, - - createToolbar: function() { - var LibraryViewSwitcher, Filters, toolbarOptions, - showFilterByType = -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] ); - - toolbarOptions = { - controller: this.controller - }; - - if ( this.controller.isModeActive( 'grid' ) ) { - toolbarOptions.className = 'media-toolbar wp-filter'; - } - - /** - * @member {wp.media.view.Toolbar} - */ - this.toolbar = new wp.media.view.Toolbar( toolbarOptions ); - - this.views.add( this.toolbar ); - - this.toolbar.set( 'spinner', new wp.media.view.Spinner({ - priority: -20 - }) ); - - if ( showFilterByType || this.options.date ) { - /* - * Create a h2 heading before the select elements that filter attachments. - * This heading is visible in the modal and visually hidden in the grid. - */ - this.toolbar.set( 'filters-heading', new wp.media.view.Heading( { - priority: -100, - text: l10n.filterAttachments, - level: 'h2', - className: 'media-attachments-filter-heading' - }).render() ); - } - - if ( showFilterByType ) { - // "Filters" is a , a visually hidden label element needs to be rendered before. - this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({ - value: l10n.filterByDate, - attributes: { - 'for': 'media-attachment-date-filters' - }, - priority: -75 - }).render() ); - this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({ - controller: this.controller, - model: this.collection.props, - priority: -75 - }).render() ); - - // BulkSelection is a
with subviews, including screen reader text. - this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({ - text: l10n.bulkSelect, - controller: this.controller, - priority: -70 - }).render() ); - - this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({ - filters: Filters, - style: 'primary', - disabled: true, - text: mediaTrash ? l10n.trashSelected : l10n.deletePermanently, - controller: this.controller, - priority: -80, - click: function() { - var changed = [], removed = [], - selection = this.controller.state().get( 'selection' ), - library = this.controller.state().get( 'library' ); - - if ( ! selection.length ) { - return; - } - - if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) { - return; - } - - if ( mediaTrash && - 'trash' !== selection.at( 0 ).get( 'status' ) && - ! window.confirm( l10n.warnBulkTrash ) ) { - - return; - } - - selection.each( function( model ) { - if ( ! model.get( 'nonces' )['delete'] ) { - removed.push( model ); - return; - } - - if ( mediaTrash && 'trash' === model.get( 'status' ) ) { - model.set( 'status', 'inherit' ); - changed.push( model.save() ); - removed.push( model ); - } else if ( mediaTrash ) { - model.set( 'status', 'trash' ); - changed.push( model.save() ); - removed.push( model ); - } else { - model.destroy({wait: true}); - } - } ); - - if ( changed.length ) { - selection.remove( removed ); - - $.when.apply( null, changed ).then( _.bind( function() { - library._requery( true ); - this.controller.trigger( 'selection:action:done' ); - }, this ) ); - } else { - this.controller.trigger( 'selection:action:done' ); - } - } - }).render() ); - - if ( mediaTrash ) { - this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({ - filters: Filters, - style: 'link button-link-delete', - disabled: true, - text: l10n.deletePermanently, - controller: this.controller, - priority: -55, - click: function() { - var removed = [], - destroy = [], - selection = this.controller.state().get( 'selection' ); - - if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) { - return; - } - - selection.each( function( model ) { - if ( ! model.get( 'nonces' )['delete'] ) { - removed.push( model ); - return; - } - - destroy.push( model ); - } ); - - if ( removed.length ) { - selection.remove( removed ); - } - - if ( destroy.length ) { - $.when.apply( null, destroy.map( function (item) { - return item.destroy(); - } ) ).then( _.bind( function() { - this.controller.trigger( 'selection:action:done' ); - }, this ) ); - } - } - }).render() ); - } - - } else if ( this.options.date ) { - // DateFilter is a ' ) - .attr( 'aria-label', l10n.insertFromUrlTitle ) - .val( this.model.get('url') ); - this.input = this.$input[0]; - - this.spinner = $('')[0]; - this.$el.append([ this.input, this.spinner ]); - - this.listenTo( this.model, 'change:url', this.render ); - - if ( this.model.get( 'url' ) ) { - _.delay( _.bind( function () { - this.model.trigger( 'change:url' ); - }, this ), 500 ); - } - }, - /** - * @return {wp.media.view.EmbedUrl} Returns itself to allow chaining. - */ - render: function() { - var $input = this.$input; - - if ( $input.is(':focus') ) { - return; - } - - this.input.value = this.model.get('url') || 'http://'; - /** - * Call `render` directly on parent class with passed arguments - */ - View.prototype.render.apply( this, arguments ); - return this; - }, - - url: function( event ) { - var url = event.target.value || ''; - this.model.set( 'url', url.trim() ); - } -}); - -module.exports = EmbedUrl; - - -/***/ }), - -/***/ "./src/js/media/views/focus-manager.js": +/***/ "3nJM": /***/ (function(module, exports) { var $ = jQuery; @@ -6286,319 +1044,169 @@ module.exports = FocusManager; /***/ }), -/***/ "./src/js/media/views/frame.js": +/***/ "4jjk": /***/ (function(module, exports) { +var l10n = wp.media.view.l10n, + Uploaded; + /** - * wp.media.view.Frame + * wp.media.view.AttachmentFilters.Uploaded * - * A frame is a composite view consisting of one or more regions and one or more - * states. - * - * @memberOf wp.media.view - * - * @see wp.media.controller.State - * @see wp.media.controller.Region + * @memberOf wp.media.view.AttachmentFilters * * @class + * @augments wp.media.view.AttachmentFilters * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View - * @mixes wp.media.controller.StateMachine */ -var Frame = wp.media.View.extend(/** @lends wp.media.view.Frame.prototype */{ - initialize: function() { - _.defaults( this.options, { - mode: [ 'select' ] - }); - this._createRegions(); - this._createStates(); - this._createModes(); - }, +Uploaded = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Uploaded.prototype */{ + createFilters: function() { + var type = this.model.get('type'), + types = wp.media.view.settings.mimeTypes, + uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0, + text; - _createRegions: function() { - // Clone the regions array. - this.regions = this.regions ? this.regions.slice() : []; - - // Initialize regions. - _.each( this.regions, function( region ) { - this[ region ] = new wp.media.controller.Region({ - view: this, - id: region, - selector: '.media-frame-' + region - }); - }, this ); - }, - /** - * Create the frame's states. - * - * @see wp.media.controller.State - * @see wp.media.controller.StateMachine - * - * @fires wp.media.controller.State#ready - */ - _createStates: function() { - // Create the default `states` collection. - this.states = new Backbone.Collection( null, { - model: wp.media.controller.State - }); - - // Ensure states have a reference to the frame. - this.states.on( 'add', function( model ) { - model.frame = this; - model.trigger('ready'); - }, this ); - - if ( this.options.states ) { - this.states.add( this.options.states ); + if ( types && type ) { + text = types[ type ]; } - }, - /** - * A frame can be in a mode or multiple modes at one time. - * - * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode. - */ - _createModes: function() { - // Store active "modes" that the frame is in. Unrelated to region modes. - this.activeModes = new Backbone.Collection(); - this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) ); - - _.each( this.options.mode, function( mode ) { - this.activateMode( mode ); - }, this ); - }, - /** - * Reset all states on the frame to their defaults. - * - * @return {wp.media.view.Frame} Returns itself to allow chaining. - */ - reset: function() { - this.states.invoke( 'trigger', 'reset' ); - return this; - }, - /** - * Map activeMode collection events to the frame. - */ - triggerModeEvents: function( model, collection, options ) { - var collectionEvent, - modeEventMap = { - add: 'activate', - remove: 'deactivate' + this.filters = { + all: { + text: text || l10n.allMediaItems, + props: { + uploadedTo: null, + orderby: 'date', + order: 'DESC', + author: null + }, + priority: 10 }, - eventToTrigger; - // Probably a better way to do this. - _.each( options, function( value, key ) { - if ( value ) { - collectionEvent = key; + + uploaded: { + text: l10n.uploadedToThisPost, + props: { + uploadedTo: wp.media.view.settings.post.id, + orderby: 'menuOrder', + order: 'ASC', + author: null + }, + priority: 20 + }, + + unattached: { + text: l10n.unattached, + props: { + uploadedTo: 0, + orderby: 'menuOrder', + order: 'ASC', + author: null + }, + priority: 50 } - } ); + }; - if ( ! _.has( modeEventMap, collectionEvent ) ) { - return; + if ( uid ) { + this.filters.mine = { + text: l10n.mine, + props: { + orderby: 'date', + order: 'DESC', + author: uid + }, + priority: 50 + }; } - - eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent]; - this.trigger( eventToTrigger ); - }, - /** - * Activate a mode on the frame. - * - * @param string mode Mode ID. - * @return {this} Returns itself to allow chaining. - */ - activateMode: function( mode ) { - // Bail if the mode is already active. - if ( this.isModeActive( mode ) ) { - return; - } - this.activeModes.add( [ { id: mode } ] ); - // Add a CSS class to the frame so elements can be styled for the mode. - this.$el.addClass( 'mode-' + mode ); - - return this; - }, - /** - * Deactivate a mode on the frame. - * - * @param string mode Mode ID. - * @return {this} Returns itself to allow chaining. - */ - deactivateMode: function( mode ) { - // Bail if the mode isn't active. - if ( ! this.isModeActive( mode ) ) { - return this; - } - this.activeModes.remove( this.activeModes.where( { id: mode } ) ); - this.$el.removeClass( 'mode-' + mode ); - /** - * Frame mode deactivation event. - * - * @event wp.media.view.Frame#{mode}:deactivate - */ - this.trigger( mode + ':deactivate' ); - - return this; - }, - /** - * Check if a mode is enabled on the frame. - * - * @param string mode Mode ID. - * @return bool - */ - isModeActive: function( mode ) { - return Boolean( this.activeModes.where( { id: mode } ).length ); } }); -// Make the `Frame` a `StateMachine`. -_.extend( Frame.prototype, wp.media.controller.StateMachine.prototype ); - -module.exports = Frame; +module.exports = Uploaded; /***/ }), -/***/ "./src/js/media/views/frame/image-details.js": +/***/ "4tHu": /***/ (function(module, exports) { -var Select = wp.media.view.MediaFrame.Select, - l10n = wp.media.view.l10n, - ImageDetails; +var l10n = wp.media.view.l10n, + EditImage; /** - * wp.media.view.MediaFrame.ImageDetails + * wp.media.controller.EditImage * - * A media frame for manipulating an image that's already been inserted - * into a post. + * A state for editing (cropping, etc.) an image. * - * @memberOf wp.media.view.MediaFrame + * @memberOf wp.media.controller * * @class - * @augments wp.media.view.MediaFrame.Select - * @augments wp.media.view.MediaFrame - * @augments wp.media.view.Frame - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - * @mixes wp.media.controller.StateMachine + * @augments wp.media.controller.State + * @augments Backbone.Model + * + * @param {object} attributes The attributes hash passed to the state. + * @param {wp.media.model.Attachment} attributes.model The attachment. + * @param {string} [attributes.id=edit-image] Unique identifier. + * @param {string} [attributes.title=Edit Image] Title for the state. Displays in the media menu and the frame's title region. + * @param {string} [attributes.content=edit-image] Initial mode for the content region. + * @param {string} [attributes.toolbar=edit-image] Initial mode for the toolbar region. + * @param {string} [attributes.menu=false] Initial mode for the menu region. + * @param {string} [attributes.url] Unused. @todo Consider removal. */ -ImageDetails = Select.extend(/** @lends wp.media.view.MediaFrame.ImageDetails.prototype */{ +EditImage = wp.media.controller.State.extend(/** @lends wp.media.controller.EditImage.prototype */{ defaults: { - id: 'image', - url: '', - menu: 'image-details', - content: 'image-details', - toolbar: 'image-details', - type: 'link', - title: l10n.imageDetailsTitle, - priority: 120 + id: 'edit-image', + title: l10n.editImage, + menu: false, + toolbar: 'edit-image', + content: 'edit-image', + url: '' }, - initialize: function( options ) { - this.image = new wp.media.model.PostImage( options.metadata ); - this.options.selection = new wp.media.model.Selection( this.image.attachment, { multiple: false } ); - Select.prototype.initialize.apply( this, arguments ); + /** + * Activates a frame for editing a featured image. + * + * @since 3.9.0 + * + * @return {void} + */ + activate: function() { + this.frame.on( 'toolbar:render:edit-image', _.bind( this.toolbar, this ) ); }, - bindHandlers: function() { - Select.prototype.bindHandlers.apply( this, arguments ); - this.on( 'menu:create:image-details', this.createMenu, this ); - this.on( 'content:create:image-details', this.imageDetailsContent, this ); - this.on( 'content:render:edit-image', this.editImageContent, this ); - this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this ); - // Override the select toolbar. - this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this ); + /** + * Deactivates a frame for editing a featured image. + * + * @since 3.9.0 + * + * @return {void} + */ + deactivate: function() { + this.frame.off( 'toolbar:render:edit-image' ); }, - createStates: function() { - this.states.add([ - new wp.media.controller.ImageDetails({ - image: this.image, - editable: false - }), - new wp.media.controller.ReplaceImage({ - id: 'replace-image', - library: wp.media.query( { type: 'image' } ), - image: this.image, - multiple: false, - title: l10n.imageReplaceTitle, - toolbar: 'replace', - priority: 80, - displaySettings: true - }), - new wp.media.controller.EditImage( { - image: this.image, - selection: this.options.selection - } ) - ]); - }, - - imageDetailsContent: function( options ) { - options.view = new wp.media.view.ImageDetails({ - controller: this, - model: this.state().image, - attachment: this.state().image.attachment - }); - }, - - editImageContent: function() { - var state = this.state(), - model = state.get('image'), - view; - - if ( ! model ) { - return; - } - - view = new wp.media.view.EditImage( { model: model, controller: this } ).render(); - - this.content.set( view ); - - // After bringing in the frame, load the actual editor via an Ajax call. - view.loadEditor(); - - }, - - renderImageDetailsToolbar: function() { - this.toolbar.set( new wp.media.view.Toolbar({ - controller: this, - items: { - select: { - style: 'primary', - text: l10n.update, - priority: 80, - - click: function() { - var controller = this.controller, - state = controller.state(); - - controller.close(); - - // Not sure if we want to use wp.media.string.image which will create a shortcode or - // perhaps wp.html.string to at least to build the . - state.trigger( 'update', controller.image.toJSON() ); - - // Restore and reset the default state. - controller.setState( controller.options.state ); - controller.reset(); - } - } - } - }) ); - }, - - renderReplaceImageToolbar: function() { - var frame = this, + /** + * Adds a toolbar with a back button. + * + * When the back button is pressed it checks whether there is a previous state. + * In case there is a previous state it sets that previous state otherwise it + * closes the frame. + * + * @since 3.9.0 + * + * @return {void} + */ + toolbar: function() { + var frame = this.frame, lastState = frame.lastState(), previous = lastState && lastState.id; - this.toolbar.set( new wp.media.view.Toolbar({ - controller: this, + frame.toolbar.set( new wp.media.view.Toolbar({ + controller: frame, items: { back: { + style: 'primary', text: l10n.back, - priority: 80, + priority: 20, click: function() { if ( previous ) { frame.setState( previous ); @@ -6606,45 +1214,18 @@ ImageDetails = Select.extend(/** @lends wp.media.view.MediaFrame.ImageDetails.pr frame.close(); } } - }, - - replace: { - style: 'primary', - text: l10n.replace, - priority: 20, - requires: { selection: true }, - - click: function() { - var controller = this.controller, - state = controller.state(), - selection = state.get( 'selection' ), - attachment = selection.single(); - - controller.close(); - - controller.image.changeAttachment( attachment, state.display( attachment ) ); - - // Not sure if we want to use wp.media.string.image which will create a shortcode or - // perhaps wp.html.string to at least to build the . - state.trigger( 'replace', controller.image.toJSON() ); - - // Restore and reset the default state. - controller.setState( controller.options.state ); - controller.reset(); - } } } }) ); } - }); -module.exports = ImageDetails; +module.exports = EditImage; /***/ }), -/***/ "./src/js/media/views/frame/post.js": +/***/ "6B7g": /***/ (function(module, exports) { var Select = wp.media.view.MediaFrame.Select, @@ -7400,241 +1981,779 @@ module.exports = Post; /***/ }), -/***/ "./src/js/media/views/frame/select.js": +/***/ "72mI": /***/ (function(module, exports) { -var MediaFrame = wp.media.view.MediaFrame, +var View = wp.media.View, + mediaTrash = wp.media.view.settings.mediaTrash, l10n = wp.media.view.l10n, - Select; + $ = jQuery, + AttachmentsBrowser, + infiniteScrolling = wp.media.view.settings.infiniteScrolling, + __ = wp.i18n.__, + sprintf = wp.i18n.sprintf; /** - * wp.media.view.MediaFrame.Select + * wp.media.view.AttachmentsBrowser * - * A frame for selecting an item or items from the media library. - * - * @memberOf wp.media.view.MediaFrame + * @memberOf wp.media.view * * @class - * @augments wp.media.view.MediaFrame - * @augments wp.media.view.Frame * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View - * @mixes wp.media.controller.StateMachine + * + * @param {object} [options] The options hash passed to the view. + * @param {boolean|string} [options.filters=false] Which filters to show in the browser's toolbar. + * Accepts 'uploaded' and 'all'. + * @param {boolean} [options.search=true] Whether to show the search interface in the + * browser's toolbar. + * @param {boolean} [options.date=true] Whether to show the date filter in the + * browser's toolbar. + * @param {boolean} [options.display=false] Whether to show the attachments display settings + * view in the sidebar. + * @param {boolean|string} [options.sidebar=true] Whether to create a sidebar for the browser. + * Accepts true, false, and 'errors'. */ -Select = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Select.prototype */{ - initialize: function() { - // Call 'initialize' directly on the parent class. - MediaFrame.prototype.initialize.apply( this, arguments ); +AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.prototype */{ + tagName: 'div', + className: 'attachments-browser', + initialize: function() { _.defaults( this.options, { - selection: [], - library: {}, - multiple: false, - state: 'library' + filters: false, + search: true, + date: true, + display: false, + sidebar: true, + AttachmentView: wp.media.view.Attachment.Library }); - this.createSelection(); - this.createStates(); - this.bindHandlers(); - }, + this.controller.on( 'toggle:upload:attachment', this.toggleUploader, this ); + this.controller.on( 'edit:selection', this.editSelection ); - /** - * Attach a selection collection to the frame. - * - * A selection is a collection of attachments used for a specific purpose - * by a media frame. e.g. Selecting an attachment (or many) to insert into - * post content. - * - * @see media.model.Selection - */ - createSelection: function() { - var selection = this.options.selection; - - if ( ! (selection instanceof wp.media.model.Selection) ) { - this.options.selection = new wp.media.model.Selection( selection, { - multiple: this.options.multiple - }); + // In the Media Library, the sidebar is used to display errors before the attachments grid. + if ( this.options.sidebar && 'errors' === this.options.sidebar ) { + this.createSidebar(); } - this._selection = { - attachments: new wp.media.model.Attachments(), - difference: [] - }; - }, + /* + * In the grid mode (the Media Library), place the Inline Uploader before + * other sections so that the visual order and the DOM order match. This way, + * the Inline Uploader in the Media Library is right after the "Add New" + * button, see ticket #37188. + */ + if ( this.controller.isModeActive( 'grid' ) ) { + this.createUploader(); - editImageContent: function() { - var image = this.state().get('image'), - view = new wp.media.view.EditImage( { model: image, controller: this } ).render(); + /* + * Create a multi-purpose toolbar. Used as main toolbar in the Media Library + * and also for other things, for example the "Drag and drop to reorder" and + * "Suggested dimensions" info in the media modal. + */ + this.createToolbar(); + } else { + this.createToolbar(); + this.createUploader(); + } - this.content.set( view ); + // Add a heading before the attachments list. + this.createAttachmentsHeading(); - // After creating the wrapper view, load the actual editor via an Ajax call. - view.loadEditor(); + // Create the attachments wrapper view. + this.createAttachmentsWrapperView(); + + if ( ! infiniteScrolling ) { + this.$el.addClass( 'has-load-more' ); + this.createLoadMoreView(); + } + + // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909. + if ( this.options.sidebar && 'errors' !== this.options.sidebar ) { + this.createSidebar(); + } + + this.updateContent(); + + if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) { + this.$el.addClass( 'hide-sidebar' ); + + if ( 'errors' === this.options.sidebar ) { + this.$el.addClass( 'sidebar-for-errors' ); + } + } + + this.collection.on( 'add remove reset', this.updateContent, this ); + + if ( ! infiniteScrolling ) { + this.collection.on( 'add remove reset', this.updateLoadMoreView, this ); + } + + // The non-cached or cached attachments query has completed. + this.collection.on( 'attachments:received', this.announceSearchResults, this ); }, /** - * Create the default states on the frame. + * Updates the `wp.a11y.speak()` ARIA live region with a message to communicate + * the number of search results to screen reader users. This function is + * debounced because the collection updates multiple times. + * + * @since 5.3.0 + * + * @return {void} */ - createStates: function() { - var options = this.options; + announceSearchResults: _.debounce( function() { + var count, + /* translators: Accessibility text. %d: Number of attachments found in a search. */ + mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Click load more for more results.' ); - if ( this.options.states ) { + if ( infiniteScrolling ) { + /* translators: Accessibility text. %d: Number of attachments found in a search. */ + mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Scroll the page for more results.' ); + } + + if ( this.collection.mirroring.args.s ) { + count = this.collection.length; + + if ( 0 === count ) { + wp.a11y.speak( l10n.noMediaTryNewSearch ); + return; + } + + if ( this.collection.hasMore() ) { + wp.a11y.speak( mediaFoundHasMoreResultsMessage.replace( '%d', count ) ); + return; + } + + wp.a11y.speak( l10n.mediaFound.replace( '%d', count ) ); + } + }, 200 ), + + editSelection: function( modal ) { + // When editing a selection, move focus to the "Go to library" button. + modal.$( '.media-button-backToLibrary' ).focus(); + }, + + /** + * @return {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining. + */ + dispose: function() { + this.options.selection.off( null, null, this ); + View.prototype.dispose.apply( this, arguments ); + return this; + }, + + createToolbar: function() { + var LibraryViewSwitcher, Filters, toolbarOptions, + showFilterByType = -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] ); + + toolbarOptions = { + controller: this.controller + }; + + if ( this.controller.isModeActive( 'grid' ) ) { + toolbarOptions.className = 'media-toolbar wp-filter'; + } + + /** + * @member {wp.media.view.Toolbar} + */ + this.toolbar = new wp.media.view.Toolbar( toolbarOptions ); + + this.views.add( this.toolbar ); + + this.toolbar.set( 'spinner', new wp.media.view.Spinner({ + priority: -20 + }) ); + + if ( showFilterByType || this.options.date ) { + /* + * Create a h2 heading before the select elements that filter attachments. + * This heading is visible in the modal and visually hidden in the grid. + */ + this.toolbar.set( 'filters-heading', new wp.media.view.Heading( { + priority: -100, + text: l10n.filterAttachments, + level: 'h2', + className: 'media-attachments-filter-heading' + }).render() ); + } + + if ( showFilterByType ) { + // "Filters" is a , a visually hidden label element needs to be rendered before. + this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({ + value: l10n.filterByDate, + attributes: { + 'for': 'media-attachment-date-filters' + }, + priority: -75 + }).render() ); + this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({ + controller: this.controller, + model: this.collection.props, + priority: -75 + }).render() ); + + // BulkSelection is a
with subviews, including screen reader text. + this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({ + text: l10n.bulkSelect, + controller: this.controller, + priority: -70 + }).render() ); + + this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({ + filters: Filters, + style: 'primary', + disabled: true, + text: mediaTrash ? l10n.trashSelected : l10n.deletePermanently, + controller: this.controller, + priority: -80, + click: function() { + var changed = [], removed = [], + selection = this.controller.state().get( 'selection' ), + library = this.controller.state().get( 'library' ); + + if ( ! selection.length ) { + return; + } + + if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) { + return; + } + + if ( mediaTrash && + 'trash' !== selection.at( 0 ).get( 'status' ) && + ! window.confirm( l10n.warnBulkTrash ) ) { + + return; + } + + selection.each( function( model ) { + if ( ! model.get( 'nonces' )['delete'] ) { + removed.push( model ); + return; + } + + if ( mediaTrash && 'trash' === model.get( 'status' ) ) { + model.set( 'status', 'inherit' ); + changed.push( model.save() ); + removed.push( model ); + } else if ( mediaTrash ) { + model.set( 'status', 'trash' ); + changed.push( model.save() ); + removed.push( model ); + } else { + model.destroy({wait: true}); + } + } ); + + if ( changed.length ) { + selection.remove( removed ); + + $.when.apply( null, changed ).then( _.bind( function() { + library._requery( true ); + this.controller.trigger( 'selection:action:done' ); + }, this ) ); + } else { + this.controller.trigger( 'selection:action:done' ); + } + } + }).render() ); + + if ( mediaTrash ) { + this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({ + filters: Filters, + style: 'link button-link-delete', + disabled: true, + text: l10n.deletePermanently, + controller: this.controller, + priority: -55, + click: function() { + var removed = [], + destroy = [], + selection = this.controller.state().get( 'selection' ); + + if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) { + return; + } + + selection.each( function( model ) { + if ( ! model.get( 'nonces' )['delete'] ) { + removed.push( model ); + return; + } + + destroy.push( model ); + } ); + + if ( removed.length ) { + selection.remove( removed ); + } + + if ( destroy.length ) { + $.when.apply( null, destroy.map( function (item) { + return item.destroy(); + } ) ).then( _.bind( function() { + this.controller.trigger( 'selection:action:done' ); + }, this ) ); + } + } + }).render() ); + } + + } else if ( this.options.date ) { + // DateFilter is a ').attr("aria-label",o.insertFromUrlTitle).val(this.model.get("url")),this.input=this.$input[0],this.spinner=s('')[0],this.$el.append([this.input,this.spinner]),this.listenTo(this.model,"change:url",this.render),this.model.get("url")&&_.delay(_.bind(function(){this.model.trigger("change:url")},this),500)},render:function(){if(!this.$input.is(":focus"))return this.input.value=this.model.get("url")||"http://",i.prototype.render.apply(this,arguments),this},url:function(e){e=e.target.value||"";this.model.set("url",e.trim())}});e.exports=a},"./src/js/media/views/focus-manager.js":function(e,t){var o=jQuery,i=wp.media.View.extend({events:{keydown:"focusManagementMode"},initialize:function(e){this.mode=e.mode||"constrainTabbing",this.tabsAutomaticActivation=e.tabsAutomaticActivation||!1},focusManagementMode:function(e){"constrainTabbing"===this.mode&&this.constrainTabbing(e),"tabsNavigation"===this.mode&&this.tabsNavigation(e)},getTabbables:function(){return this.$(":tabbable").not('.moxie-shim input[type="file"]')},focus:function(){this.$(".media-modal").trigger("focus")},constrainTabbing:function(e){var t;if(9===e.keyCode)return(t=this.getTabbables()).last()[0]!==e.target||e.shiftKey?t.first()[0]===e.target&&e.shiftKey?(t.last().focus(),!1):void 0:(t.first().focus(),!1)},setAriaHiddenOnBodyChildren:function(t){var e,i=this;this.isBodyAriaHidden||(e=document.body.children,_.each(e,function(e){e!==t[0]&&i.elementShouldBeHidden(e)&&(e.setAttribute("aria-hidden","true"),i.ariaHiddenElements.push(e))}),this.isBodyAriaHidden=!0)},removeAriaHiddenFromBodyChildren:function(){_.each(this.ariaHiddenElements,function(e){e.removeAttribute("aria-hidden")}),this.ariaHiddenElements=[],this.isBodyAriaHidden=!1},elementShouldBeHidden:function(e){var t=e.getAttribute("role");return!("SCRIPT"===e.tagName||e.hasAttribute("aria-hidden")||e.hasAttribute("aria-live")||-1!==["alert","status","log","marquee","timer"].indexOf(t))},isBodyAriaHidden:!1,ariaHiddenElements:[],tabs:o(),setupAriaTabs:function(){this.tabs=this.$('[role="tab"]'),this.tabs.attr({"aria-selected":"false",tabIndex:"-1"}),this.tabs.filter(".active").removeAttr("tabindex").attr("aria-selected","true")},tabsNavigation:function(e){var t="horizontal";-1!==[32,35,36,37,38,39,40].indexOf(e.which)&&("horizontal"===(t="vertical"===this.$el.attr("aria-orientation")?"vertical":t)&&-1!==[38,40].indexOf(e.which)||"vertical"===t&&-1!==[37,39].indexOf(e.which)||this.switchTabs(e,this.tabs))},switchTabs:function(e){var t,i=e.which,s=this.tabs.index(o(e.target));switch(i){case 32:this.activateTab(this.tabs[s]);break;case 35:e.preventDefault(),this.activateTab(this.tabs[this.tabs.length-1]);break;case 36:e.preventDefault(),this.activateTab(this.tabs[0]);break;case 37:case 38:e.preventDefault(),t=s-1<0?this.tabs.length-1:s-1,this.activateTab(this.tabs[t]);break;case 39:case 40:e.preventDefault(),t=s+1===this.tabs.length?0:s+1,this.activateTab(this.tabs[t])}},activateTab:function(e){if(e){if(e.focus(),this.tabsAutomaticActivation)return e.removeAttribute("tabindex"),e.setAttribute("aria-selected","true"),void e.click();o(e).on("click",function(){e.removeAttribute("tabindex"),e.setAttribute("aria-selected","true")})}}});e.exports=i},"./src/js/media/views/frame.js":function(e,t){var i=wp.media.View.extend({initialize:function(){_.defaults(this.options,{mode:["select"]}),this._createRegions(),this._createStates(),this._createModes()},_createRegions:function(){this.regions=this.regions?this.regions.slice():[],_.each(this.regions,function(e){this[e]=new wp.media.controller.Region({view:this,id:e,selector:".media-frame-"+e})},this)},_createStates:function(){this.states=new Backbone.Collection(null,{model:wp.media.controller.State}),this.states.on("add",function(e){e.frame=this,e.trigger("ready")},this),this.options.states&&this.states.add(this.options.states)},_createModes:function(){this.activeModes=new Backbone.Collection,this.activeModes.on("add remove reset",_.bind(this.triggerModeEvents,this)),_.each(this.options.mode,function(e){this.activateMode(e)},this)},reset:function(){return this.states.invoke("trigger","reset"),this},triggerModeEvents:function(e,t,i){var s,o={add:"activate",remove:"deactivate"};_.each(i,function(e,t){e&&(s=t)}),_.has(o,s)&&(o=e.get("id")+":"+o[s],this.trigger(o))},activateMode:function(e){if(!this.isModeActive(e))return this.activeModes.add([{id:e}]),this.$el.addClass("mode-"+e),this},deactivateMode:function(e){return this.isModeActive(e)&&(this.activeModes.remove(this.activeModes.where({id:e})),this.$el.removeClass("mode-"+e),this.trigger(e+":deactivate")),this},isModeActive:function(e){return Boolean(this.activeModes.where({id:e}).length)}});_.extend(i.prototype,wp.media.controller.StateMachine.prototype),e.exports=i},"./src/js/media/views/frame/image-details.js":function(e,t){var i=wp.media.view.MediaFrame.Select,s=wp.media.view.l10n,o=i.extend({defaults:{id:"image",url:"",menu:"image-details",content:"image-details",toolbar:"image-details",type:"link",title:s.imageDetailsTitle,priority:120},initialize:function(e){this.image=new wp.media.model.PostImage(e.metadata),this.options.selection=new wp.media.model.Selection(this.image.attachment,{multiple:!1}),i.prototype.initialize.apply(this,arguments)},bindHandlers:function(){i.prototype.bindHandlers.apply(this,arguments),this.on("menu:create:image-details",this.createMenu,this),this.on("content:create:image-details",this.imageDetailsContent,this),this.on("content:render:edit-image",this.editImageContent,this),this.on("toolbar:render:image-details",this.renderImageDetailsToolbar,this),this.on("toolbar:render:replace",this.renderReplaceImageToolbar,this)},createStates:function(){this.states.add([new wp.media.controller.ImageDetails({image:this.image,editable:!1}),new wp.media.controller.ReplaceImage({id:"replace-image",library:wp.media.query({type:"image"}),image:this.image,multiple:!1,title:s.imageReplaceTitle,toolbar:"replace",priority:80,displaySettings:!0}),new wp.media.controller.EditImage({image:this.image,selection:this.options.selection})])},imageDetailsContent:function(e){e.view=new wp.media.view.ImageDetails({controller:this,model:this.state().image,attachment:this.state().image.attachment})},editImageContent:function(){var e=this.state().get("image");e&&(e=new wp.media.view.EditImage({model:e,controller:this}).render(),this.content.set(e),e.loadEditor())},renderImageDetailsToolbar:function(){this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{select:{style:"primary",text:s.update,priority:80,click:function(){var e=this.controller,t=e.state();e.close(),t.trigger("update",e.image.toJSON()),e.setState(e.options.state),e.reset()}}}}))},renderReplaceImageToolbar:function(){var e=this,t=e.lastState(),i=t&&t.id;this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{back:{text:s.back,priority:80,click:function(){i?e.setState(i):e.close()}},replace:{style:"primary",text:s.replace,priority:20,requires:{selection:!0},click:function(){var e=this.controller,t=e.state(),i=t.get("selection").single();e.close(),e.image.changeAttachment(i,t.display(i)),t.trigger("replace",e.image.toJSON()),e.setState(e.options.state),e.reset()}}}}))}});e.exports=o},"./src/js/media/views/frame/post.js":function(e,t){var i=wp.media.view.MediaFrame.Select,s=wp.media.controller.Library,o=wp.media.view.l10n,a=i.extend({initialize:function(){this.counts={audio:{count:wp.media.view.settings.attachmentCounts.audio,state:"playlist"},video:{count:wp.media.view.settings.attachmentCounts.video,state:"video-playlist"}},_.defaults(this.options,{multiple:!0,editing:!1,state:"insert",metadata:{}}),i.prototype.initialize.apply(this,arguments),this.createIframeStates()},createStates:function(){var e=this.options;this.states.add([new s({id:"insert",title:o.insertMediaTitle,priority:20,toolbar:"main-insert",filterable:"all",library:wp.media.query(e.library),multiple:!!e.multiple&&"reset",editable:!0,allowLocalEdits:!0,displaySettings:!0,displayUserSettings:!0}),new s({id:"gallery",title:o.createGalleryTitle,priority:40,toolbar:"main-gallery",filterable:"uploaded",multiple:"add",editable:!1,library:wp.media.query(_.defaults({type:"image"},e.library))}),new wp.media.controller.Embed({metadata:e.metadata}),new wp.media.controller.EditImage({model:e.editImage}),new wp.media.controller.GalleryEdit({library:e.selection,editing:e.editing,menu:"gallery"}),new wp.media.controller.GalleryAdd,new s({id:"playlist",title:o.createPlaylistTitle,priority:60,toolbar:"main-playlist",filterable:"uploaded",multiple:"add",editable:!1,library:wp.media.query(_.defaults({type:"audio"},e.library))}),new wp.media.controller.CollectionEdit({type:"audio",collectionType:"playlist",title:o.editPlaylistTitle,SettingsView:wp.media.view.Settings.Playlist,library:e.selection,editing:e.editing,menu:"playlist",dragInfoText:o.playlistDragInfo,dragInfo:!1}),new wp.media.controller.CollectionAdd({type:"audio",collectionType:"playlist",title:o.addToPlaylistTitle}),new s({id:"video-playlist",title:o.createVideoPlaylistTitle,priority:60,toolbar:"main-video-playlist",filterable:"uploaded",multiple:"add",editable:!1,library:wp.media.query(_.defaults({type:"video"},e.library))}),new wp.media.controller.CollectionEdit({type:"video",collectionType:"playlist",title:o.editVideoPlaylistTitle,SettingsView:wp.media.view.Settings.Playlist,library:e.selection,editing:e.editing,menu:"video-playlist",dragInfoText:o.videoPlaylistDragInfo,dragInfo:!1}),new wp.media.controller.CollectionAdd({type:"video",collectionType:"playlist",title:o.addToVideoPlaylistTitle})]),wp.media.view.settings.post.featuredImageId&&this.states.add(new wp.media.controller.FeaturedImage)},bindHandlers:function(){i.prototype.bindHandlers.apply(this,arguments),this.on("activate",this.activate,this),void 0!==_.find(this.counts,function(e){return 0===e.count})&&this.listenTo(wp.media.model.Attachments.all,"change:type",this.mediaTypeCounts),this.on("menu:create:gallery",this.createMenu,this),this.on("menu:create:playlist",this.createMenu,this),this.on("menu:create:video-playlist",this.createMenu,this),this.on("toolbar:create:main-insert",this.createToolbar,this),this.on("toolbar:create:main-gallery",this.createToolbar,this),this.on("toolbar:create:main-playlist",this.createToolbar,this),this.on("toolbar:create:main-video-playlist",this.createToolbar,this),this.on("toolbar:create:featured-image",this.featuredImageToolbar,this),this.on("toolbar:create:main-embed",this.mainEmbedToolbar,this),_.each({menu:{default:"mainMenu",gallery:"galleryMenu",playlist:"playlistMenu","video-playlist":"videoPlaylistMenu"},content:{embed:"embedContent","edit-image":"editImageContent","edit-selection":"editSelectionContent"},toolbar:{"main-insert":"mainInsertToolbar","main-gallery":"mainGalleryToolbar","gallery-edit":"galleryEditToolbar","gallery-add":"galleryAddToolbar","main-playlist":"mainPlaylistToolbar","playlist-edit":"playlistEditToolbar","playlist-add":"playlistAddToolbar","main-video-playlist":"mainVideoPlaylistToolbar","video-playlist-edit":"videoPlaylistEditToolbar","video-playlist-add":"videoPlaylistAddToolbar"}},function(e,i){_.each(e,function(e,t){this.on(i+":render:"+t,this[e],this)},this)},this)},activate:function(){_.each(this.counts,function(e){e.count<1&&this.menuItemVisibility(e.state,"hide")},this)},mediaTypeCounts:function(e,t){void 0!==this.counts[t]&&this.counts[t].count<1&&(this.counts[t].count++,this.menuItemVisibility(this.counts[t].state,"show"))},mainMenu:function(e){e.set({"library-separator":new wp.media.View({className:"separator",priority:100,attributes:{role:"presentation"}})})},menuItemVisibility:function(e,t){var i=this.menu.get();"hide"===t?i.hide(e):"show"===t&&i.show(e)},galleryMenu:function(e){var t=this.lastState(),i=t&&t.id,s=this;e.set({cancel:{text:o.cancelGalleryTitle,priority:20,click:function(){i?s.setState(i):s.close(),this.controller.modal.focusManager.focus()}},separateCancel:new wp.media.View({className:"separator",priority:40})})},playlistMenu:function(e){var t=this.lastState(),i=t&&t.id,s=this;e.set({cancel:{text:o.cancelPlaylistTitle,priority:20,click:function(){i?s.setState(i):s.close(),this.controller.modal.focusManager.focus()}},separateCancel:new wp.media.View({className:"separator",priority:40})})},videoPlaylistMenu:function(e){var t=this.lastState(),i=t&&t.id,s=this;e.set({cancel:{text:o.cancelVideoPlaylistTitle,priority:20,click:function(){i?s.setState(i):s.close(),this.controller.modal.focusManager.focus()}},separateCancel:new wp.media.View({className:"separator",priority:40})})},embedContent:function(){var e=new wp.media.view.Embed({controller:this,model:this.state()}).render();this.content.set(e)},editSelectionContent:function(){var e=this.state(),t=e.get("selection"),e=new wp.media.view.AttachmentsBrowser({controller:this,collection:t,selection:t,model:e,sortable:!0,search:!1,date:!1,dragInfo:!0,AttachmentView:wp.media.view.Attachments.EditSelection}).render();e.toolbar.set("backToLibrary",{text:o.returnToLibrary,priority:-100,click:function(){this.controller.content.mode("browse"),this.controller.modal.focusManager.focus()}}),this.content.set(e),this.trigger("edit:selection",this)},editImageContent:function(){var e=this.state().get("image"),e=new wp.media.view.EditImage({model:e,controller:this}).render();this.content.set(e),e.loadEditor()},selectionStatusToolbar:function(e){var t=this.state().get("editable");e.set("selection",new wp.media.view.Selection({controller:this,collection:this.state().get("selection"),priority:-40,editable:t&&function(){this.controller.content.mode("edit-selection")}}).render())},mainInsertToolbar:function(e){var i=this;this.selectionStatusToolbar(e),e.set("insert",{style:"primary",priority:80,text:o.insertIntoPost,requires:{selection:!0},click:function(){var e=i.state(),t=e.get("selection");i.close(),e.trigger("insert",t).reset()}})},mainGalleryToolbar:function(e){var s=this;this.selectionStatusToolbar(e),e.set("gallery",{style:"primary",text:o.createNewGallery,priority:60,requires:{selection:!0},click:function(){var e=s.state().get("selection"),t=s.state("gallery-edit"),i=e.where({type:"image"});t.set("library",new wp.media.model.Selection(i,{props:e.props.toJSON(),multiple:!0})),this.controller.setState("gallery-edit"),this.controller.modal.focusManager.focus()}})},mainPlaylistToolbar:function(e){var s=this;this.selectionStatusToolbar(e),e.set("playlist",{style:"primary",text:o.createNewPlaylist,priority:100,requires:{selection:!0},click:function(){var e=s.state().get("selection"),t=s.state("playlist-edit"),i=e.where({type:"audio"});t.set("library",new wp.media.model.Selection(i,{props:e.props.toJSON(),multiple:!0})),this.controller.setState("playlist-edit"),this.controller.modal.focusManager.focus()}})},mainVideoPlaylistToolbar:function(e){var s=this;this.selectionStatusToolbar(e),e.set("video-playlist",{style:"primary",text:o.createNewVideoPlaylist,priority:100,requires:{selection:!0},click:function(){var e=s.state().get("selection"),t=s.state("video-playlist-edit"),i=e.where({type:"video"});t.set("library",new wp.media.model.Selection(i,{props:e.props.toJSON(),multiple:!0})),this.controller.setState("video-playlist-edit"),this.controller.modal.focusManager.focus()}})},featuredImageToolbar:function(e){this.createSelectToolbar(e,{text:o.setFeaturedImage,state:this.options.state})},mainEmbedToolbar:function(e){e.view=new wp.media.view.Toolbar.Embed({controller:this})},galleryEditToolbar:function(){var e=this.state().get("editing");this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{insert:{style:"primary",text:e?o.updateGallery:o.insertGallery,priority:80,requires:{library:!0},click:function(){var e=this.controller,t=e.state();e.close(),t.trigger("update",t.get("library")),e.setState(e.options.state),e.reset()}}}}))},galleryAddToolbar:function(){this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{insert:{style:"primary",text:o.addToGallery,priority:80,requires:{selection:!0},click:function(){var e=this.controller,t=e.state();e.state("gallery-edit").get("library").add(t.get("selection").models),t.trigger("reset"),e.setState("gallery-edit"),this.controller.modal.focusManager.focus()}}}}))},playlistEditToolbar:function(){var e=this.state().get("editing");this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{insert:{style:"primary",text:e?o.updatePlaylist:o.insertPlaylist,priority:80,requires:{library:!0},click:function(){var e=this.controller,t=e.state();e.close(),t.trigger("update",t.get("library")),e.setState(e.options.state),e.reset()}}}}))},playlistAddToolbar:function(){this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{insert:{style:"primary",text:o.addToPlaylist,priority:80,requires:{selection:!0},click:function(){var e=this.controller,t=e.state();e.state("playlist-edit").get("library").add(t.get("selection").models),t.trigger("reset"),e.setState("playlist-edit"),this.controller.modal.focusManager.focus()}}}}))},videoPlaylistEditToolbar:function(){var e=this.state().get("editing");this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{insert:{style:"primary",text:e?o.updateVideoPlaylist:o.insertVideoPlaylist,priority:140,requires:{library:!0},click:function(){var e=this.controller,t=e.state(),i=t.get("library");i.type="video",e.close(),t.trigger("update",i),e.setState(e.options.state),e.reset()}}}}))},videoPlaylistAddToolbar:function(){this.toolbar.set(new wp.media.view.Toolbar({controller:this,items:{insert:{style:"primary",text:o.addToVideoPlaylist,priority:140,requires:{selection:!0},click:function(){var e=this.controller,t=e.state();e.state("video-playlist-edit").get("library").add(t.get("selection").models),t.trigger("reset"),e.setState("video-playlist-edit"),this.controller.modal.focusManager.focus()}}}}))}});e.exports=a},"./src/js/media/views/frame/select.js":function(e,t){var i=wp.media.view.MediaFrame,s=wp.media.view.l10n,o=i.extend({initialize:function(){i.prototype.initialize.apply(this,arguments),_.defaults(this.options,{selection:[],library:{},multiple:!1,state:"library"}),this.createSelection(),this.createStates(),this.bindHandlers()},createSelection:function(){var e=this.options.selection;e instanceof wp.media.model.Selection||(this.options.selection=new wp.media.model.Selection(e,{multiple:this.options.multiple})),this._selection={attachments:new wp.media.model.Attachments,difference:[]}},editImageContent:function(){var e=this.state().get("image"),e=new wp.media.view.EditImage({model:e,controller:this}).render();this.content.set(e),e.loadEditor()},createStates:function(){var e=this.options;this.options.states||this.states.add([new wp.media.controller.Library({library:wp.media.query(e.library),multiple:e.multiple,title:e.title,priority:20}),new wp.media.controller.EditImage({model:e.editImage})])},bindHandlers:function(){this.on("router:create:browse",this.createRouter,this),this.on("router:render:browse",this.browseRouter,this),this.on("content:create:browse",this.browseContent,this),this.on("content:render:upload",this.uploadContent,this),this.on("toolbar:create:select",this.createSelectToolbar,this),this.on("content:render:edit-image",this.editImageContent,this)},browseRouter:function(e){e.set({upload:{text:s.uploadFilesTitle,priority:20},browse:{text:s.mediaLibraryTitle,priority:40}})},browseContent:function(e){var t=this.state();this.$el.removeClass("hide-toolbar"),e.view=new wp.media.view.AttachmentsBrowser({controller:this,collection:t.get("library"),selection:t.get("selection"),model:t,sortable:t.get("sortable"),search:t.get("searchable"),filters:t.get("filterable"),date:t.get("date"),display:t.has("display")?t.get("display"):t.get("displaySettings"),dragInfo:t.get("dragInfo"),idealColumnWidth:t.get("idealColumnWidth"),suggestedWidth:t.get("suggestedWidth"),suggestedHeight:t.get("suggestedHeight"),AttachmentView:t.get("AttachmentView")})},uploadContent:function(){this.$el.removeClass("hide-toolbar"),this.content.set(new wp.media.view.UploaderInline({controller:this}))},createSelectToolbar:function(e,t){(t=t||this.options.button||{}).controller=this,e.view=new wp.media.view.Toolbar.Select(t)}});e.exports=o},"./src/js/media/views/heading.js":function(e,t){var i=wp.media.View.extend({tagName:function(){return this.options.level||"h1"},className:"media-views-heading",initialize:function(){this.options.className&&this.$el.addClass(this.options.className),this.text=this.options.text},render:function(){return this.$el.html(this.text),this}});e.exports=i},"./src/js/media/views/iframe.js":function(e,t){var i=wp.media.View.extend({className:"media-iframe",render:function(){return this.views.detach(),this.$el.html('