[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 /* global tinymce */ 2 tinymce.PluginManager.add( 'wpeditimage', function( editor ) { 3 var toolbar, serializer, touchOnImage, pasteInCaption, 4 each = tinymce.each, 5 trim = tinymce.trim, 6 iOS = tinymce.Env.iOS; 7 8 function isPlaceholder( node ) { 9 return !! ( editor.dom.getAttrib( node, 'data-mce-placeholder' ) || editor.dom.getAttrib( node, 'data-mce-object' ) ); 10 } 11 12 editor.addButton( 'wp_img_remove', { 13 tooltip: 'Remove', 14 icon: 'dashicon dashicons-no', 15 onclick: function() { 16 removeImage( editor.selection.getNode() ); 17 } 18 } ); 19 20 editor.addButton( 'wp_img_edit', { 21 tooltip: 'Edit|button', // '|button' is not displayed, only used for context. 22 icon: 'dashicon dashicons-edit', 23 onclick: function() { 24 editImage( editor.selection.getNode() ); 25 } 26 } ); 27 28 each( { 29 alignleft: 'Align left', 30 aligncenter: 'Align center', 31 alignright: 'Align right', 32 alignnone: 'No alignment' 33 }, function( tooltip, name ) { 34 var direction = name.slice( 5 ); 35 36 editor.addButton( 'wp_img_' + name, { 37 tooltip: tooltip, 38 icon: 'dashicon dashicons-align-' + direction, 39 cmd: 'alignnone' === name ? 'wpAlignNone' : 'Justify' + direction.slice( 0, 1 ).toUpperCase() + direction.slice( 1 ), 40 onPostRender: function() { 41 var self = this; 42 43 editor.on( 'NodeChange', function( event ) { 44 var node; 45 46 // Don't bother. 47 if ( event.element.nodeName !== 'IMG' ) { 48 return; 49 } 50 51 node = editor.dom.getParent( event.element, '.wp-caption' ) || event.element; 52 53 if ( 'alignnone' === name ) { 54 self.active( ! /\balign(left|center|right)\b/.test( node.className ) ); 55 } else { 56 self.active( editor.dom.hasClass( node, name ) ); 57 } 58 } ); 59 } 60 } ); 61 } ); 62 63 editor.once( 'preinit', function() { 64 if ( editor.wp && editor.wp._createToolbar ) { 65 toolbar = editor.wp._createToolbar( [ 66 'wp_img_alignleft', 67 'wp_img_aligncenter', 68 'wp_img_alignright', 69 'wp_img_alignnone', 70 'wp_img_edit', 71 'wp_img_remove' 72 ] ); 73 } 74 } ); 75 76 editor.on( 'wptoolbar', function( event ) { 77 if ( event.element.nodeName === 'IMG' && ! isPlaceholder( event.element ) ) { 78 event.toolbar = toolbar; 79 } 80 } ); 81 82 function isNonEditable( node ) { 83 var parent = editor.$( node ).parents( '[contenteditable]' ); 84 return parent && parent.attr( 'contenteditable' ) === 'false'; 85 } 86 87 // Safari on iOS fails to select images in contentEditoble mode on touch. 88 // Select them again. 89 if ( iOS ) { 90 editor.on( 'init', function() { 91 editor.on( 'touchstart', function( event ) { 92 if ( event.target.nodeName === 'IMG' && ! isNonEditable( event.target ) ) { 93 touchOnImage = true; 94 } 95 }); 96 97 editor.dom.bind( editor.getDoc(), 'touchmove', function() { 98 touchOnImage = false; 99 }); 100 101 editor.on( 'touchend', function( event ) { 102 if ( touchOnImage && event.target.nodeName === 'IMG' && ! isNonEditable( event.target ) ) { 103 var node = event.target; 104 105 touchOnImage = false; 106 107 window.setTimeout( function() { 108 editor.selection.select( node ); 109 editor.nodeChanged(); 110 }, 100 ); 111 } else if ( toolbar ) { 112 toolbar.hide(); 113 } 114 }); 115 }); 116 } 117 118 function parseShortcode( content ) { 119 return content.replace( /(?:<p>)?\[(?:wp_)?caption([^\]]+)\]([\s\S]+?)\[\/(?:wp_)?caption\](?:<\/p>)?/g, function( a, b, c ) { 120 var id, align, classes, caption, img, width; 121 122 id = b.match( /id=['"]([^'"]*)['"] ?/ ); 123 if ( id ) { 124 b = b.replace( id[0], '' ); 125 } 126 127 align = b.match( /align=['"]([^'"]*)['"] ?/ ); 128 if ( align ) { 129 b = b.replace( align[0], '' ); 130 } 131 132 classes = b.match( /class=['"]([^'"]*)['"] ?/ ); 133 if ( classes ) { 134 b = b.replace( classes[0], '' ); 135 } 136 137 width = b.match( /width=['"]([0-9]*)['"] ?/ ); 138 if ( width ) { 139 b = b.replace( width[0], '' ); 140 } 141 142 c = trim( c ); 143 img = c.match( /((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)([\s\S]*)/i ); 144 145 if ( img && img[2] ) { 146 caption = trim( img[2] ); 147 img = trim( img[1] ); 148 } else { 149 // Old captions shortcode style. 150 caption = trim( b ).replace( /caption=['"]/, '' ).replace( /['"]$/, '' ); 151 img = c; 152 } 153 154 id = ( id && id[1] ) ? id[1].replace( /[<>&]+/g, '' ) : ''; 155 align = ( align && align[1] ) ? align[1] : 'alignnone'; 156 classes = ( classes && classes[1] ) ? ' ' + classes[1].replace( /[<>&]+/g, '' ) : ''; 157 158 if ( ! width && img ) { 159 width = img.match( /width=['"]([0-9]*)['"]/ ); 160 } 161 162 if ( width && width[1] ) { 163 width = width[1]; 164 } 165 166 if ( ! width || ! caption ) { 167 return c; 168 } 169 170 width = parseInt( width, 10 ); 171 if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { 172 width += 10; 173 } 174 175 return '<div class="mceTemp"><dl id="' + id + '" class="wp-caption ' + align + classes + '" style="width: ' + width + 'px">' + 176 '<dt class="wp-caption-dt">'+ img +'</dt><dd class="wp-caption-dd">'+ caption +'</dd></dl></div>'; 177 }); 178 } 179 180 function getShortcode( content ) { 181 return content.replace( /(?:<div [^>]+mceTemp[^>]+>)?\s*(<dl [^>]+wp-caption[^>]+>[\s\S]+?<\/dl>)\s*(?:<\/div>)?/g, function( all, dl ) { 182 var out = ''; 183 184 if ( dl.indexOf('<img ') === -1 || dl.indexOf('</p>') !== -1 ) { 185 // Broken caption. The user managed to drag the image out or type in the wrapper div? 186 // Remove the <dl>, <dd> and <dt> and return the remaining text. 187 return dl.replace( /<d[ldt]( [^>]+)?>/g, '' ).replace( /<\/d[ldt]>/g, '' ); 188 } 189 190 out = dl.replace( /\s*<dl ([^>]+)>\s*<dt [^>]+>([\s\S]+?)<\/dt>\s*<dd [^>]+>([\s\S]*?)<\/dd>\s*<\/dl>\s*/gi, function( a, b, c, caption ) { 191 var id, classes, align, width; 192 193 width = c.match( /width="([0-9]*)"/ ); 194 width = ( width && width[1] ) ? width[1] : ''; 195 196 classes = b.match( /class="([^"]*)"/ ); 197 classes = ( classes && classes[1] ) ? classes[1] : ''; 198 align = classes.match( /align[a-z]+/i ) || 'alignnone'; 199 200 if ( ! width || ! caption ) { 201 if ( 'alignnone' !== align[0] ) { 202 c = c.replace( /><img/, ' class="' + align[0] + '"><img' ); 203 } 204 return c; 205 } 206 207 id = b.match( /id="([^"]*)"/ ); 208 id = ( id && id[1] ) ? id[1] : ''; 209 210 classes = classes.replace( /wp-caption ?|align[a-z]+ ?/gi, '' ); 211 212 if ( classes ) { 213 classes = ' class="' + classes + '"'; 214 } 215 216 caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) { 217 // No line breaks inside HTML tags. 218 return a.replace( /[\r\n\t]+/, ' ' ); 219 }); 220 221 // Convert remaining line breaks to <br>. 222 caption = caption.replace( /\s*\n\s*/g, '<br />' ); 223 224 return '[caption id="' + id + '" align="' + align + '" width="' + width + '"' + classes + ']' + c + ' ' + caption + '[/caption]'; 225 }); 226 227 if ( out.indexOf('[caption') === -1 ) { 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. 230 out = dl.replace( /[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2' ); 231 } 232 233 return out; 234 }); 235 } 236 237 function extractImageData( imageNode ) { 238 var classes, extraClasses, metadata, captionBlock, caption, link, width, height, 239 captionClassName = [], 240 dom = editor.dom, 241 isIntRegExp = /^\d+$/; 242 243 // Default attributes. 244 metadata = { 245 attachment_id: false, 246 size: 'custom', 247 caption: '', 248 align: 'none', 249 extraClasses: '', 250 link: false, 251 linkUrl: '', 252 linkClassName: '', 253 linkTargetBlank: false, 254 linkRel: '', 255 title: '' 256 }; 257 258 metadata.url = dom.getAttrib( imageNode, 'src' ); 259 metadata.alt = dom.getAttrib( imageNode, 'alt' ); 260 metadata.title = dom.getAttrib( imageNode, 'title' ); 261 262 width = dom.getAttrib( imageNode, 'width' ); 263 height = dom.getAttrib( imageNode, 'height' ); 264 265 if ( ! isIntRegExp.test( width ) || parseInt( width, 10 ) < 1 ) { 266 width = imageNode.naturalWidth || imageNode.width; 267 } 268 269 if ( ! isIntRegExp.test( height ) || parseInt( height, 10 ) < 1 ) { 270 height = imageNode.naturalHeight || imageNode.height; 271 } 272 273 metadata.customWidth = metadata.width = width; 274 metadata.customHeight = metadata.height = height; 275 276 classes = tinymce.explode( imageNode.className, ' ' ); 277 extraClasses = []; 278 279 tinymce.each( classes, function( name ) { 280 281 if ( /^wp-image/.test( name ) ) { 282 metadata.attachment_id = parseInt( name.replace( 'wp-image-', '' ), 10 ); 283 } else if ( /^align/.test( name ) ) { 284 metadata.align = name.replace( 'align', '' ); 285 } else if ( /^size/.test( name ) ) { 286 metadata.size = name.replace( 'size-', '' ); 287 } else { 288 extraClasses.push( name ); 289 } 290 291 } ); 292 293 metadata.extraClasses = extraClasses.join( ' ' ); 294 295 // Extract caption. 296 captionBlock = dom.getParents( imageNode, '.wp-caption' ); 297 298 if ( captionBlock.length ) { 299 captionBlock = captionBlock[0]; 300 301 classes = captionBlock.className.split( ' ' ); 302 tinymce.each( classes, function( name ) { 303 if ( /^align/.test( name ) ) { 304 metadata.align = name.replace( 'align', '' ); 305 } else if ( name && name !== 'wp-caption' ) { 306 captionClassName.push( name ); 307 } 308 } ); 309 310 metadata.captionClassName = captionClassName.join( ' ' ); 311 312 caption = dom.select( 'dd.wp-caption-dd', captionBlock ); 313 if ( caption.length ) { 314 caption = caption[0]; 315 316 metadata.caption = editor.serializer.serialize( caption ) 317 .replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' ); 318 } 319 } 320 321 // Extract linkTo. 322 if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' ) { 323 link = imageNode.parentNode; 324 metadata.linkUrl = dom.getAttrib( link, 'href' ); 325 metadata.linkTargetBlank = dom.getAttrib( link, 'target' ) === '_blank' ? true : false; 326 metadata.linkRel = dom.getAttrib( link, 'rel' ); 327 metadata.linkClassName = link.className; 328 } 329 330 return metadata; 331 } 332 333 function hasTextContent( node ) { 334 return node && !! ( node.textContent || node.innerText ).replace( /\ufeff/g, '' ); 335 } 336 337 // Verify HTML in captions. 338 function verifyHTML( caption ) { 339 if ( ! caption || ( caption.indexOf( '<' ) === -1 && caption.indexOf( '>' ) === -1 ) ) { 340 return caption; 341 } 342 343 if ( ! serializer ) { 344 serializer = new tinymce.html.Serializer( {}, editor.schema ); 345 } 346 347 return serializer.serialize( editor.parser.parse( caption, { forced_root_block: false } ) ); 348 } 349 350 function updateImage( $imageNode, imageData ) { 351 var classes, className, node, html, parent, wrap, linkNode, imageNode, 352 captionNode, dd, dl, id, attrs, linkAttrs, width, height, align, 353 $imageNode, srcset, src, 354 dom = editor.dom; 355 356 if ( ! $imageNode || ! $imageNode.length ) { 357 return; 358 } 359 360 imageNode = $imageNode[0]; 361 classes = tinymce.explode( imageData.extraClasses, ' ' ); 362 363 if ( ! classes ) { 364 classes = []; 365 } 366 367 if ( ! imageData.caption ) { 368 classes.push( 'align' + imageData.align ); 369 } 370 371 if ( imageData.attachment_id ) { 372 classes.push( 'wp-image-' + imageData.attachment_id ); 373 if ( imageData.size && imageData.size !== 'custom' ) { 374 classes.push( 'size-' + imageData.size ); 375 } 376 } 377 378 width = imageData.width; 379 height = imageData.height; 380 381 if ( imageData.size === 'custom' ) { 382 width = imageData.customWidth; 383 height = imageData.customHeight; 384 } 385 386 attrs = { 387 src: imageData.url, 388 width: width || null, 389 height: height || null, 390 title: imageData.title || null, 391 'class': classes.join( ' ' ) || null 392 }; 393 394 dom.setAttribs( imageNode, attrs ); 395 396 // Preserve empty alt attributes. 397 $imageNode.attr( 'alt', imageData.alt || '' ); 398 399 linkAttrs = { 400 href: imageData.linkUrl, 401 rel: imageData.linkRel || null, 402 target: imageData.linkTargetBlank ? '_blank': null, 403 'class': imageData.linkClassName || null 404 }; 405 406 if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) { 407 // Update or remove an existing link wrapped around the image. 408 if ( imageData.linkUrl ) { 409 dom.setAttribs( imageNode.parentNode, linkAttrs ); 410 } else { 411 dom.remove( imageNode.parentNode, true ); 412 } 413 } else if ( imageData.linkUrl ) { 414 if ( linkNode = dom.getParent( imageNode, 'a' ) ) { 415 // The image is inside a link together with other nodes, 416 // or is nested in another node, move it out. 417 dom.insertAfter( imageNode, linkNode ); 418 } 419 420 // Add link wrapped around the image. 421 linkNode = dom.create( 'a', linkAttrs ); 422 imageNode.parentNode.insertBefore( linkNode, imageNode ); 423 linkNode.appendChild( imageNode ); 424 } 425 426 captionNode = editor.dom.getParent( imageNode, '.mceTemp' ); 427 428 if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) { 429 node = imageNode.parentNode; 430 } else { 431 node = imageNode; 432 } 433 434 if ( imageData.caption ) { 435 imageData.caption = verifyHTML( imageData.caption ); 436 437 id = imageData.attachment_id ? 'attachment_' + imageData.attachment_id : null; 438 align = 'align' + ( imageData.align || 'none' ); 439 className = 'wp-caption ' + align; 440 441 if ( imageData.captionClassName ) { 442 className += ' ' + imageData.captionClassName.replace( /[<>&]+/g, '' ); 443 } 444 445 if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { 446 width = parseInt( width, 10 ); 447 width += 10; 448 } 449 450 if ( captionNode ) { 451 dl = dom.select( 'dl.wp-caption', captionNode ); 452 453 if ( dl.length ) { 454 dom.setAttribs( dl, { 455 id: id, 456 'class': className, 457 style: 'width: ' + width + 'px' 458 } ); 459 } 460 461 dd = dom.select( '.wp-caption-dd', captionNode ); 462 463 if ( dd.length ) { 464 dom.setHTML( dd[0], imageData.caption ); 465 } 466 467 } else { 468 id = id ? 'id="'+ id +'" ' : ''; 469 470 // Should create a new function for generating the caption markup. 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>'; 473 474 wrap = dom.create( 'div', { 'class': 'mceTemp' }, html ); 475 476 if ( parent = dom.getParent( node, 'p' ) ) { 477 parent.parentNode.insertBefore( wrap, parent ); 478 } else { 479 node.parentNode.insertBefore( wrap, node ); 480 } 481 482 editor.$( wrap ).find( 'dt.wp-caption-dt' ).append( node ); 483 484 if ( parent && dom.isEmpty( parent ) ) { 485 dom.remove( parent ); 486 } 487 } 488 } else if ( captionNode ) { 489 // Remove the caption wrapper and place the image in new paragraph. 490 parent = dom.create( 'p' ); 491 captionNode.parentNode.insertBefore( parent, captionNode ); 492 parent.appendChild( node ); 493 dom.remove( captionNode ); 494 } 495 496 $imageNode = editor.$( imageNode ); 497 srcset = $imageNode.attr( 'srcset' ); 498 src = $imageNode.attr( 'src' ); 499 500 // Remove srcset and sizes if the image file was edited or the image was replaced. 501 if ( srcset && src ) { 502 src = src.replace( /[?#].*/, '' ); 503 504 if ( srcset.indexOf( src ) === -1 ) { 505 $imageNode.attr( 'srcset', null ).attr( 'sizes', null ); 506 } 507 } 508 509 if ( wp.media.events ) { 510 wp.media.events.trigger( 'editor:image-update', { 511 editor: editor, 512 metadata: imageData, 513 image: imageNode 514 } ); 515 } 516 517 editor.nodeChanged(); 518 } 519 520 function editImage( img ) { 521 var frame, callback, metadata, imageNode; 522 523 if ( typeof wp === 'undefined' || ! wp.media ) { 524 editor.execCommand( 'mceImage' ); 525 return; 526 } 527 528 metadata = extractImageData( img ); 529 530 // Mark the image node so we can select it later. 531 editor.$( img ).attr( 'data-wp-editing', 1 ); 532 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', { 535 editor: editor, 536 metadata: metadata, 537 image: img 538 } ); 539 540 frame = wp.media({ 541 frame: 'image', 542 state: 'image-details', 543 metadata: metadata 544 } ); 545 546 wp.media.events.trigger( 'editor:frame-create', { frame: frame } ); 547 548 callback = function( imageData ) { 549 editor.undoManager.transact( function() { 550 updateImage( imageNode, imageData ); 551 } ); 552 frame.detach(); 553 }; 554 555 frame.state('image-details').on( 'update', callback ); 556 frame.state('replace-image').on( 'replace', callback ); 557 frame.on( 'close', function() { 558 editor.focus(); 559 frame.detach(); 560 561 /* 562 * `close` fires first... 563 * To be able to update the image node, we need to find it here, 564 * and use it in the callback. 565 */ 566 imageNode = editor.$( 'img[data-wp-editing]' ) 567 imageNode.removeAttr( 'data-wp-editing' ); 568 }); 569 570 frame.open(); 571 } 572 573 function removeImage( node ) { 574 var wrap = editor.dom.getParent( node, 'div.mceTemp' ); 575 576 if ( ! wrap && node.nodeName === 'IMG' ) { 577 wrap = editor.dom.getParent( node, 'a' ); 578 } 579 580 if ( wrap ) { 581 if ( wrap.nextSibling ) { 582 editor.selection.select( wrap.nextSibling ); 583 } else if ( wrap.previousSibling ) { 584 editor.selection.select( wrap.previousSibling ); 585 } else { 586 editor.selection.select( wrap.parentNode ); 587 } 588 589 editor.selection.collapse( true ); 590 editor.dom.remove( wrap ); 591 } else { 592 editor.dom.remove( node ); 593 } 594 595 editor.nodeChanged(); 596 editor.undoManager.add(); 597 } 598 599 editor.on( 'init', function() { 600 var dom = editor.dom, 601 captionClass = editor.getParam( 'wpeditimage_html5_captions' ) ? 'html5-captions' : 'html4-captions'; 602 603 dom.addClass( editor.getBody(), captionClass ); 604 605 // Prevent IE11 from making dl.wp-caption resizable. 606 if ( tinymce.Env.ie && tinymce.Env.ie > 10 ) { 607 // The 'mscontrolselect' event is supported only in IE11+. 608 dom.bind( editor.getBody(), 'mscontrolselect', function( event ) { 609 if ( event.target.nodeName === 'IMG' && dom.getParent( event.target, '.wp-caption' ) ) { 610 // Hide the thick border with resize handles around dl.wp-caption. 611 editor.getBody().focus(); // :( 612 } else if ( event.target.nodeName === 'DL' && dom.hasClass( event.target, 'wp-caption' ) ) { 613 // Trigger the thick border with resize handles... 614 // This will make the caption text editable. 615 event.target.focus(); 616 } 617 }); 618 } 619 }); 620 621 editor.on( 'ObjectResized', function( event ) { 622 var node = event.target; 623 624 if ( node.nodeName === 'IMG' ) { 625 editor.undoManager.transact( function() { 626 var parent, width, 627 dom = editor.dom; 628 629 node.className = node.className.replace( /\bsize-[^ ]+/, '' ); 630 631 if ( parent = dom.getParent( node, '.wp-caption' ) ) { 632 width = event.width || dom.getAttrib( node, 'width' ); 633 634 if ( width ) { 635 width = parseInt( width, 10 ); 636 637 if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { 638 width += 10; 639 } 640 641 dom.setStyle( parent, 'width', width + 'px' ); 642 } 643 } 644 }); 645 } 646 }); 647 648 editor.on( 'pastePostProcess', function( event ) { 649 // Pasting in a caption node. 650 if ( editor.dom.getParent( editor.selection.getNode(), 'dd.wp-caption-dd' ) ) { 651 // Remove "non-block" elements that should not be in captions. 652 editor.$( 'img, audio, video, object, embed, iframe, script, style', event.node ).remove(); 653 654 editor.$( '*', event.node ).each( function( i, node ) { 655 if ( editor.dom.isBlock( node ) ) { 656 // Insert <br> where the blocks used to be. Makes it look better after pasting in the caption. 657 if ( tinymce.trim( node.textContent || node.innerText ) ) { 658 editor.dom.insertAfter( editor.dom.create( 'br' ), node ); 659 editor.dom.remove( node, true ); 660 } else { 661 editor.dom.remove( node ); 662 } 663 } 664 }); 665 666 // Trim <br> tags. 667 editor.$( 'br', event.node ).each( function( i, node ) { 668 if ( ! node.nextSibling || node.nextSibling.nodeName === 'BR' || 669 ! node.previousSibling || node.previousSibling.nodeName === 'BR' ) { 670 671 editor.dom.remove( node ); 672 } 673 } ); 674 675 // Pasted HTML is cleaned up for inserting in the caption. 676 pasteInCaption = true; 677 } 678 }); 679 680 editor.on( 'BeforeExecCommand', function( event ) { 681 var node, p, DL, align, replacement, captionParent, 682 cmd = event.command, 683 dom = editor.dom; 684 685 if ( cmd === 'mceInsertContent' || cmd === 'Indent' || cmd === 'Outdent' ) { 686 node = editor.selection.getNode(); 687 captionParent = dom.getParent( node, 'div.mceTemp' ); 688 689 if ( captionParent ) { 690 if ( cmd === 'mceInsertContent' ) { 691 if ( pasteInCaption ) { 692 pasteInCaption = false; 693 /* 694 * We are in the caption element, and in 'paste' context, 695 * and the pasted HTML was cleaned up on 'pastePostProcess' above. 696 * Let it be pasted in the caption. 697 */ 698 return; 699 } 700 701 /* 702 * The paste is somewhere else in the caption DL element. 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 */ 706 p = dom.create( 'p' ); 707 dom.insertAfter( p, captionParent ); 708 editor.selection.setCursorLocation( p, 0 ); 709 710 /* 711 * If the image is selected and the user pastes "over" it, 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 */ 715 if ( node.nodeName === 'IMG' ) { 716 editor.$( captionParent ).remove(); 717 } 718 719 editor.nodeChanged(); 720 } else { 721 // Clicking Indent or Outdent while an image with a caption is selected breaks the caption. 722 // See #38313. 723 event.preventDefault(); 724 event.stopImmediatePropagation(); 725 return false; 726 } 727 } 728 } else if ( cmd === 'JustifyLeft' || cmd === 'JustifyRight' || cmd === 'JustifyCenter' || cmd === 'wpAlignNone' ) { 729 node = editor.selection.getNode(); 730 align = 'align' + cmd.slice( 7 ).toLowerCase(); 731 DL = editor.dom.getParent( node, '.wp-caption' ); 732 733 if ( node.nodeName !== 'IMG' && ! DL ) { 734 return; 735 } 736 737 node = DL || node; 738 739 if ( editor.dom.hasClass( node, align ) ) { 740 replacement = ' alignnone'; 741 } else { 742 replacement = ' ' + align; 743 } 744 745 node.className = trim( node.className.replace( / ?align(left|center|right|none)/g, '' ) + replacement ); 746 747 editor.nodeChanged(); 748 event.preventDefault(); 749 750 if ( toolbar ) { 751 toolbar.reposition(); 752 } 753 754 editor.fire( 'ExecCommand', { 755 command: cmd, 756 ui: event.ui, 757 value: event.value 758 } ); 759 } 760 }); 761 762 editor.on( 'keydown', function( event ) { 763 var node, wrap, P, spacer, 764 selection = editor.selection, 765 keyCode = event.keyCode, 766 dom = editor.dom, 767 VK = tinymce.util.VK; 768 769 if ( keyCode === VK.ENTER ) { 770 // When pressing Enter inside a caption move the caret to a new parapraph under it. 771 node = selection.getNode(); 772 wrap = dom.getParent( node, 'div.mceTemp' ); 773 774 if ( wrap ) { 775 dom.events.cancel( event ); // Doesn't cancel all :( 776 777 // Remove any extra dt and dd cleated on pressing Enter... 778 tinymce.each( dom.select( 'dt, dd', wrap ), function( element ) { 779 if ( dom.isEmpty( element ) ) { 780 dom.remove( element ); 781 } 782 }); 783 784 spacer = tinymce.Env.ie && tinymce.Env.ie < 11 ? '' : '<br data-mce-bogus="1" />'; 785 P = dom.create( 'p', null, spacer ); 786 787 if ( node.nodeName === 'DD' ) { 788 dom.insertAfter( P, wrap ); 789 } else { 790 wrap.parentNode.insertBefore( P, wrap ); 791 } 792 793 editor.nodeChanged(); 794 selection.setCursorLocation( P, 0 ); 795 } 796 } else if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { 797 node = selection.getNode(); 798 799 if ( node.nodeName === 'DIV' && dom.hasClass( node, 'mceTemp' ) ) { 800 wrap = node; 801 } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) { 802 wrap = dom.getParent( node, 'div.mceTemp' ); 803 } 804 805 if ( wrap ) { 806 dom.events.cancel( event ); 807 removeImage( node ); 808 return false; 809 } 810 } 811 }); 812 813 /* 814 * After undo/redo FF seems to set the image height very slowly when it is set to 'auto' in the CSS. 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 */ 818 if ( tinymce.Env.gecko ) { 819 editor.on( 'undo redo', function() { 820 if ( editor.selection.getNode().nodeName === 'IMG' ) { 821 editor.selection.collapse(); 822 } 823 }); 824 } 825 826 editor.wpSetImgCaption = function( content ) { 827 return parseShortcode( content ); 828 }; 829 830 editor.wpGetImgCaption = function( content ) { 831 return getShortcode( content ); 832 }; 833 834 editor.on( 'beforeGetContent', function( event ) { 835 if ( event.format !== 'raw' ) { 836 editor.$( 'img[id="__wp-temp-img-id"]' ).removeAttr( 'id' ); 837 } 838 }); 839 840 editor.on( 'BeforeSetContent', function( event ) { 841 if ( event.format !== 'raw' ) { 842 event.content = editor.wpSetImgCaption( event.content ); 843 } 844 }); 845 846 editor.on( 'PostProcess', function( event ) { 847 if ( event.get ) { 848 event.content = editor.wpGetImgCaption( event.content ); 849 } 850 }); 851 852 ( function() { 853 var wrap; 854 855 editor.on( 'dragstart', function() { 856 var node = editor.selection.getNode(); 857 858 if ( node.nodeName === 'IMG' ) { 859 wrap = editor.dom.getParent( node, '.mceTemp' ); 860 861 if ( ! wrap && node.parentNode.nodeName === 'A' && ! hasTextContent( node.parentNode ) ) { 862 wrap = node.parentNode; 863 } 864 } 865 } ); 866 867 editor.on( 'drop', function( event ) { 868 var dom = editor.dom, 869 rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint( event.clientX, event.clientY, editor.getDoc() ); 870 871 // Don't allow anything to be dropped in a captioned image. 872 if ( rng && dom.getParent( rng.startContainer, '.mceTemp' ) ) { 873 event.preventDefault(); 874 } else if ( wrap ) { 875 event.preventDefault(); 876 877 editor.undoManager.transact( function() { 878 if ( rng ) { 879 editor.selection.setRng( rng ); 880 } 881 882 editor.selection.setNode( wrap ); 883 dom.remove( wrap ); 884 } ); 885 } 886 887 wrap = null; 888 } ); 889 } )(); 890 891 // Add to editor.wp. 892 editor.wp = editor.wp || {}; 893 editor.wp.isPlaceholder = isPlaceholder; 894 895 // Back-compat. 896 return { 897 _do_shcode: parseShortcode, 898 _get_shcode: getShortcode 899 }; 900 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Dec 25 01:00:02 2024 | Cross-referenced by PHPXref 0.7.1 |