wp/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    16 			removeImage( editor.selection.getNode() );
    16 			removeImage( editor.selection.getNode() );
    17 		}
    17 		}
    18 	} );
    18 	} );
    19 
    19 
    20 	editor.addButton( 'wp_img_edit', {
    20 	editor.addButton( 'wp_img_edit', {
    21 		tooltip: 'Edit|button', // '|button' is not displayed, only used for context
    21 		tooltip: 'Edit|button', // '|button' is not displayed, only used for context.
    22 		icon: 'dashicon dashicons-edit',
    22 		icon: 'dashicon dashicons-edit',
    23 		onclick: function() {
    23 		onclick: function() {
    24 			editImage( editor.selection.getNode() );
    24 			editImage( editor.selection.getNode() );
    25 		}
    25 		}
    26 	} );
    26 	} );
   144 
   144 
   145 			if ( img && img[2] ) {
   145 			if ( img && img[2] ) {
   146 				caption = trim( img[2] );
   146 				caption = trim( img[2] );
   147 				img = trim( img[1] );
   147 				img = trim( img[1] );
   148 			} else {
   148 			} else {
   149 				// old captions shortcode style
   149 				// Old captions shortcode style.
   150 				caption = trim( b ).replace( /caption=['"]/, '' ).replace( /['"]$/, '' );
   150 				caption = trim( b ).replace( /caption=['"]/, '' ).replace( /['"]$/, '' );
   151 				img = c;
   151 				img = c;
   152 			}
   152 			}
   153 
   153 
   154 			id = ( id && id[1] ) ? id[1].replace( /[<>&]+/g,  '' ) : '';
   154 			id = ( id && id[1] ) ? id[1].replace( /[<>&]+/g,  '' ) : '';
   212 				if ( classes ) {
   212 				if ( classes ) {
   213 					classes = ' class="' + classes + '"';
   213 					classes = ' class="' + classes + '"';
   214 				}
   214 				}
   215 
   215 
   216 				caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) {
   216 				caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) {
   217 					// no line breaks inside HTML tags
   217 					// No line breaks inside HTML tags.
   218 					return a.replace( /[\r\n\t]+/, ' ' );
   218 					return a.replace( /[\r\n\t]+/, ' ' );
   219 				});
   219 				});
   220 
   220 
   221 				// convert remaining line breaks to <br>
   221 				// Convert remaining line breaks to <br>.
   222 				caption = caption.replace( /\s*\n\s*/g, '<br />' );
   222 				caption = caption.replace( /\s*\n\s*/g, '<br />' );
   223 
   223 
   224 				return '[caption id="' + id + '" align="' + align + '" width="' + width + '"' + classes + ']' + c + ' ' + caption + '[/caption]';
   224 				return '[caption id="' + id + '" align="' + align + '" width="' + width + '"' + classes + ']' + c + ' ' + caption + '[/caption]';
   225 			});
   225 			});
   226 
   226 
   227 			if ( out.indexOf('[caption') === -1 ) {
   227 			if ( out.indexOf('[caption') === -1 ) {
   228 				// the caption html seems broken, try to find the image that may be wrapped in a link
   228 				// The caption html seems broken, try to find the image that may be wrapped in a link
   229 				// and may be followed by <p> with the caption text.
   229 				// and may be followed by <p> with the caption text.
   230 				out = dl.replace( /[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2' );
   230 				out = dl.replace( /[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2' );
   231 			}
   231 			}
   232 
   232 
   233 			return out;
   233 			return out;
   238 		var classes, extraClasses, metadata, captionBlock, caption, link, width, height,
   238 		var classes, extraClasses, metadata, captionBlock, caption, link, width, height,
   239 			captionClassName = [],
   239 			captionClassName = [],
   240 			dom = editor.dom,
   240 			dom = editor.dom,
   241 			isIntRegExp = /^\d+$/;
   241 			isIntRegExp = /^\d+$/;
   242 
   242 
   243 		// default attributes
   243 		// Default attributes.
   244 		metadata = {
   244 		metadata = {
   245 			attachment_id: false,
   245 			attachment_id: false,
   246 			size: 'custom',
   246 			size: 'custom',
   247 			caption: '',
   247 			caption: '',
   248 			align: 'none',
   248 			align: 'none',
   290 
   290 
   291 		} );
   291 		} );
   292 
   292 
   293 		metadata.extraClasses = extraClasses.join( ' ' );
   293 		metadata.extraClasses = extraClasses.join( ' ' );
   294 
   294 
   295 		// Extract caption
   295 		// Extract caption.
   296 		captionBlock = dom.getParents( imageNode, '.wp-caption' );
   296 		captionBlock = dom.getParents( imageNode, '.wp-caption' );
   297 
   297 
   298 		if ( captionBlock.length ) {
   298 		if ( captionBlock.length ) {
   299 			captionBlock = captionBlock[0];
   299 			captionBlock = captionBlock[0];
   300 
   300 
   316 				metadata.caption = editor.serializer.serialize( caption )
   316 				metadata.caption = editor.serializer.serialize( caption )
   317 					.replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' );
   317 					.replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' );
   318 			}
   318 			}
   319 		}
   319 		}
   320 
   320 
   321 		// Extract linkTo
   321 		// Extract linkTo.
   322 		if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' ) {
   322 		if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' ) {
   323 			link = imageNode.parentNode;
   323 			link = imageNode.parentNode;
   324 			metadata.linkUrl = dom.getAttrib( link, 'href' );
   324 			metadata.linkUrl = dom.getAttrib( link, 'href' );
   325 			metadata.linkTargetBlank = dom.getAttrib( link, 'target' ) === '_blank' ? true : false;
   325 			metadata.linkTargetBlank = dom.getAttrib( link, 'target' ) === '_blank' ? true : false;
   326 			metadata.linkRel = dom.getAttrib( link, 'rel' );
   326 			metadata.linkRel = dom.getAttrib( link, 'rel' );
   332 
   332 
   333 	function hasTextContent( node ) {
   333 	function hasTextContent( node ) {
   334 		return node && !! ( node.textContent || node.innerText ).replace( /\ufeff/g, '' );
   334 		return node && !! ( node.textContent || node.innerText ).replace( /\ufeff/g, '' );
   335 	}
   335 	}
   336 
   336 
   337 	// Verify HTML in captions
   337 	// Verify HTML in captions.
   338 	function verifyHTML( caption ) {
   338 	function verifyHTML( caption ) {
   339 		if ( ! caption || ( caption.indexOf( '<' ) === -1 && caption.indexOf( '>' ) === -1 ) ) {
   339 		if ( ! caption || ( caption.indexOf( '<' ) === -1 && caption.indexOf( '>' ) === -1 ) ) {
   340 			return caption;
   340 			return caption;
   341 		}
   341 		}
   342 
   342 
   402 			target: imageData.linkTargetBlank ? '_blank': null,
   402 			target: imageData.linkTargetBlank ? '_blank': null,
   403 			'class': imageData.linkClassName || null
   403 			'class': imageData.linkClassName || null
   404 		};
   404 		};
   405 
   405 
   406 		if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) {
   406 		if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) {
   407 			// Update or remove an existing link wrapped around the image
   407 			// Update or remove an existing link wrapped around the image.
   408 			if ( imageData.linkUrl ) {
   408 			if ( imageData.linkUrl ) {
   409 				dom.setAttribs( imageNode.parentNode, linkAttrs );
   409 				dom.setAttribs( imageNode.parentNode, linkAttrs );
   410 			} else {
   410 			} else {
   411 				dom.remove( imageNode.parentNode, true );
   411 				dom.remove( imageNode.parentNode, true );
   412 			}
   412 			}
   413 		} else if ( imageData.linkUrl ) {
   413 		} else if ( imageData.linkUrl ) {
   414 			if ( linkNode = dom.getParent( imageNode, 'a' ) ) {
   414 			if ( linkNode = dom.getParent( imageNode, 'a' ) ) {
   415 				// The image is inside a link together with other nodes,
   415 				// The image is inside a link together with other nodes,
   416 				// or is nested in another node, move it out
   416 				// or is nested in another node, move it out.
   417 				dom.insertAfter( imageNode, linkNode );
   417 				dom.insertAfter( imageNode, linkNode );
   418 			}
   418 			}
   419 
   419 
   420 			// Add link wrapped around the image
   420 			// Add link wrapped around the image.
   421 			linkNode = dom.create( 'a', linkAttrs );
   421 			linkNode = dom.create( 'a', linkAttrs );
   422 			imageNode.parentNode.insertBefore( linkNode, imageNode );
   422 			imageNode.parentNode.insertBefore( linkNode, imageNode );
   423 			linkNode.appendChild( imageNode );
   423 			linkNode.appendChild( imageNode );
   424 		}
   424 		}
   425 
   425 
   465 				}
   465 				}
   466 
   466 
   467 			} else {
   467 			} else {
   468 				id = id ? 'id="'+ id +'" ' : '';
   468 				id = id ? 'id="'+ id +'" ' : '';
   469 
   469 
   470 				// should create a new function for generating the caption markup
   470 				// Should create a new function for generating the caption markup.
   471 				html =  '<dl ' + id + 'class="' + className +'" style="width: '+ width +'px">' +
   471 				html =  '<dl ' + id + 'class="' + className +'" style="width: '+ width +'px">' +
   472 					'<dt class="wp-caption-dt"></dt><dd class="wp-caption-dd">'+ imageData.caption +'</dd></dl>';
   472 					'<dt class="wp-caption-dt"></dt><dd class="wp-caption-dd">'+ imageData.caption +'</dd></dl>';
   473 
   473 
   474 				wrap = dom.create( 'div', { 'class': 'mceTemp' }, html );
   474 				wrap = dom.create( 'div', { 'class': 'mceTemp' }, html );
   475 
   475 
   484 				if ( parent && dom.isEmpty( parent ) ) {
   484 				if ( parent && dom.isEmpty( parent ) ) {
   485 					dom.remove( parent );
   485 					dom.remove( parent );
   486 				}
   486 				}
   487 			}
   487 			}
   488 		} else if ( captionNode ) {
   488 		} else if ( captionNode ) {
   489 			// Remove the caption wrapper and place the image in new paragraph
   489 			// Remove the caption wrapper and place the image in new paragraph.
   490 			parent = dom.create( 'p' );
   490 			parent = dom.create( 'p' );
   491 			captionNode.parentNode.insertBefore( parent, captionNode );
   491 			captionNode.parentNode.insertBefore( parent, captionNode );
   492 			parent.appendChild( node );
   492 			parent.appendChild( node );
   493 			dom.remove( captionNode );
   493 			dom.remove( captionNode );
   494 		}
   494 		}
   528 		metadata = extractImageData( img );
   528 		metadata = extractImageData( img );
   529 
   529 
   530 		// Mark the image node so we can select it later.
   530 		// Mark the image node so we can select it later.
   531 		editor.$( img ).attr( 'data-wp-editing', 1 );
   531 		editor.$( img ).attr( 'data-wp-editing', 1 );
   532 
   532 
   533 		// Manipulate the metadata by reference that is fed into the PostImage model used in the media modal
   533 		// Manipulate the metadata by reference that is fed into the PostImage model used in the media modal.
   534 		wp.media.events.trigger( 'editor:image-edit', {
   534 		wp.media.events.trigger( 'editor:image-edit', {
   535 			editor: editor,
   535 			editor: editor,
   536 			metadata: metadata,
   536 			metadata: metadata,
   537 			image: img
   537 			image: img
   538 		} );
   538 		} );
   556 		frame.state('replace-image').on( 'replace', callback );
   556 		frame.state('replace-image').on( 'replace', callback );
   557 		frame.on( 'close', function() {
   557 		frame.on( 'close', function() {
   558 			editor.focus();
   558 			editor.focus();
   559 			frame.detach();
   559 			frame.detach();
   560 
   560 
   561 			// `close` fires first...
   561 			/*
   562 			// To be able to update the image node, we need to find it here,
   562 			 * `close` fires first...
   563 			// and use it in the callback.
   563 			 * To be able to update the image node, we need to find it here,
       
   564 			 * and use it in the callback.
       
   565 			 */
   564 			imageNode = editor.$( 'img[data-wp-editing]' )
   566 			imageNode = editor.$( 'img[data-wp-editing]' )
   565 			imageNode.removeAttr( 'data-wp-editing' );
   567 			imageNode.removeAttr( 'data-wp-editing' );
   566 		});
   568 		});
   567 
   569 
   568 		frame.open();
   570 		frame.open();
   598 		var dom = editor.dom,
   600 		var dom = editor.dom,
   599 			captionClass = editor.getParam( 'wpeditimage_html5_captions' ) ? 'html5-captions' : 'html4-captions';
   601 			captionClass = editor.getParam( 'wpeditimage_html5_captions' ) ? 'html5-captions' : 'html4-captions';
   600 
   602 
   601 		dom.addClass( editor.getBody(), captionClass );
   603 		dom.addClass( editor.getBody(), captionClass );
   602 
   604 
   603 		// Prevent IE11 from making dl.wp-caption resizable
   605 		// Prevent IE11 from making dl.wp-caption resizable.
   604 		if ( tinymce.Env.ie && tinymce.Env.ie > 10 ) {
   606 		if ( tinymce.Env.ie && tinymce.Env.ie > 10 ) {
   605 			// The 'mscontrolselect' event is supported only in IE11+
   607 			// The 'mscontrolselect' event is supported only in IE11+.
   606 			dom.bind( editor.getBody(), 'mscontrolselect', function( event ) {
   608 			dom.bind( editor.getBody(), 'mscontrolselect', function( event ) {
   607 				if ( event.target.nodeName === 'IMG' && dom.getParent( event.target, '.wp-caption' ) ) {
   609 				if ( event.target.nodeName === 'IMG' && dom.getParent( event.target, '.wp-caption' ) ) {
   608 					// Hide the thick border with resize handles around dl.wp-caption
   610 					// Hide the thick border with resize handles around dl.wp-caption.
   609 					editor.getBody().focus(); // :(
   611 					editor.getBody().focus(); // :(
   610 				} else if ( event.target.nodeName === 'DL' && dom.hasClass( event.target, 'wp-caption' ) ) {
   612 				} else if ( event.target.nodeName === 'DL' && dom.hasClass( event.target, 'wp-caption' ) ) {
   611 					// Trigger the thick border with resize handles...
   613 					// Trigger the thick border with resize handles...
   612 					// This will make the caption text editable.
   614 					// This will make the caption text editable.
   613 					event.target.focus();
   615 					event.target.focus();
   686 
   688 
   687 			if ( captionParent ) {
   689 			if ( captionParent ) {
   688 				if ( cmd === 'mceInsertContent' ) {
   690 				if ( cmd === 'mceInsertContent' ) {
   689 					if ( pasteInCaption ) {
   691 					if ( pasteInCaption ) {
   690 						pasteInCaption = false;
   692 						pasteInCaption = false;
   691 						// We are in the caption element, and in 'paste' context,
   693 						/*
   692 						// and the pasted HTML was cleaned up on 'pastePostProcess' above.
   694 						 * We are in the caption element, and in 'paste' context,
   693 						// Let it be pasted in the caption.
   695 						 * and the pasted HTML was cleaned up on 'pastePostProcess' above.
       
   696 						 * Let it be pasted in the caption.
       
   697 						 */
   694 						return;
   698 						return;
   695 					}
   699 					}
   696 
   700 
   697 					// The paste is somewhere else in the caption DL element.
   701 					/*
   698 					// Prevent pasting in there as it will break the caption.
   702 					 * The paste is somewhere else in the caption DL element.
   699 					// Make new paragraph under the caption DL and move the caret there.
   703 					 * Prevent pasting in there as it will break the caption.
       
   704 					 * Make new paragraph under the caption DL and move the caret there.
       
   705 					 */
   700 					p = dom.create( 'p' );
   706 					p = dom.create( 'p' );
   701 					dom.insertAfter( p, captionParent );
   707 					dom.insertAfter( p, captionParent );
   702 					editor.selection.setCursorLocation( p, 0 );
   708 					editor.selection.setCursorLocation( p, 0 );
   703 
   709 
   704 					// If the image is selected and the user pastes "over" it,
   710 					/*
   705 					// replace both the image and the caption elements with the pasted content.
   711 					 * If the image is selected and the user pastes "over" it,
   706 					// This matches the behavior when pasting over non-caption images.
   712 					 * replace both the image and the caption elements with the pasted content.
       
   713 					 * This matches the behavior when pasting over non-caption images.
       
   714 					 */
   707 					if ( node.nodeName === 'IMG' ) {
   715 					if ( node.nodeName === 'IMG' ) {
   708                         editor.$( captionParent ).remove();
   716 						editor.$( captionParent ).remove();
   709                     }
   717 					}
   710 
   718 
   711 					editor.nodeChanged();
   719 					editor.nodeChanged();
   712 				} else {
   720 				} else {
   713 					// Clicking Indent or Outdent while an image with a caption is selected breaks the caption.
   721 					// Clicking Indent or Outdent while an image with a caption is selected breaks the caption.
   714 					// See #38313.
   722 					// See #38313.
   757 			keyCode = event.keyCode,
   765 			keyCode = event.keyCode,
   758 			dom = editor.dom,
   766 			dom = editor.dom,
   759 			VK = tinymce.util.VK;
   767 			VK = tinymce.util.VK;
   760 
   768 
   761 		if ( keyCode === VK.ENTER ) {
   769 		if ( keyCode === VK.ENTER ) {
   762 			// When pressing Enter inside a caption move the caret to a new parapraph under it
   770 			// When pressing Enter inside a caption move the caret to a new parapraph under it.
   763 			node = selection.getNode();
   771 			node = selection.getNode();
   764 			wrap = dom.getParent( node, 'div.mceTemp' );
   772 			wrap = dom.getParent( node, 'div.mceTemp' );
   765 
   773 
   766 			if ( wrap ) {
   774 			if ( wrap ) {
   767 				dom.events.cancel( event ); // Doesn't cancel all :(
   775 				dom.events.cancel( event ); // Doesn't cancel all :(
   800 				return false;
   808 				return false;
   801 			}
   809 			}
   802 		}
   810 		}
   803 	});
   811 	});
   804 
   812 
   805 	// After undo/redo FF seems to set the image height very slowly when it is set to 'auto' in the CSS.
   813 	/*
   806 	// This causes image.getBoundingClientRect() to return wrong values and the resize handles are shown in wrong places.
   814 	 * After undo/redo FF seems to set the image height very slowly when it is set to 'auto' in the CSS.
   807 	// Collapse the selection to remove the resize handles.
   815 	 * This causes image.getBoundingClientRect() to return wrong values and the resize handles are shown in wrong places.
       
   816 	 * Collapse the selection to remove the resize handles.
       
   817 	 */
   808 	if ( tinymce.Env.gecko ) {
   818 	if ( tinymce.Env.gecko ) {
   809 		editor.on( 'undo redo', function() {
   819 		editor.on( 'undo redo', function() {
   810 			if ( editor.selection.getNode().nodeName === 'IMG' ) {
   820 			if ( editor.selection.getNode().nodeName === 'IMG' ) {
   811 				editor.selection.collapse();
   821 				editor.selection.collapse();
   812 			}
   822 			}
   876 
   886 
   877 			wrap = null;
   887 			wrap = null;
   878 		} );
   888 		} );
   879 	} )();
   889 	} )();
   880 
   890 
   881 	// Add to editor.wp
   891 	// Add to editor.wp.
   882 	editor.wp = editor.wp || {};
   892 	editor.wp = editor.wp || {};
   883 	editor.wp.isPlaceholder = isPlaceholder;
   893 	editor.wp.isPlaceholder = isPlaceholder;
   884 
   894 
   885 	// Back-compat.
   895 	// Back-compat.
   886 	return {
   896 	return {