Introduce Edit Image (single mode) in the media modal and use it to edit images inserted in the editor. Adds new feature: replace an image in the editor. Props gcorne, see #24409.
Built from https://develop.svn.wordpress.org/trunk@27050 git-svn-id: http://core.svn.wordpress.org/trunk@26924 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
9172107539
commit
33eedc570b
|
@ -228,7 +228,6 @@ final class _WP_Editors {
|
||||||
'paste',
|
'paste',
|
||||||
'tabfocus',
|
'tabfocus',
|
||||||
'textcolor',
|
'textcolor',
|
||||||
'image',
|
|
||||||
'fullscreen',
|
'fullscreen',
|
||||||
'wordpress',
|
'wordpress',
|
||||||
'wpeditimage',
|
'wpeditimage',
|
||||||
|
|
|
@ -86,6 +86,10 @@
|
||||||
color: #a9a9a9;
|
color: #a9a9a9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-frame .hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Enable draggable on IE10 touch events until it's rolled into jQuery UI core */
|
/* Enable draggable on IE10 touch events until it's rolled into jQuery UI core */
|
||||||
.ui-sortable,
|
.ui-sortable,
|
||||||
.ui-draggable {
|
.ui-draggable {
|
||||||
|
@ -1411,7 +1415,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Embed from URL
|
* Embed from URL and Image Details
|
||||||
*/
|
*/
|
||||||
.embed-url {
|
.embed-url {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -1452,6 +1456,10 @@
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image-details .embed-image-settings {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.media-embed .thumbnail {
|
.media-embed .thumbnail {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
|
@ -1484,6 +1492,10 @@
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-embed .setting .hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.media-embed .setting span {
|
.media-embed .setting span {
|
||||||
display: block;
|
display: block;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
@ -1926,4 +1938,4 @@
|
||||||
.media-frame .spinner {
|
.media-frame .spinner {
|
||||||
background-image: url('../images/spinner-2x.gif');
|
background-image: url('../images/spinner-2x.gif');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -86,6 +86,10 @@
|
||||||
color: #a9a9a9;
|
color: #a9a9a9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-frame .hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Enable draggable on IE10 touch events until it's rolled into jQuery UI core */
|
/* Enable draggable on IE10 touch events until it's rolled into jQuery UI core */
|
||||||
.ui-sortable,
|
.ui-sortable,
|
||||||
.ui-draggable {
|
.ui-draggable {
|
||||||
|
@ -1411,7 +1415,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Embed from URL
|
* Embed from URL and Image Details
|
||||||
*/
|
*/
|
||||||
.embed-url {
|
.embed-url {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -1452,6 +1456,10 @@
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image-details .embed-image-settings {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.media-embed .thumbnail {
|
.media-embed .thumbnail {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
|
@ -1484,6 +1492,10 @@
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-embed .setting .hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.media-embed .setting span {
|
.media-embed .setting span {
|
||||||
display: block;
|
display: block;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
@ -1926,4 +1938,4 @@
|
||||||
.media-frame .spinner {
|
.media-frame .spinner {
|
||||||
background-image: url('../images/spinner-2x.gif');
|
background-image: url('../images/spinner-2x.gif');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,7 +2,7 @@
|
||||||
window.wp = window.wp || {};
|
window.wp = window.wp || {};
|
||||||
|
|
||||||
(function($){
|
(function($){
|
||||||
var Attachment, Attachments, Query, compare, l10n, media;
|
var Attachment, Attachments, Query, PostImage, compare, l10n, media;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wp.media( attributes )
|
* wp.media( attributes )
|
||||||
|
@ -30,6 +30,8 @@ window.wp = window.wp || {};
|
||||||
frame = new MediaFrame.Select( attributes );
|
frame = new MediaFrame.Select( attributes );
|
||||||
} else if ( 'post' === attributes.frame && MediaFrame.Post ) {
|
} else if ( 'post' === attributes.frame && MediaFrame.Post ) {
|
||||||
frame = new MediaFrame.Post( attributes );
|
frame = new MediaFrame.Post( attributes );
|
||||||
|
} else if ( 'image' === attributes.frame && MediaFrame.ImageDetails ) {
|
||||||
|
frame = new MediaFrame.ImageDetails( attributes );
|
||||||
}
|
}
|
||||||
|
|
||||||
delete attributes.frame;
|
delete attributes.frame;
|
||||||
|
@ -339,6 +341,121 @@ window.wp = window.wp || {};
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wp.media.model.Attachment
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @augments Backbone.Model
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
PostImage = media.model.PostImage = Backbone.Model.extend({
|
||||||
|
|
||||||
|
initialize: function( attributes ) {
|
||||||
|
this.attachment = false;
|
||||||
|
|
||||||
|
if ( attributes.attachment_id ) {
|
||||||
|
this.attachment = media.model.Attachment.get( attributes.attachment_id );
|
||||||
|
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();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
bindAttachmentListeners: function() {
|
||||||
|
this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl );
|
||||||
|
},
|
||||||
|
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
} 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSize: function() {
|
||||||
|
var size;
|
||||||
|
|
||||||
|
if ( ! this.attachment ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ];
|
||||||
|
this.set( 'url', size.url );
|
||||||
|
this.set( 'width', size.width );
|
||||||
|
this.set( 'height', size.height );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wp.media.model.Attachments
|
* wp.media.model.Attachments
|
||||||
*
|
*
|
||||||
|
@ -1170,4 +1287,4 @@ window.wp = window.wp || {};
|
||||||
window.wp = null;
|
window.wp = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
}(jQuery));
|
}(jQuery));
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -980,6 +980,107 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
media.controller.ImageDetails = media.controller.State.extend({
|
||||||
|
|
||||||
|
defaults: _.defaults({
|
||||||
|
id: 'image-details',
|
||||||
|
toolbar: 'image-details',
|
||||||
|
title: l10n.imageDetailsTitle,
|
||||||
|
content: 'image-details',
|
||||||
|
menu: 'image-details',
|
||||||
|
router: false,
|
||||||
|
attachment: false,
|
||||||
|
priority: 60,
|
||||||
|
editing: false
|
||||||
|
}, media.controller.Library.prototype.defaults ),
|
||||||
|
|
||||||
|
initialize: function( options ) {
|
||||||
|
this.image = options.image;
|
||||||
|
media.controller.State.prototype.initialize.apply( this, arguments );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wp.media.controller.ReplaceImage
|
||||||
|
*
|
||||||
|
* Replace a selected single image
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
media.controller.ReplaceImage = media.controller.Library.extend({
|
||||||
|
defaults: _.defaults({
|
||||||
|
id: 'replace-image',
|
||||||
|
filterable: 'uploaded',
|
||||||
|
multiple: false,
|
||||||
|
toolbar: 'replace',
|
||||||
|
title: l10n.replaceImageTitle,
|
||||||
|
priority: 60,
|
||||||
|
syncSelection: false
|
||||||
|
}, media.controller.Library.prototype.defaults ),
|
||||||
|
|
||||||
|
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', media.query({ type: 'image' }) );
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* call 'initialize' directly on the parent class
|
||||||
|
*/
|
||||||
|
media.controller.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') );
|
||||||
|
},
|
||||||
|
|
||||||
|
activate: function() {
|
||||||
|
this.updateSelection();
|
||||||
|
/**
|
||||||
|
* call 'activate' directly on the parent class
|
||||||
|
*/
|
||||||
|
media.controller.Library.prototype.activate.apply( this, arguments );
|
||||||
|
},
|
||||||
|
|
||||||
|
deactivate: function() {
|
||||||
|
/**
|
||||||
|
* call 'deactivate' directly on the parent class
|
||||||
|
*/
|
||||||
|
media.controller.Library.prototype.deactivate.apply( this, arguments );
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSelection: function() {
|
||||||
|
var selection = this.get('selection'),
|
||||||
|
attachment = this.image.attachment;
|
||||||
|
|
||||||
|
selection.reset( attachment ? [ attachment ] : [] );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wp.media.controller.Embed
|
* wp.media.controller.Embed
|
||||||
*
|
*
|
||||||
|
@ -1924,8 +2025,157 @@
|
||||||
}
|
}
|
||||||
}) );
|
}) );
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
media.view.MediaFrame.ImageDetails = media.view.MediaFrame.Select.extend({
|
||||||
|
defaults: {
|
||||||
|
id: 'image',
|
||||||
|
url: '',
|
||||||
|
menu: 'image-details',
|
||||||
|
content: 'image-details',
|
||||||
|
toolbar: 'image-details',
|
||||||
|
type: 'link',
|
||||||
|
title: l10n.imageDetailsTitle,
|
||||||
|
priority: 120
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function( options ) {
|
||||||
|
this.image = new media.model.PostImage( options.metadata );
|
||||||
|
this.options.selection = new media.model.Selection( this.image.attachment, { multiple: false } );
|
||||||
|
media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments );
|
||||||
|
},
|
||||||
|
|
||||||
|
bindHandlers: function() {
|
||||||
|
media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
|
||||||
|
this.on( 'menu:create:image-details', this.createMenu, this );
|
||||||
|
this.on( 'content:render:image-details', this.renderImageDetailsContent, this );
|
||||||
|
this.on( 'menu:render:image-details', this.renderMenu, this );
|
||||||
|
this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this );
|
||||||
|
// override the select toolbar
|
||||||
|
this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this );
|
||||||
|
},
|
||||||
|
|
||||||
|
createStates: function() {
|
||||||
|
this.states.add([
|
||||||
|
new media.controller.ImageDetails({
|
||||||
|
image: this.image,
|
||||||
|
editable: false,
|
||||||
|
menu: 'image-details'
|
||||||
|
}),
|
||||||
|
new media.controller.ReplaceImage({
|
||||||
|
id: 'replace-image',
|
||||||
|
library: media.query( { type: 'image' } ),
|
||||||
|
image: this.image,
|
||||||
|
multiple: false,
|
||||||
|
title: l10n.imageReplaceTitle,
|
||||||
|
menu: 'image-details',
|
||||||
|
toolbar: 'replace',
|
||||||
|
priority: 80,
|
||||||
|
displaySettings: true
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderImageDetailsContent: function() {
|
||||||
|
var view = new media.view.ImageDetails({
|
||||||
|
controller: this,
|
||||||
|
model: this.state().image,
|
||||||
|
attachment: this.state().image.attachment
|
||||||
|
}).render();
|
||||||
|
|
||||||
|
this.content.set( view );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMenu: function( view ) {
|
||||||
|
var lastState = this.lastState(),
|
||||||
|
previous = lastState && lastState.id,
|
||||||
|
frame = this;
|
||||||
|
|
||||||
|
view.set({
|
||||||
|
cancel: {
|
||||||
|
text: l10n.imageDetailsCancel,
|
||||||
|
priority: 20,
|
||||||
|
click: function() {
|
||||||
|
if ( previous ) {
|
||||||
|
frame.setState( previous );
|
||||||
|
} else {
|
||||||
|
frame.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
separateCancel: new media.View({
|
||||||
|
className: 'separator',
|
||||||
|
priority: 40
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
renderImageDetailsToolbar: function() {
|
||||||
|
this.toolbar.set( new 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 <img />
|
||||||
|
state.trigger( 'update', controller.image.toJSON() );
|
||||||
|
|
||||||
|
// Restore and reset the default state.
|
||||||
|
controller.setState( controller.options.state );
|
||||||
|
controller.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) );
|
||||||
|
},
|
||||||
|
|
||||||
|
renderReplaceImageToolbar: function() {
|
||||||
|
this.toolbar.set( new media.view.Toolbar({
|
||||||
|
controller: this,
|
||||||
|
items: {
|
||||||
|
replace: {
|
||||||
|
style: 'primary',
|
||||||
|
text: l10n.replace,
|
||||||
|
priority: 80,
|
||||||
|
|
||||||
|
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 <img />
|
||||||
|
state.trigger( 'replace', controller.image.toJSON() );
|
||||||
|
|
||||||
|
// Restore and reset the default state.
|
||||||
|
controller.setState( controller.options.state );
|
||||||
|
controller.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) );
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wp.media.view.Modal
|
* wp.media.view.Modal
|
||||||
*
|
*
|
||||||
|
@ -4555,7 +4805,7 @@
|
||||||
attachment = this.options.attachment;
|
attachment = this.options.attachment;
|
||||||
|
|
||||||
if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
|
if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
|
||||||
$input.hide();
|
$input.addClass( 'hidden' );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4571,7 +4821,7 @@
|
||||||
$input.prop( 'readonly', 'custom' !== linkTo );
|
$input.prop( 'readonly', 'custom' !== linkTo );
|
||||||
}
|
}
|
||||||
|
|
||||||
$input.show();
|
$input.removeClass( 'hidden' );
|
||||||
|
|
||||||
// If the input is visible, focus and select its contents.
|
// If the input is visible, focus and select its contents.
|
||||||
if ( $input.is(':visible') ) {
|
if ( $input.is(':visible') ) {
|
||||||
|
@ -4932,4 +5182,50 @@
|
||||||
this.$('img').attr( 'src', this.model.get('url') );
|
this.$('img').attr( 'src', this.model.get('url') );
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}(jQuery));
|
|
||||||
|
media.view.ImageDetails = media.view.Settings.AttachmentDisplay.extend({
|
||||||
|
className: 'image-details',
|
||||||
|
template: media.template('image-details'),
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
// used in AttachmentDisplay.prototype.updateLinkTo
|
||||||
|
this.options.attachment = this.model.attachment;
|
||||||
|
media.view.Settings.AttachmentDisplay.prototype.initialize.apply( this, arguments );
|
||||||
|
},
|
||||||
|
|
||||||
|
prepare: function() {
|
||||||
|
var attachment = false;
|
||||||
|
|
||||||
|
if ( this.model.attachment ) {
|
||||||
|
attachment = this.model.attachment.toJSON();
|
||||||
|
}
|
||||||
|
return _.defaults({
|
||||||
|
model: this.model.toJSON(),
|
||||||
|
attachment: attachment
|
||||||
|
}, this.options );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var self = this,
|
||||||
|
args = arguments;
|
||||||
|
if ( this.model.attachment && 'pending' === this.model.dfd.state() ) {
|
||||||
|
// should instead show a spinner when the attachment is new and then add a listener that updates on change
|
||||||
|
this.model.dfd.done( function() {
|
||||||
|
media.view.Settings.AttachmentDisplay.prototype.render.apply( self, args );
|
||||||
|
self.resetFocus();
|
||||||
|
} );
|
||||||
|
} else {
|
||||||
|
media.view.Settings.AttachmentDisplay.prototype.render.apply( this, arguments );
|
||||||
|
setTimeout( function() { self.resetFocus(); }, 10 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
resetFocus: function() {
|
||||||
|
this.$( '.caption textarea' ).focus();
|
||||||
|
this.$( '.embed-image-settings' ).scrollTop( 0 );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}(jQuery));
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -101,6 +101,176 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractImageData( imageNode ) {
|
||||||
|
var classes, metadata, captionBlock, caption;
|
||||||
|
|
||||||
|
// default attributes
|
||||||
|
metadata = {
|
||||||
|
attachment_id: false,
|
||||||
|
url: false,
|
||||||
|
height: '',
|
||||||
|
width: '',
|
||||||
|
size: 'none',
|
||||||
|
caption: '',
|
||||||
|
alt: '',
|
||||||
|
align: 'none',
|
||||||
|
link: false,
|
||||||
|
linkUrl: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
metadata.url = editor.dom.getAttrib( imageNode, 'src' );
|
||||||
|
metadata.alt = editor.dom.getAttrib( imageNode, 'alt' );
|
||||||
|
metadata.width = parseInt( editor.dom.getAttrib( imageNode, 'width' ), 10 );
|
||||||
|
metadata.height = parseInt( editor.dom.getAttrib( imageNode, 'height' ), 10 );
|
||||||
|
|
||||||
|
//TODO: probably should capture attributes on both the <img /> and the <a /> so that they can be restored when the image and/or caption are updated
|
||||||
|
// maybe use getAttribs()
|
||||||
|
|
||||||
|
// extract meta data from classes (candidate for turning into a method)
|
||||||
|
classes = imageNode.className.split( ' ' );
|
||||||
|
tinymce.each( classes, function( name ) {
|
||||||
|
|
||||||
|
if ( /^wp-image/.test( name ) ) {
|
||||||
|
metadata.attachment_id = parseInt( name.replace( 'wp-image-', '' ), 10 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( /^align/.test( name ) ) {
|
||||||
|
metadata.align = name.replace( 'align', '' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( /^size/.test( name ) ) {
|
||||||
|
metadata.size = name.replace( 'size-', '' );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
// extract caption
|
||||||
|
captionBlock = editor.dom.getParents( imageNode, '.wp-caption' );
|
||||||
|
|
||||||
|
if ( captionBlock.length ) {
|
||||||
|
captionBlock = captionBlock[0];
|
||||||
|
|
||||||
|
classes = captionBlock.className.split( ' ' );
|
||||||
|
tinymce.each( classes, function( name ) {
|
||||||
|
if ( /^align/.test( name ) ) {
|
||||||
|
metadata.align = name.replace( 'align', '' );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
caption = editor.dom.select( 'dd.wp-caption-dd', captionBlock );
|
||||||
|
if ( caption.length ) {
|
||||||
|
caption = caption[0];
|
||||||
|
// need to do some more thinking about this
|
||||||
|
metadata.caption = editor.serializer.serialize( caption )
|
||||||
|
.replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract linkTo
|
||||||
|
if ( imageNode.parentNode.nodeName === 'A' ) {
|
||||||
|
metadata.linkUrl = editor.dom.getAttrib( imageNode.parentNode, 'href' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateImage( imageNode, imageData ) {
|
||||||
|
var className, width, node, html, captionNode, nodeToReplace, uid;
|
||||||
|
|
||||||
|
if ( imageData.caption ) {
|
||||||
|
|
||||||
|
html = createImageAndLink( imageData, 'html' );
|
||||||
|
|
||||||
|
width = imageData.width + 10;
|
||||||
|
className = 'align' + imageData.align;
|
||||||
|
|
||||||
|
//TODO: shouldn't add the id attribute if it isn't an attachment
|
||||||
|
|
||||||
|
// should create a new function for genrating the caption markup
|
||||||
|
html = '<dl id="'+ imageData.attachment_id +'" class="wp-caption '+ className +'" style="width: '+ width +'px">' +
|
||||||
|
'<dt class="wp-caption-dt">'+ html + '</dt><dd class="wp-caption-dd">'+ imageData.caption +'</dd></dl>';
|
||||||
|
|
||||||
|
node = editor.dom.create( 'div', { 'class': 'mceTemp', draggable: 'true' }, html );
|
||||||
|
} else {
|
||||||
|
node = createImageAndLink( imageData, 'node' );
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeToReplace = imageNode;
|
||||||
|
|
||||||
|
captionNode = editor.dom.getParent( imageNode, '.mceTemp' );
|
||||||
|
|
||||||
|
if ( captionNode ) {
|
||||||
|
nodeToReplace = captionNode;
|
||||||
|
} else {
|
||||||
|
if ( imageNode.parentNode.nodeName === 'A' ) {
|
||||||
|
nodeToReplace = imageNode.parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// uniqueId isn't super exciting, so maybe we want to use something else
|
||||||
|
uid = editor.dom.uniqueId( 'wp_' );
|
||||||
|
editor.dom.setAttrib( node, 'data-wp-replace-id', uid );
|
||||||
|
editor.dom.replace( node, nodeToReplace );
|
||||||
|
|
||||||
|
// find the updated node
|
||||||
|
node = editor.dom.select( '[data-wp-replace-id="' + uid + '"]' )[0];
|
||||||
|
|
||||||
|
editor.dom.setAttrib( node, 'data-wp-replace-id', '' );
|
||||||
|
|
||||||
|
if ( node.nodeName === 'IMG' ) {
|
||||||
|
editor.selection.select( node );
|
||||||
|
} else {
|
||||||
|
editor.selection.select( editor.dom.select( 'img', node )[0] );
|
||||||
|
}
|
||||||
|
editor.nodeChanged();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function createImageAndLink( imageData, mode ) {
|
||||||
|
var classes = [],
|
||||||
|
props;
|
||||||
|
|
||||||
|
mode = mode ? mode : 'node';
|
||||||
|
|
||||||
|
|
||||||
|
if ( ! imageData.caption ) {
|
||||||
|
classes.push( 'align' + imageData.align );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( imageData.attachment_id ) {
|
||||||
|
classes.push( 'wp-image-' + imageData.attachment_id );
|
||||||
|
if ( imageData.size ) {
|
||||||
|
classes.push( 'size-' + imageData.size );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
props = {
|
||||||
|
src: imageData.url,
|
||||||
|
width: imageData.width,
|
||||||
|
height: imageData.height,
|
||||||
|
alt: imageData.alt
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( classes.length ) {
|
||||||
|
props['class'] = classes.join( ' ' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( imageData.linkUrl ) {
|
||||||
|
if ( mode === 'node' ) {
|
||||||
|
return editor.dom.create( 'a', { href: imageData.linkUrl }, editor.dom.createHTML( 'img', props ) );
|
||||||
|
} else if ( mode === 'html' ) {
|
||||||
|
return editor.dom.createHTML( 'a', { href: imageData.linkUrl }, editor.dom.createHTML( 'img', props ) );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( mode === 'node' ) {
|
||||||
|
return editor.dom.create( 'img', props );
|
||||||
|
} else if ( mode === 'html' ) {
|
||||||
|
return editor.dom.createHTML( 'img', props );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
editor.on( 'init', function() {
|
editor.on( 'init', function() {
|
||||||
var dom = editor.dom;
|
var dom = editor.dom;
|
||||||
|
|
||||||
|
@ -452,6 +622,40 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
editor.on( 'mousedown', function( e ) {
|
||||||
|
var imageNode, frame, callback;
|
||||||
|
if ( e.target.nodeName === 'IMG' && editor.selection.getNode() === e.target ) {
|
||||||
|
// Don't trigger on right-click
|
||||||
|
if ( e.button !== 2 ) {
|
||||||
|
|
||||||
|
// Don't attempt to edit placeholders
|
||||||
|
if ( editor.dom.hasClass( e.target, 'mceItem' ) || '1' === editor.dom.getAttrib( e.target, 'data-mce-placeholder' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
imageNode = e.target;
|
||||||
|
|
||||||
|
frame = wp.media({
|
||||||
|
frame: 'image',
|
||||||
|
state: 'image-details',
|
||||||
|
metadata: extractImageData( imageNode )
|
||||||
|
} );
|
||||||
|
|
||||||
|
callback = function( imageData ) {
|
||||||
|
updateImage( imageNode, imageData );
|
||||||
|
editor.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
frame.state('image-details').on( 'update', callback );
|
||||||
|
frame.state('replace-image').on( 'replace', callback );
|
||||||
|
|
||||||
|
frame.open();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
editor.wpSetImgCaption = function( content ) {
|
editor.wpSetImgCaption = function( content ) {
|
||||||
return parseShortcode( content );
|
return parseShortcode( content );
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -500,6 +500,117 @@ function wp_print_media_templates() {
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" id="tmpl-image-details">
|
||||||
|
<?php // reusing .media-embed to pick up the styles for now ?>
|
||||||
|
<div class="media-embed">
|
||||||
|
<div class="embed-image-settings">
|
||||||
|
<div class="thumbnail">
|
||||||
|
<img src="{{ data.model.url }}" draggable="false" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting url">
|
||||||
|
<?php // might want to make the url editable if it isn't an attachment ?>
|
||||||
|
<input type="text" disabled="disabled" value="{{ data.model.url }}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
/** This filter is documented in wp-admin/includes/media.php */
|
||||||
|
if ( ! apply_filters( 'disable_captions', '' ) ) : ?>
|
||||||
|
<label class="setting caption">
|
||||||
|
<span><?php _e('Caption'); ?></span>
|
||||||
|
<textarea data-setting="caption">{{ data.model.caption }}</textarea>
|
||||||
|
</label>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<label class="setting alt-text">
|
||||||
|
<span><?php _e('Alt Text'); ?></span>
|
||||||
|
<input type="text" data-setting="alt" value="{{ data.model.alt }}" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="setting align">
|
||||||
|
<span><?php _e('Align'); ?></span>
|
||||||
|
<div class="button-group button-large" data-setting="align">
|
||||||
|
<button class="button" value="left">
|
||||||
|
<?php esc_attr_e('Left'); ?>
|
||||||
|
</button>
|
||||||
|
<button class="button" value="center">
|
||||||
|
<?php esc_attr_e('Center'); ?>
|
||||||
|
</button>
|
||||||
|
<button class="button" value="right">
|
||||||
|
<?php esc_attr_e('Right'); ?>
|
||||||
|
</button>
|
||||||
|
<button class="button active" value="none">
|
||||||
|
<?php esc_attr_e('None'); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting link-to">
|
||||||
|
<span><?php _e('Link To'); ?></span>
|
||||||
|
|
||||||
|
<# if ( data.attachment ) { #>
|
||||||
|
<div class="button-group button-large" data-setting="link">
|
||||||
|
<button class="button" value="file">
|
||||||
|
<?php esc_attr_e('Media File'); ?>
|
||||||
|
</button>
|
||||||
|
<button class="button" value="post">
|
||||||
|
<?php esc_attr_e('Attachment Page'); ?>
|
||||||
|
</button>
|
||||||
|
<button class="button" value="custom">
|
||||||
|
<?php esc_attr_e('Custom URL'); ?>
|
||||||
|
</button>
|
||||||
|
<button class="button active" value="none">
|
||||||
|
<?php esc_attr_e('None'); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="link-to-custom" data-setting="linkUrl" />
|
||||||
|
|
||||||
|
<# } else { #>
|
||||||
|
<div class="button-group button-large" data-setting="link">
|
||||||
|
<button class="button" value="file">
|
||||||
|
<?php esc_attr_e('Image URL'); ?>
|
||||||
|
</button>
|
||||||
|
<button class="button" value="custom">
|
||||||
|
<?php esc_attr_e('Custom URL'); ?>
|
||||||
|
</button>
|
||||||
|
<button class="button active" value="none">
|
||||||
|
<?php esc_attr_e('None'); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="link-to-custom" data-setting="linkUrl" />
|
||||||
|
|
||||||
|
<# } #>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<# if ( data.attachment ) { #>
|
||||||
|
<div class="setting size">
|
||||||
|
<span><?php _e('Size'); ?></span>
|
||||||
|
<div class="button-group button-large" data-setting="size">
|
||||||
|
<?php
|
||||||
|
/** This filter is documented in wp-admin/includes/media.php */
|
||||||
|
$sizes = apply_filters( 'image_size_names_choose', array(
|
||||||
|
'thumbnail' => __('Thumbnail'),
|
||||||
|
'medium' => __('Medium'),
|
||||||
|
'large' => __('Large'),
|
||||||
|
'full' => __('Full Size'),
|
||||||
|
) );
|
||||||
|
|
||||||
|
foreach ( $sizes as $value => $name ) : ?>
|
||||||
|
<#
|
||||||
|
var size = data.attachment.sizes['<?php echo esc_js( $value ); ?>'];
|
||||||
|
if ( size ) { #>
|
||||||
|
<button class="button" value="<?php echo esc_attr( $value ); ?>">
|
||||||
|
<?php echo esc_html( $name ); ?>
|
||||||
|
</button>
|
||||||
|
<# } #>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<# } #>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1967,6 +1967,8 @@ function wp_enqueue_media( $args = array() ) {
|
||||||
'search' => __( 'Search' ),
|
'search' => __( 'Search' ),
|
||||||
'select' => __( 'Select' ),
|
'select' => __( 'Select' ),
|
||||||
'cancel' => __( 'Cancel' ),
|
'cancel' => __( 'Cancel' ),
|
||||||
|
'update' => __( 'Update' ),
|
||||||
|
'replace' => __( 'Replace' ),
|
||||||
/* translators: This is a would-be plural string used in the media manager.
|
/* translators: This is a would-be plural string used in the media manager.
|
||||||
If there is not a word you can use in your language to avoid issues with the
|
If there is not a word you can use in your language to avoid issues with the
|
||||||
lack of plural support here, turn it into "selected: %d" then translate it.
|
lack of plural support here, turn it into "selected: %d" then translate it.
|
||||||
|
@ -2005,6 +2007,12 @@ function wp_enqueue_media( $args = array() ) {
|
||||||
'addToGallery' => __( 'Add to gallery' ),
|
'addToGallery' => __( 'Add to gallery' ),
|
||||||
'addToGalleryTitle' => __( 'Add to Gallery' ),
|
'addToGalleryTitle' => __( 'Add to Gallery' ),
|
||||||
'reverseOrder' => __( 'Reverse order' ),
|
'reverseOrder' => __( 'Reverse order' ),
|
||||||
|
|
||||||
|
|
||||||
|
// Edit Image
|
||||||
|
'imageDetailsTitle' => __( 'Image Details' ),
|
||||||
|
'imageReplaceTitle' => __( 'Replace Image' ),
|
||||||
|
'imageDetailsCancel' => __( 'Cancel Edit' )
|
||||||
);
|
);
|
||||||
|
|
||||||
$settings = apply_filters( 'media_view_settings', $settings, $post );
|
$settings = apply_filters( 'media_view_settings', $settings, $post );
|
||||||
|
|
Loading…
Reference in New Issue