2 tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
7 function parseShortcode( content ) {
8 return content.replace( /(?:<p>)?\[(?:wp_)?caption([^\]]+)\]([\s\S]+?)\[\/(?:wp_)?caption\](?:<\/p>)?/g, function( a, b, c ) {
9 var id, align, classes, caption, img, width,
12 id = b.match( /id=['"]([^'"]*)['"] ?/ );
14 b = b.replace( id[0], '' );
17 align = b.match( /align=['"]([^'"]*)['"] ?/ );
19 b = b.replace( align[0], '' );
22 classes = b.match( /class=['"]([^'"]*)['"] ?/ );
24 b = b.replace( classes[0], '' );
27 width = b.match( /width=['"]([0-9]*)['"] ?/ );
29 b = b.replace( width[0], '' );
33 img = c.match( /((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)([\s\S]*)/i );
35 if ( img && img[2] ) {
36 caption = trim( img[2] );
39 // old captions shortcode style
40 caption = trim( b ).replace( /caption=['"]/, '' ).replace( /['"]$/, '' );
44 id = ( id && id[1] ) ? id[1].replace( /[<>&]+/g, '' ) : '';
45 align = ( align && align[1] ) ? align[1] : 'alignnone';
46 classes = ( classes && classes[1] ) ? ' ' + classes[1].replace( /[<>&]+/g, '' ) : '';
48 if ( ! width && img ) {
49 width = img.match( /width=['"]([0-9]*)['"]/ );
52 if ( width && width[1] ) {
56 if ( ! width || ! caption ) {
60 width = parseInt( width, 10 );
61 if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) {
65 return '<div class="mceTemp"><dl id="' + id + '" class="wp-caption ' + align + classes + '" style="width: ' + width + 'px">' +
66 '<dt class="wp-caption-dt">'+ img +'</dt><dd class="wp-caption-dd">'+ caption +'</dd></dl></div>';
70 function getShortcode( content ) {
71 return content.replace( /<div (?:id="attachment_|class="mceTemp)[^>]*>([\s\S]+?)<\/div>/g, function( a, b ) {
74 if ( b.indexOf('<img ') === -1 ) {
75 // Broken caption. The user managed to drag the image out?
76 // Try to return the caption text as a paragraph.
77 out = b.match( /<dd [^>]+>([\s\S]+?)<\/dd>/i );
79 if ( out && out[1] ) {
80 return '<p>' + out[1] + '</p>';
86 out = b.replace( /\s*<dl ([^>]+)>\s*<dt [^>]+>([\s\S]+?)<\/dt>\s*<dd [^>]+>([\s\S]*?)<\/dd>\s*<\/dl>\s*/gi, function( a, b, c, caption ) {
87 var id, classes, align, width;
89 width = c.match( /width="([0-9]*)"/ );
90 width = ( width && width[1] ) ? width[1] : '';
92 if ( ! width || ! caption ) {
96 id = b.match( /id="([^"]*)"/ );
97 id = ( id && id[1] ) ? id[1] : '';
99 classes = b.match( /class="([^"]*)"/ );
100 classes = ( classes && classes[1] ) ? classes[1] : '';
102 align = classes.match( /align[a-z]+/i ) || 'alignnone';
103 classes = classes.replace( /wp-caption ?|align[a-z]+ ?/gi, '' );
106 classes = ' class="' + classes + '"';
109 caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) {
110 // no line breaks inside HTML tags
111 return a.replace( /[\r\n\t]+/, ' ' );
114 // convert remaining line breaks to <br>
115 caption = caption.replace( /\s*\n\s*/g, '<br />' );
117 return '[caption id="' + id + '" align="' + align + '" width="' + width + '"' + classes + ']' + c + ' ' + caption + '[/caption]';
120 if ( out.indexOf('[caption') === -1 ) {
121 // the caption html seems broken, try to find the image that may be wrapped in a link
122 // and may be followed by <p> with the caption text.
123 out = b.replace( /[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2' );
130 function extractImageData( imageNode ) {
131 var classes, extraClasses, metadata, captionBlock, caption, link, width, height,
132 captionClassName = [],
134 isIntRegExp = /^\d+$/;
136 // default attributes
138 attachment_id: false,
146 linkTargetBlank: false,
151 metadata.url = dom.getAttrib( imageNode, 'src' );
152 metadata.alt = dom.getAttrib( imageNode, 'alt' );
153 metadata.title = dom.getAttrib( imageNode, 'title' );
155 width = dom.getAttrib( imageNode, 'width' );
156 height = dom.getAttrib( imageNode, 'height' );
158 if ( ! isIntRegExp.test( width ) || parseInt( width, 10 ) < 1 ) {
159 width = imageNode.naturalWidth || imageNode.width;
162 if ( ! isIntRegExp.test( height ) || parseInt( height, 10 ) < 1 ) {
163 height = imageNode.naturalHeight || imageNode.height;
166 metadata.customWidth = metadata.width = width;
167 metadata.customHeight = metadata.height = height;
169 classes = tinymce.explode( imageNode.className, ' ' );
172 tinymce.each( classes, function( name ) {
174 if ( /^wp-image/.test( name ) ) {
175 metadata.attachment_id = parseInt( name.replace( 'wp-image-', '' ), 10 );
176 } else if ( /^align/.test( name ) ) {
177 metadata.align = name.replace( 'align', '' );
178 } else if ( /^size/.test( name ) ) {
179 metadata.size = name.replace( 'size-', '' );
181 extraClasses.push( name );
186 metadata.extraClasses = extraClasses.join( ' ' );
189 captionBlock = dom.getParents( imageNode, '.wp-caption' );
191 if ( captionBlock.length ) {
192 captionBlock = captionBlock[0];
194 classes = captionBlock.className.split( ' ' );
195 tinymce.each( classes, function( name ) {
196 if ( /^align/.test( name ) ) {
197 metadata.align = name.replace( 'align', '' );
198 } else if ( name && name !== 'wp-caption' ) {
199 captionClassName.push( name );
203 metadata.captionClassName = captionClassName.join( ' ' );
205 caption = dom.select( 'dd.wp-caption-dd', captionBlock );
206 if ( caption.length ) {
207 caption = caption[0];
209 metadata.caption = editor.serializer.serialize( caption )
210 .replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' );
215 if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' ) {
216 link = imageNode.parentNode;
217 metadata.linkUrl = dom.getAttrib( link, 'href' );
218 metadata.linkTargetBlank = dom.getAttrib( link, 'target' ) === '_blank' ? true : false;
219 metadata.linkRel = dom.getAttrib( link, 'rel' );
220 metadata.linkClassName = link.className;
226 function hasTextContent( node ) {
227 return node && !! ( node.textContent || node.innerText );
230 // Verify HTML in captions
231 function verifyHTML( caption ) {
232 if ( ! caption || ( caption.indexOf( '<' ) === -1 && caption.indexOf( '>' ) === -1 ) ) {
236 if ( ! serializer ) {
237 serializer = new tinymce.html.Serializer( {}, editor.schema );
240 return serializer.serialize( editor.parser.parse( caption, { forced_root_block: false } ) );
243 function updateImage( imageNode, imageData ) {
244 var classes, className, node, html, parent, wrap, linkNode,
245 captionNode, dd, dl, id, attrs, linkAttrs, width, height, align,
248 classes = tinymce.explode( imageData.extraClasses, ' ' );
254 if ( ! imageData.caption ) {
255 classes.push( 'align' + imageData.align );
258 if ( imageData.attachment_id ) {
259 classes.push( 'wp-image-' + imageData.attachment_id );
260 if ( imageData.size && imageData.size !== 'custom' ) {
261 classes.push( 'size-' + imageData.size );
265 width = imageData.width;
266 height = imageData.height;
268 if ( imageData.size === 'custom' ) {
269 width = imageData.customWidth;
270 height = imageData.customHeight;
275 width: width || null,
276 height: height || null,
278 title: imageData.title || null,
279 'class': classes.join( ' ' ) || null
282 dom.setAttribs( imageNode, attrs );
285 href: imageData.linkUrl,
286 rel: imageData.linkRel || null,
287 target: imageData.linkTargetBlank ? '_blank': null,
288 'class': imageData.linkClassName || null
291 if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) {
292 // Update or remove an existing link wrapped around the image
293 if ( imageData.linkUrl ) {
294 dom.setAttribs( imageNode.parentNode, linkAttrs );
296 dom.remove( imageNode.parentNode, true );
298 } else if ( imageData.linkUrl ) {
299 if ( linkNode = dom.getParent( imageNode, 'a' ) ) {
300 // The image is inside a link together with other nodes,
301 // or is nested in another node, move it out
302 dom.insertAfter( imageNode, linkNode );
305 // Add link wrapped around the image
306 linkNode = dom.create( 'a', linkAttrs );
307 imageNode.parentNode.insertBefore( linkNode, imageNode );
308 linkNode.appendChild( imageNode );
311 captionNode = editor.dom.getParent( imageNode, '.mceTemp' );
313 if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) {
314 node = imageNode.parentNode;
319 if ( imageData.caption ) {
320 imageData.caption = verifyHTML( imageData.caption );
322 id = imageData.attachment_id ? 'attachment_' + imageData.attachment_id : null;
323 align = 'align' + ( imageData.align || 'none' );
324 className = 'wp-caption ' + align;
326 if ( imageData.captionClassName ) {
327 className += ' ' + imageData.captionClassName.replace( /[<>&]+/g, '' );
330 if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) {
331 width = parseInt( width, 10 );
336 dl = dom.select( 'dl.wp-caption', captionNode );
339 dom.setAttribs( dl, {
342 style: 'width: ' + width + 'px'
346 dd = dom.select( '.wp-caption-dd', captionNode );
349 dom.setHTML( dd[0], imageData.caption );
353 id = id ? 'id="'+ id +'" ' : '';
355 // should create a new function for generating the caption markup
356 html = '<dl ' + id + 'class="' + className +'" style="width: '+ width +'px">' +
357 '<dt class="wp-caption-dt">' + dom.getOuterHTML( node ) + '</dt><dd class="wp-caption-dd">'+ imageData.caption +'</dd></dl>';
359 if ( parent = dom.getParent( node, 'p' ) ) {
360 wrap = dom.create( 'div', { 'class': 'mceTemp' }, html );
361 parent.parentNode.insertBefore( wrap, parent );
364 if ( dom.isEmpty( parent ) ) {
365 dom.remove( parent );
368 dom.setOuterHTML( node, '<div class="mceTemp">' + html + '</div>' );
371 } else if ( captionNode ) {
372 // Remove the caption wrapper and place the image in new paragraph
373 parent = dom.create( 'p' );
374 captionNode.parentNode.insertBefore( parent, captionNode );
375 parent.appendChild( node );
376 dom.remove( captionNode );
379 if ( wp.media.events ) {
380 wp.media.events.trigger( 'editor:image-update', {
387 editor.nodeChanged();
388 // Refresh the toolbar
389 addToolbar( imageNode );
392 function editImage( img ) {
393 var frame, callback, metadata;
395 if ( typeof wp === 'undefined' || ! wp.media ) {
396 editor.execCommand( 'mceImage' );
400 metadata = extractImageData( img );
402 // Manipulate the metadata by reference that is fed into the PostImage model used in the media modal
403 wp.media.events.trigger( 'editor:image-edit', {
411 state: 'image-details',
415 wp.media.events.trigger( 'editor:frame-create', { frame: frame } );
417 callback = function( imageData ) {
419 editor.undoManager.transact( function() {
420 updateImage( img, imageData );
425 frame.state('image-details').on( 'update', callback );
426 frame.state('replace-image').on( 'replace', callback );
427 frame.on( 'close', function() {
430 editingImage = false;
436 function removeImage( node ) {
439 if ( node.nodeName === 'DIV' && editor.dom.hasClass( node, 'mceTemp' ) ) {
441 } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) {
442 wrap = editor.dom.getParent( node, 'div.mceTemp' );
446 if ( wrap.nextSibling ) {
447 editor.selection.select( wrap.nextSibling );
448 } else if ( wrap.previousSibling ) {
449 editor.selection.select( wrap.previousSibling );
451 editor.selection.select( wrap.parentNode );
454 editor.selection.collapse( true );
455 editor.dom.remove( wrap );
457 editor.dom.remove( node );
461 editor.nodeChanged();
462 editor.undoManager.add();
465 function addToolbar( node ) {
466 var rectangle, toolbarHtml, toolbar, left,
471 // Don't add to placeholders
472 if ( ! node || node.nodeName !== 'IMG' || isPlaceholder( node ) ) {
476 dom.setAttrib( node, 'data-wp-imgselect', 1 );
477 rectangle = dom.getRect( node );
479 toolbarHtml = '<i class="dashicons dashicons-edit edit" data-mce-bogus="all"></i>' +
480 '<i class="dashicons dashicons-no-alt remove" data-mce-bogus="all"></i>';
482 toolbar = dom.create( 'p', {
483 'id': 'wp-image-toolbar',
484 'data-mce-bogus': 'all',
485 'contenteditable': false
489 left = rectangle.x + rectangle.w - 82;
494 editor.getBody().appendChild( toolbar );
495 dom.setStyles( toolbar, {
500 toolbarActive = true;
503 function removeToolbar() {
504 var toolbar = editor.dom.get( 'wp-image-toolbar' );
507 editor.dom.remove( toolbar );
510 editor.dom.setAttrib( editor.dom.select( 'img[data-wp-imgselect]' ), 'data-wp-imgselect', null );
512 editingImage = false;
513 toolbarActive = false;
516 function isPlaceholder( node ) {
517 var dom = editor.dom;
519 if ( dom.hasClass( node, 'mceItem' ) || dom.getAttrib( node, 'data-mce-placeholder' ) ||
520 dom.getAttrib( node, 'data-mce-object' ) ) {
528 function isToolbarButton( node ) {
529 return ( node && node.nodeName === 'I' && node.parentNode.id === 'wp-image-toolbar' );
532 function edit( event ) {
537 // Don't trigger on right-click
538 if ( event.button && event.button > 1 ) {
542 if ( isToolbarButton( node ) ) {
543 image = dom.select( 'img[data-wp-imgselect]' )[0];
546 editor.selection.select( image );
548 if ( dom.hasClass( node, 'remove' ) ) {
549 removeImage( image );
550 } else if ( dom.hasClass( node, 'edit' ) ) {
551 if ( ! editingImage ) {
558 event.preventDefault();
559 } else if ( node.nodeName === 'IMG' && ! editor.dom.getAttrib( node, 'data-wp-imgselect' ) && ! isPlaceholder( node ) ) {
561 } else if ( node.nodeName !== 'IMG' ) {
566 if ( 'ontouchend' in document ) {
567 editor.on( 'click', function( event ) {
568 var target = event.target;
570 if ( editingImage && target.nodeName === 'IMG' ) {
571 event.preventDefault();
574 if ( isToolbarButton( target ) ) {
575 event.preventDefault();
576 event.stopPropagation();
581 editor.on( 'mouseup touchend', edit );
583 editor.on( 'init', function() {
584 var dom = editor.dom,
585 captionClass = editor.getParam( 'wpeditimage_html5_captions' ) ? 'html5-captions' : 'html4-captions';
587 dom.addClass( editor.getBody(), captionClass );
589 // Add caption field to the default image dialog
590 editor.on( 'wpLoadImageForm', function( event ) {
591 if ( editor.getParam( 'wpeditimage_disable_captions' ) ) {
602 label: 'Image caption'
605 event.data.splice( event.data.length - 1, 0, captionField );
608 // Fix caption parent width for images added from URL
609 editor.on( 'wpNewImageRefresh', function( event ) {
610 var parent, captionWidth;
612 if ( parent = dom.getParent( event.node, 'dl.wp-caption' ) ) {
613 if ( ! parent.style.width ) {
614 captionWidth = parseInt( event.node.clientWidth, 10 ) + 10;
615 captionWidth = captionWidth ? captionWidth + 'px' : '50%';
616 dom.setStyle( parent, 'width', captionWidth );
621 editor.on( 'wpImageFormSubmit', function( event ) {
622 var data = event.imgData.data,
623 imgNode = event.imgData.node,
624 caption = event.imgData.caption,
628 wrap, parent, node, html, imgId;
630 // Temp image id so we can find the node later
631 data.id = '__wp-temp-img-id';
632 // Cancel the original callback
633 event.imgData.cancel = true;
635 if ( ! data.style ) {
640 // Delete the image and the caption
642 if ( wrap = dom.getParent( imgNode, 'div.mceTemp' ) ) {
644 } else if ( imgNode.parentNode.nodeName === 'A' ) {
645 dom.remove( imgNode.parentNode );
647 dom.remove( imgNode );
650 editor.nodeChanged();
656 caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<\/?[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) {
657 // No line breaks inside HTML tags
658 return a.replace( /[\r\n\t]+/, ' ' );
661 // Convert remaining line breaks to <br>
662 caption = caption.replace( /(<br[^>]*>)\s*\n\s*/g, '$1' ).replace( /\s*\n\s*/g, '<br />' );
663 caption = verifyHTML( caption );
667 // New image inserted
668 html = dom.createHTML( 'img', data );
671 node = editor.selection.getNode();
674 captionWidth = parseInt( data.width, 10 );
676 if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) {
680 captionWidth = ' style="width: ' + captionWidth + 'px"';
683 html = '<dl class="wp-caption alignnone"' + captionWidth + '>' +
684 '<dt class="wp-caption-dt">'+ html +'</dt><dd class="wp-caption-dd">'+ caption +'</dd></dl>';
686 if ( node.nodeName === 'P' ) {
689 parent = dom.getParent( node, 'p' );
692 if ( parent && parent.nodeName === 'P' ) {
693 wrap = dom.create( 'div', { 'class': 'mceTemp' }, html );
694 parent.parentNode.insertBefore( wrap, parent );
695 editor.selection.select( wrap );
696 editor.nodeChanged();
698 if ( dom.isEmpty( parent ) ) {
699 dom.remove( parent );
702 editor.selection.setContent( '<div class="mceTemp">' + html + '</div>' );
705 editor.selection.setContent( html );
708 // Edit existing image
710 // Store the original image id if any
711 imgId = imgNode.id || null;
712 // Update the image node
713 dom.setAttribs( imgNode, data );
714 wrap = dom.getParent( imgNode, 'dl.wp-caption' );
718 if ( parent = dom.select( 'dd.wp-caption-dd', wrap )[0] ) {
719 parent.innerHTML = caption;
722 if ( imgNode.className ) {
723 captionId = imgNode.className.match( /wp-image-([0-9]+)/ );
724 captionAlign = imgNode.className.match( /align(left|right|center|none)/ );
727 if ( captionAlign ) {
728 captionAlign = captionAlign[0];
729 imgNode.className = imgNode.className.replace( /align(left|right|center|none)/g, '' );
731 captionAlign = 'alignnone';
734 captionAlign = ' class="wp-caption ' + captionAlign + '"';
737 captionId = ' id="attachment_' + captionId[1] + '"';
740 captionWidth = data.width || imgNode.clientWidth;
742 if ( captionWidth ) {
743 captionWidth = parseInt( captionWidth, 10 );
745 if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) {
749 captionWidth = ' style="width: '+ captionWidth +'px"';
752 if ( imgNode.parentNode && imgNode.parentNode.nodeName === 'A' ) {
753 html = dom.getOuterHTML( imgNode.parentNode );
754 node = imgNode.parentNode;
756 html = dom.getOuterHTML( imgNode );
760 html = '<dl ' + captionId + captionAlign + captionWidth + '>' +
761 '<dt class="wp-caption-dt">'+ html +'</dt><dd class="wp-caption-dd">'+ caption +'</dd></dl>';
763 if ( parent = dom.getParent( imgNode, 'p' ) ) {
764 wrap = dom.create( 'div', { 'class': 'mceTemp' }, html );
765 dom.insertAfter( wrap, parent );
766 editor.selection.select( wrap );
767 editor.nodeChanged();
769 // Delete the old image node
772 if ( dom.isEmpty( parent ) ) {
773 dom.remove( parent );
776 editor.selection.setContent( '<div class="mceTemp">' + html + '</div>' );
781 // Remove the caption wrapper and place the image in new paragraph
782 if ( imgNode.parentNode.nodeName === 'A' ) {
783 html = dom.getOuterHTML( imgNode.parentNode );
785 html = dom.getOuterHTML( imgNode );
788 parent = dom.create( 'p', {}, html );
789 dom.insertAfter( parent, wrap.parentNode );
790 editor.selection.select( parent );
791 editor.nodeChanged();
792 dom.remove( wrap.parentNode );
797 imgNode = dom.get('__wp-temp-img-id');
798 dom.setAttrib( imgNode, 'id', imgId );
799 event.imgData.node = imgNode;
802 editor.on( 'wpLoadImageData', function( event ) {
804 data = event.imgData.data,
805 imgNode = event.imgData.node;
807 if ( parent = dom.getParent( imgNode, 'dl.wp-caption' ) ) {
808 parent = dom.select( 'dd.wp-caption-dd', parent )[0];
811 data.caption = editor.serializer.serialize( parent )
812 .replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' );
817 dom.bind( editor.getDoc(), 'dragstart', function( event ) {
818 var node = editor.selection.getNode();
820 // Prevent dragging images out of the caption elements
821 if ( node.nodeName === 'IMG' && dom.getParent( node, '.wp-caption' ) ) {
822 event.preventDefault();
825 // Remove toolbar to avoid an orphaned toolbar when dragging an image to a new location
829 // Prevent IE11 from making dl.wp-caption resizable
830 if ( tinymce.Env.ie && tinymce.Env.ie > 10 ) {
831 // The 'mscontrolselect' event is supported only in IE11+
832 dom.bind( editor.getBody(), 'mscontrolselect', function( event ) {
833 if ( event.target.nodeName === 'IMG' && dom.getParent( event.target, '.wp-caption' ) ) {
834 // Hide the thick border with resize handles around dl.wp-caption
835 editor.getBody().focus(); // :(
836 } else if ( event.target.nodeName === 'DL' && dom.hasClass( event.target, 'wp-caption' ) ) {
837 // Trigger the thick border with resize handles...
838 // This will make the caption text editable.
839 event.target.focus();
843 editor.on( 'click', function( event ) {
844 if ( event.target.nodeName === 'IMG' && dom.getAttrib( event.target, 'data-wp-imgselect' ) &&
845 dom.getParent( event.target, 'dl.wp-caption' ) ) {
847 editor.getBody().focus();
853 editor.on( 'ObjectResized', function( event ) {
854 var node = event.target;
856 if ( node.nodeName === 'IMG' ) {
857 editor.undoManager.transact( function() {
861 node.className = node.className.replace( /\bsize-[^ ]+/, '' );
863 if ( parent = dom.getParent( node, '.wp-caption' ) ) {
864 width = event.width || dom.getAttrib( node, 'width' );
867 width = parseInt( width, 10 );
869 if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) {
873 dom.setStyle( parent, 'width', width + 'px' );
882 editor.on( 'BeforeExecCommand', function( event ) {
883 var node, p, DL, align,
887 if ( cmd === 'mceInsertContent' ) {
888 // When inserting content, if the caret is inside a caption create new paragraph under
889 // and move the caret there
890 if ( node = dom.getParent( editor.selection.getNode(), 'div.mceTemp' ) ) {
891 p = dom.create( 'p' );
892 dom.insertAfter( p, node );
893 editor.selection.setCursorLocation( p, 0 );
894 editor.nodeChanged();
896 } else if ( cmd === 'JustifyLeft' || cmd === 'JustifyRight' || cmd === 'JustifyCenter' ) {
897 node = editor.selection.getNode();
898 align = cmd.substr(7).toLowerCase();
899 align = 'align' + align;
900 DL = dom.getParent( node, 'dl.wp-caption' );
905 // When inside an image caption, set the align* class on dl.wp-caption
906 if ( dom.hasClass( DL, align ) ) {
907 dom.removeClass( DL, align );
908 dom.addClass( DL, 'alignnone' );
910 DL.className = DL.className.replace( /align[^ ]+/g, '' );
911 dom.addClass( DL, align );
914 if ( node.nodeName === 'IMG' ) {
915 // Re-select the image to update resize handles, etc.
916 editor.nodeChanged();
919 event.preventDefault();
922 if ( node.nodeName === 'IMG' ) {
923 if ( dom.hasClass( node, align ) ) {
924 // The align class is being removed
925 dom.addClass( node, 'alignnone' );
927 dom.removeClass( node, 'alignnone' );
933 editor.on( 'keydown', function( event ) {
934 var node, wrap, P, spacer,
935 selection = editor.selection,
936 keyCode = event.keyCode,
938 VK = tinymce.util.VK;
940 if ( keyCode === VK.ENTER ) {
941 // When pressing Enter inside a caption move the caret to a new parapraph under it
942 node = selection.getNode();
943 wrap = dom.getParent( node, 'div.mceTemp' );
946 dom.events.cancel( event ); // Doesn't cancel all :(
948 // Remove any extra dt and dd cleated on pressing Enter...
949 tinymce.each( dom.select( 'dt, dd', wrap ), function( element ) {
950 if ( dom.isEmpty( element ) ) {
951 dom.remove( element );
955 spacer = tinymce.Env.ie && tinymce.Env.ie < 11 ? '' : '<br data-mce-bogus="1" />';
956 P = dom.create( 'p', null, spacer );
958 if ( node.nodeName === 'DD' ) {
959 dom.insertAfter( P, wrap );
961 wrap.parentNode.insertBefore( P, wrap );
964 editor.nodeChanged();
965 selection.setCursorLocation( P, 0 );
967 } else if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) {
968 node = selection.getNode();
970 if ( node.nodeName === 'DIV' && dom.hasClass( node, 'mceTemp' ) ) {
972 } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) {
973 wrap = dom.getParent( node, 'div.mceTemp' );
977 dom.events.cancel( event );
985 // Most key presses will replace the image so we need to remove the toolbar
986 if ( toolbarActive ) {
987 if ( event.ctrlKey || event.metaKey || event.altKey || ( keyCode < 48 && keyCode !== VK.SPACEBAR ) ) {
995 editor.on( 'mousedown', function( event ) {
996 if ( isToolbarButton( event.target ) ) {
997 if ( tinymce.Env.ie ) {
998 // Stop IE > 8 from making the wrapper resizable on mousedown
999 event.preventDefault();
1001 } else if ( event.target.nodeName !== 'IMG' ) {
1006 // Remove from undo levels
1007 editor.on( 'BeforeAddUndo', function( event ) {
1008 event.level.content = event.level.content.replace( / data-wp-imgselect="1"/g, '' );
1011 // After undo/redo FF seems to set the image height very slowly when it is set to 'auto' in the CSS.
1012 // This causes image.getBoundingClientRect() to return wrong values and the resize handles are shown in wrong places.
1013 // Collapse the selection to remove the resize handles.
1014 if ( tinymce.Env.gecko ) {
1015 editor.on( 'undo redo', function() {
1016 if ( editor.selection.getNode().nodeName === 'IMG' ) {
1017 editor.selection.collapse();
1022 editor.on( 'cut wpview-selected', function() {
1026 editor.wpSetImgCaption = function( content ) {
1027 return parseShortcode( content );
1030 editor.wpGetImgCaption = function( content ) {
1031 return getShortcode( content );
1034 editor.on( 'BeforeSetContent', function( event ) {
1035 if ( event.format !== 'raw' ) {
1036 event.content = editor.wpSetImgCaption( event.content );
1040 editor.on( 'PostProcess', function( event ) {
1042 event.content = editor.wpGetImgCaption( event.content );
1043 event.content = event.content.replace( / data-wp-imgselect="1"/g, '' );
1048 _do_shcode: parseShortcode,
1049 _get_shcode: getShortcode