mirror of
https://github.com/WordPress/WordPress.git
synced 2024-11-01 00:10:36 +01:00
248 lines
6.3 KiB
JavaScript
248 lines
6.3 KiB
JavaScript
|
/*globals _, wp, jQuery */
|
||
|
|
||
|
/**
|
||
|
* 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`.
|
||
|
*
|
||
|
* @constructor
|
||
|
* @augments wp.media.view.Frame
|
||
|
* @augments wp.media.View
|
||
|
* @augments wp.Backbone.View
|
||
|
* @augments Backbone.View
|
||
|
* @mixes wp.media.controller.StateMachine
|
||
|
*/
|
||
|
var Frame = require( '../frame.js' ),
|
||
|
MediaFrame = require( '../media-frame.js' ),
|
||
|
Modal = require( '../modal.js' ),
|
||
|
EditAttachmentMetadata = require( '../../controllers/edit-attachment-metadata.js' ),
|
||
|
TwoColumn = require( '../attachment/details-two-column.js' ),
|
||
|
AttachmentCompat = require( '../attachment-compat.js' ),
|
||
|
EditImageController = require( '../../controllers/edit-image.js' ),
|
||
|
DetailsView = require( '../edit-image-details.js' ),
|
||
|
$ = jQuery,
|
||
|
EditAttachments;
|
||
|
|
||
|
EditAttachments = MediaFrame.extend({
|
||
|
|
||
|
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 );
|
||
|
|
||
|
// Close the modal if the attachment is deleted.
|
||
|
this.listenTo( this.model, 'change:status destroy', this.close, 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( 'close', this.detach );
|
||
|
},
|
||
|
|
||
|
createModal: function() {
|
||
|
var self = this;
|
||
|
|
||
|
// Initialize modal container view.
|
||
|
if ( this.options.modal ) {
|
||
|
this.modal = new Modal({
|
||
|
controller: this,
|
||
|
title: this.options.title
|
||
|
});
|
||
|
|
||
|
this.modal.on( 'open', function () {
|
||
|
$( 'body' ).on( 'keydown.media-modal', _.bind( self.keyEvent, self ) );
|
||
|
} );
|
||
|
|
||
|
// Completely destroy the modal DOM element when closing it.
|
||
|
this.modal.on( 'close', function() {
|
||
|
self.modal.remove();
|
||
|
$( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */
|
||
|
// Restore the original focus item if possible
|
||
|
$( 'li.attachment[data-id="' + self.model.get( 'id' ) +'"]' ).focus();
|
||
|
self.resetRoute();
|
||
|
} );
|
||
|
|
||
|
// 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 EditAttachmentMetadata( { model: this.model } )
|
||
|
]);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 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 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 AttachmentCompat({
|
||
|
controller: this,
|
||
|
model: this.model
|
||
|
}) );
|
||
|
|
||
|
// Update browser url when navigating media details
|
||
|
if ( this.model ) {
|
||
|
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 EditImageController( {
|
||
|
model: this.model,
|
||
|
frame: this
|
||
|
} );
|
||
|
// Noop some methods.
|
||
|
editImageController._toolbar = function() {};
|
||
|
editImageController._router = function() {};
|
||
|
editImageController._menu = function() {};
|
||
|
|
||
|
contentRegion.view = new DetailsView( {
|
||
|
model: this.model,
|
||
|
frame: this,
|
||
|
controller: editImageController
|
||
|
} );
|
||
|
},
|
||
|
|
||
|
editImageModeRender: function( view ) {
|
||
|
view.on( 'ready', view.loadEditor );
|
||
|
},
|
||
|
|
||
|
toggleNav: function() {
|
||
|
this.$('.left').toggleClass( 'disabled', ! this.hasPrevious() );
|
||
|
this.$('.right').toggleClass( 'disabled', ! this.hasNext() );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Rerender the view.
|
||
|
*/
|
||
|
rerender: function() {
|
||
|
// 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() ) {
|
||
|
this.$( '.left' ).blur();
|
||
|
return;
|
||
|
}
|
||
|
this.model = this.library.at( this.getCurrentIndex() - 1 );
|
||
|
this.rerender();
|
||
|
this.$( '.left' ).focus();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Click handler to switch to the next media item.
|
||
|
*/
|
||
|
nextMediaItem: function() {
|
||
|
if ( ! this.hasNext() ) {
|
||
|
this.$( '.right' ).blur();
|
||
|
return;
|
||
|
}
|
||
|
this.model = this.library.at( this.getCurrentIndex() + 1 );
|
||
|
this.rerender();
|
||
|
this.$( '.right' ).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() {
|
||
|
this.gridRouter.navigate( this.gridRouter.baseUrl( '' ) );
|
||
|
}
|
||
|
});
|
||
|
|
||
|
module.exports = EditAttachments;
|