Ticket #28595: 28595.patch
File 28595.patch, 18.5 KB (added by , 11 years ago) |
---|
-
src/wp-includes/js/mce-view.js
39 39 getHtml: function() {}, 40 40 render: function() { 41 41 this.setContent( 42 '<div class="toolbar">' + 43 ( _.isFunction( views[ this.type ].edit ) ? '<div class="dashicons dashicons-edit edit"></div>' : '' ) + 44 '<div class="dashicons dashicons-no-alt remove"></div>' + 42 '<p class="wpview-selection-before">\u00a0</p>' + 43 '<div class="wpview-body" contenteditable="false">' + 44 '<div class="toolbar">' + 45 ( _.isFunction( views[ this.type ].edit ) ? '<div class="dashicons dashicons-edit edit"></div>' : '' ) + 46 '<div class="dashicons dashicons-no-alt remove"></div>' + 47 '</div>' + 48 '<div class="wpview-content wpview-type-' + this.type + '">' + 49 this.getHtml() + 50 '</div>' + 51 ( this.overlay ? '<div class="wpview-overlay"></div>' : '' ) + 52 // The <ins> is used to mark the end of the wrapper div (has to be the last child node). 53 // Needed when comparing the content as string for preventing extra undo levels. 54 '<ins data-wpview-end="1"></ins>' + 45 55 '</div>' + 46 '<div class="wpview-content">' + 47 this.getHtml() + 48 '</div>' + 49 ( this.overlay ? '<div class="wpview-overlay"></div>' : '' ) + 50 // The <ins> is used to mark the end of the wrapper div (has to be the last child node). 51 // Needed when comparing the content as string for preventing extra undo levels. 52 '<ins data-wpview-end="1"></ins>', 56 '<p class="wpview-selection-after">\u00a0</p>', 53 57 function( self, editor, node ) { 54 58 $( self ).trigger( 'ready', [ editor, node ] ); 55 } 59 }, 60 'wrap' 56 61 ); 57 62 }, 58 63 unbind: function() {}, 59 setContent: function( html, callback, replace) {64 setContent: function( html, callback, option ) { 60 65 _.each( tinymce.editors, function( editor ) { 61 66 var self = this; 62 67 if ( editor.plugins.wpview ) { 63 68 $( editor.getBody() ) 64 69 .find( '[data-wpview-text="' + this.encodedText + '"]' ) 65 70 .each( function ( i, element ) { 66 var contentWrap = $( element ). children( '.wpview-content' ),71 var contentWrap = $( element ).find( '.wpview-content' ), 67 72 wrap = element; 68 73 69 if ( contentWrap.length ) {74 if ( contentWrap.length && option !== 'wrap' ) { 70 75 element = contentWrap = contentWrap[0]; 71 76 } 72 77 73 78 if ( _.isString( html ) ) { 74 if ( replace) {79 if ( option === 'replace' ) { 75 80 element = editor.dom.replace( editor.dom.createFragment( html ), wrap ); 76 81 } else { 77 82 editor.dom.setHTML( element, html ); 78 83 } 79 84 } else { 80 if ( replace) {85 if ( option === 'replace' ) { 81 86 element = editor.dom.replace( html, wrap ); 82 87 } else { 83 88 element.appendChild( html ); … … 85 90 } 86 91 87 92 if ( _.isFunction( callback ) ) { 88 callback( self, editor, $( element ). children( '.wpview-content' )[0] );93 callback( self, editor, $( element ).find( '.wpview-content' )[0] ); 89 94 } 90 95 } ); 91 96 } … … 267 272 tag: 'div', 268 273 269 274 attrs: { 270 'class': 'wpview-wrap wpview-type-' + viewType,275 'class': 'wpview-wrap', 271 276 'data-wpview-text': encodedText, 272 'data-wpview-type': viewType, 273 'contenteditable': 'false' 277 'data-wpview-type': viewType 274 278 }, 275 279 276 280 content: '\u00a0' … … 736 740 if ( self.type === 'embed' ) { 737 741 self.setError( response.message, 'admin-media' ); 738 742 } else { 739 self.setContent( '<p>' + self.original + '</p>', null, true);743 self.setContent( '<p>' + self.original + '</p>', null, 'replace' ); 740 744 } 741 745 } else if ( response && response.statusText ) { 742 746 self.setError( response.statusText, 'admin-media' ); -
src/wp-includes/js/tinymce/plugins/wpview/plugin.js
4 4 */ 5 5 tinymce.PluginManager.add( 'wpview', function( editor ) { 6 6 var selected, 7 Env = tinymce.Env, 7 8 VK = tinymce.util.VK, 8 9 TreeWalker = tinymce.dom.TreeWalker, 9 toRemove = false; 10 11 function getParentView( node ) { 12 while ( node && node.nodeName !== 'BODY' ) { 13 if ( isView( node ) ) { 14 return node; 15 } 16 17 node = node.parentNode; 18 } 19 } 10 toRemove = false, 11 cursorInterval; 20 12 21 13 function isView( node ) { 22 return node && /\bwpview-wrap\b/.test( node.className ); 14 return editor.dom.getParent( node, function( node ) { 15 return editor.dom.hasClass( node, 'wpview-wrap' ); 16 } ); 23 17 } 24 18 25 19 function createPadNode() { 26 20 return editor.dom.create( 'p', { 'data-wpview-pad': 1 }, 27 ( tinymce.Env.ie && tinymce.Env.ie < 11 ) ? '' : '<br data-mce-bogus="1" />' );21 ( Env.ie && Env.ie < 11 ) ? '' : '<br data-mce-bogus="1" />' ); 28 22 } 29 23 30 24 /** 31 25 * Get the text/shortcode string for a view. 32 26 * 33 * @param view The view wrapper's HTML id ornode27 * @param view The view wrapper's node 34 28 * @returns string The text/shoercode string of the view 35 29 */ 36 30 function getViewText( view ) { 37 view = getParentView( typeof view === 'string' ? editor.dom.get( view ) :view );31 view = isView( view ); 38 32 39 33 if ( view ) { 40 34 return window.decodeURIComponent( editor.dom.getAttrib( view, 'data-wpview-text' ) || '' ); … … 49 43 * @param text The text string to be set 50 44 */ 51 45 function setViewText( view, text ) { 52 view = getParentView( typeof view === 'string' ? editor.dom.get( view ) :view );46 view = isView( view ); 53 47 54 48 if ( view ) { 55 49 editor.dom.setAttrib( view, 'data-wpview-text', window.encodeURIComponent( text || '' ) ); … … 61 55 function _stop( event ) { 62 56 event.stopPropagation(); 63 57 } 58 59 function setViewCursor( before, view ) { 60 var location = before ? 'before' : 'after', 61 offset = before ? 0 : 1; 62 editor.selection.setCursorLocation( editor.dom.select( '.wpview-selection-' + location, view )[0], offset ); 63 } 64 65 function handleEnter( view, before ) { 66 var dom = editor.dom, 67 padNode; 68 69 if ( ! before && view.nextSibling && dom.isEmpty( view.nextSibling ) && view.nextSibling.nodeName === 'P' ) { 70 padNode = view.nextSibling; 71 } else if ( before && view.previousSibling && dom.isEmpty( view.previousSibling ) && view.previousSibling.nodeName === 'P' ) { 72 padNode = view.previousSibling; 73 } else { 74 padNode = dom.create( 'p' ); 75 76 if ( ! ( Env.ie && Env.ie < 11 ) ) { 77 padNode.innerHTML = '<br data-mce-bogus="1">'; 78 } 79 80 if ( before ) { 81 view.parentNode.insertBefore( padNode, view ); 82 } else { 83 dom.insertAfter( padNode, view ); 84 } 85 } 86 87 deselect(); 88 editor.selection.setCursorLocation( padNode, 0 ); 89 editor.nodeChanged(); 90 } 64 91 65 92 function select( viewNode ) { 66 93 var clipboard, … … 116 143 selected = null; 117 144 } 118 145 119 function selectSiblingView( node, direction ) {120 var body = editor.getBody(),121 sibling = direction === 'previous' ? 'previousSibling' : 'nextSibling';122 123 while ( node && node.parentNode !== body ) {124 if ( node[sibling] ) {125 // The caret will be in another element126 return false;127 }128 129 node = node.parentNode;130 }131 132 if ( isView( node[sibling] ) ) {133 select( node[sibling] );134 return true;135 }136 137 return false;138 }139 140 146 // Check if the `wp.mce` API exists. 141 147 if ( typeof wp === 'undefined' || ! wp.mce ) { 142 148 return; … … 244 250 // or inserted is added to a text node (instead of the view). 245 251 editor.on( 'BeforeSetContent', function() { 246 252 var walker, target, 247 view = getParentView( selection.getNode() );253 view = isView( selection.getNode() ); 248 254 249 255 // If the selection is not within a view, bail. 250 256 if ( ! view ) { … … 286 292 }); 287 293 288 294 editor.dom.bind( editor.getBody().parentNode, 'mousedown mouseup click', function( event ) { 289 var view = getParentView( event.target ),295 var view = isView( event.target ), 290 296 deselectEventType; 291 297 292 298 // Contain clicks inside the view wrapper … … 294 300 event.stopPropagation(); 295 301 296 302 // Hack to try and keep the block resize handles from appearing. They will show on mousedown and then be removed on mouseup. 297 if ( tinymce.Env.ie <= 10 ) {303 if ( Env.ie <= 10 ) { 298 304 deselect(); 299 305 } 300 306 … … 314 320 315 321 // Fix issue with deselecting a view in IE8. Without this hack, clicking content above the view wouldn't actually deselect it 316 322 // and the caret wouldn't be placed at the mouse location 317 if ( tinymce.Env.ie && tinymce.Env.ie <= 8 ) {323 if ( Env.ie && Env.ie <= 8 ) { 318 324 deselectEventType = 'mouseup'; 319 325 } else { 320 326 deselectEventType = 'mousedown'; … … 361 367 } 362 368 }); 363 369 370 371 // Handle key presses for selected views. 364 372 editor.on( 'keydown', function( event ) { 365 var keyCode = event.keyCode, 366 body = editor.getBody(), 367 view, padNode; 373 var dom = editor.dom, 374 keyCode = event.keyCode, 375 selection = editor.selection, 376 view; 368 377 369 378 // If a view isn't selected, let the event go on its merry way. 370 379 if ( ! selected ) { 371 380 return; 372 381 } 373 382 374 // Let key presses that involve the command or control keys through.383 // Let key presses that involve the command or control keys through. 375 384 // Also, let any of the F# keys through. 376 385 if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) { 377 if ( ( event.metaKey || event.ctrlKey ) && keyCode === 88 ) { 378 toRemove = selected; 386 // But remove the view when cmd/ctrl + x/backspace are pressed. 387 if ( ( event.metaKey || event.ctrlKey ) && ( keyCode === 88 || keyCode === VK.BACKSPACE ) ) { 388 // We'll remove a cut view on keyup, otherwise the browser can't copy the content. 389 if ( keyCode === 88 ) { 390 toRemove = selected; 391 } else { 392 editor.dom.remove( selected ); 393 } 379 394 } 380 395 return; 381 396 } 382 397 383 view = getParentView( editor.selection.getNode() );398 view = isView( selection.getNode() ); 384 399 385 // If the caret is not within the selected view, deselect the 386 // view and bail. 400 // If the caret is not within the selected view, deselect the view and bail. 387 401 if ( view !== selected ) { 388 402 deselect(); 389 403 return; 390 404 } 391 405 392 // Deselect views with the arrow keys393 406 if ( keyCode === VK.LEFT || keyCode === VK.UP ) { 407 setViewCursor( true, view ); 394 408 deselect(); 395 // Handle case where two views are stacked on top of one another396 if ( isView( view.previousSibling ) ) {397 select( view.previousSibling );398 // Handle case where view is the first node399 } else if ( ! view.previousSibling ) {400 padNode = createPadNode();401 body.insertBefore( padNode, body.firstChild );402 editor.selection.setCursorLocation( body.firstChild, 0 );403 // Handle default case404 } else {405 editor.selection.select( view.previousSibling, true );406 editor.selection.collapse();407 }408 409 } else if ( keyCode === VK.RIGHT || keyCode === VK.DOWN ) { 410 setViewCursor( false, view ); 409 411 deselect(); 410 // Handle case where the next node is another wpview 411 if ( isView( view.nextSibling ) ) { 412 select( view.nextSibling ); 413 // Handle case were the view is that last node 414 } else if ( ! view.nextSibling ) { 415 padNode = createPadNode(); 416 body.appendChild( padNode ); 417 editor.selection.setCursorLocation( body.lastChild, 0 ); 418 // Handle default case where the next node is a non-wpview 419 } else { 420 editor.selection.setCursorLocation( view.nextSibling, 0 ); 421 } 412 } else if ( keyCode === VK.ENTER ) { 413 handleEnter( view ); 422 414 } else if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { 423 // If delete or backspace is pressed, delete the view. 424 editor.dom.remove( selected ); 415 dom.remove( selected ); 425 416 } 426 417 427 418 event.preventDefault(); 428 419 }); 429 420 430 // Select views when arrow keys are used to navigate the content of the editor.421 // (De)select views when arrow keys are used to navigate the content of the editor. 431 422 editor.on( 'keydown', function( event ) { 432 423 var keyCode = event.keyCode, 433 424 dom = editor.dom, 434 range = editor.selection.getRng(), 435 startNode = range.startContainer, 436 body = editor.getBody(), 437 node, container; 425 selection = editor.selection, 426 node = selection.getNode(), 427 cursorBefore = ( node.className === 'wpview-selection-before' ), 428 cursorAfter = ( node.className === 'wpview-selection-after' ), 429 view; 438 430 439 if ( ! startNode || startNode === body || event.metaKey || event.ctrlKey) {431 if ( ! cursorBefore && ! cursorAfter ) { 440 432 return; 441 433 } 442 434 443 if ( keyCode === VK.UP || keyCode === VK.LEFT ) { 444 if ( keyCode === VK.LEFT && ( ! range.collapsed || range.startOffset !== 0 ) ) { 445 // Not at the beginning of the current range 446 return; 447 } 435 if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) { 436 return; 437 } 448 438 449 if ( ! ( node = dom.getParent( startNode, dom.isBlock )) ) {450 451 439 if ( ( cursorBefore && keyCode === VK.LEFT ) || ( cursorAfter && keyCode === VK.RIGHT ) ) { 440 return; 441 } 452 442 453 if ( selectSiblingView( node, 'previous' ) ) { 454 event.preventDefault(); 443 view = isView( node ); 444 445 if ( ( cursorAfter && keyCode === VK.UP ) || ( cursorBefore && keyCode === VK.BACKSPACE ) ) { 446 if ( view.previousSibling ) { 447 if ( isView( view.previousSibling ) ) { 448 setViewCursor( false, view.previousSibling ); 449 } else { 450 selection.select( view.previousSibling, true ); 451 selection.collapse(); 452 } 453 } else { 454 handleEnter( view, true ); 455 455 } 456 } else if ( keyCode === VK.DOWN || keyCode === VK.RIGHT ) { 457 if ( ! ( node = dom.getParent( startNode, dom.isBlock ) ) ) { 458 return; 456 event.preventDefault(); 457 } else if ( cursorAfter && keyCode === VK.DOWN ) { 458 if ( view.nextSibling ) { 459 if ( isView( view.nextSibling ) ) { 460 setViewCursor( false, view.nextSibling ); 461 } else { 462 return; 463 } 464 } else { 465 handleEnter( view ); 459 466 } 460 461 if ( keyCode === VK.RIGHT ) { 462 container = range.endContainer; 463 464 if ( ! range.collapsed || ( range.startOffset === 0 && container.length ) || 465 container.nextSibling || 466 ( container.nodeType === 3 && range.startOffset !== container.length ) ) { // Not at the end of the current range 467 467 event.preventDefault(); 468 } else if ( cursorBefore && keyCode === VK.UP ) { 469 if ( view.previousSibling ) { 470 if ( isView( view.previousSibling ) ) { 471 setViewCursor( true, view.previousSibling ); 472 } else { 468 473 return; 469 474 } 470 471 // In a child element 472 while ( container && container !== node && container !== body ) { 473 if ( container.nextSibling ) { 474 return; 475 } 476 container = container.parentNode; 475 } else { 476 handleEnter( view, true ); 477 } 478 event.preventDefault(); 479 } else if ( cursorBefore && keyCode === VK.DOWN ) { 480 if ( view.previousSibling ) { 481 if ( isView( view.nextSibling ) ) { 482 setViewCursor( true, view.nextSibling ); 483 } else { 484 selection.setCursorLocation( view.nextSibling, 0 ); 477 485 } 486 } else { 487 handleEnter( view ); 478 488 } 489 event.preventDefault(); 490 } else if ( ( cursorAfter && keyCode === VK.LEFT ) || ( cursorBefore && keyCode === VK.RIGHT ) ) { 491 select( view ); 492 event.preventDefault(); 493 } else if ( cursorAfter && keyCode === VK.BACKSPACE ) { 494 dom.remove( view ); 495 event.preventDefault(); 496 } else if ( cursorAfter ) { 497 handleEnter( view ); 498 } else if ( cursorBefore ) { 499 handleEnter( view , true); 500 } 501 502 if ( keyCode === VK.ENTER ) { 503 event.preventDefault(); 504 } 505 }); 479 506 480 if ( selectSiblingView( node, 'next' ) ) { 507 // Make sure we don't eat any content. 508 editor.on( 'keydown', function( event ) { 509 var selection = editor.selection, 510 range, view; 511 512 if ( event.keyCode === VK.BACKSPACE ) { 513 if ( ( range = selection.getRng() ) && 514 range.startOffset === 0 && 515 range.endOffset === 0 && 516 selection.isCollapsed() && 517 ( view = isView( selection.getNode().previousSibling ) ) && 518 ! editor.dom.isEmpty( selection.getNode() ) ) { 519 setViewCursor( false, view ); 481 520 event.preventDefault(); 482 521 } 483 522 } … … 515 554 } 516 555 } 517 556 }); 557 558 editor.on( 'nodechange', function( event ) { 559 var dom = editor.dom, 560 views = editor.dom.select( '.wpview-wrap' ), 561 className = event.element.className, 562 view = event.element.parentNode; 563 564 clearInterval( cursorInterval ); 565 566 dom.removeClass( views, 'wpview-selection-before' ); 567 dom.removeClass( views, 'wpview-selection-after' ); 568 dom.removeClass( views, 'wpview-cursor-hide' ); 569 570 if ( ! selected && className === 'wpview-selection-before' || className === 'wpview-selection-after' ) { 571 dom.addClass( view, className ); 572 573 cursorInterval = setInterval( function() { 574 if ( dom.hasClass( view, 'wpview-cursor-hide' ) ) { 575 dom.removeClass( view, 'wpview-cursor-hide' ); 576 } else { 577 dom.addClass( view, 'wpview-cursor-hide' ); 578 } 579 }, 500 ); 580 } 581 } ); 518 582 519 583 return { 520 584 getViewText: getViewText, -
src/wp-includes/js/tinymce/skins/wordpress/wp-content.css
203 203 } 204 204 205 205 /* hide the shortcode content, but allow the content to still be selected */ 206 .wpview-wrap .wpview-clipboard { 206 .wpview-wrap .wpview-clipboard, 207 .wpview-wrap > p { 207 208 position: absolute; 208 209 top: 0; 209 210 left: 0; … … 219 220 } 220 221 221 222 .wpview-wrap .wpview-clipboard, 222 .wpview-wrap .wpview-clipboard * { 223 .wpview-wrap .wpview-clipboard *, 224 .wpview-wrap > p { 223 225 -moz-user-select: text; 224 226 -webkit-user-select: text; 225 227 -ms-user-select: text; 226 228 user-select: text; 227 229 } 228 230 231 .wpview-wrap.wpview-selection-before:before, 232 .wpview-wrap.wpview-selection-after:before { 233 content: ''; 234 margin: 0; 235 padding: 0; 236 position: absolute; 237 top: -2px; 238 left: -3px; 239 bottom: -2px; 240 width: 1px; 241 background-color: black; 242 -webkit-transition: opacity 0.15s ease-out; 243 -moz-transition: opacity 0.15s ease-out; 244 transition: opacity 0.15s ease-out; 245 opacity: 1; 246 } 247 248 .wpview-wrap.wpview-selection-after:before { 249 left: auto; 250 right: -3px; 251 } 252 253 .wpview-wrap.wpview-cursor-hide:before { 254 opacity: 0; 255 } 256 229 257 /** 230 258 * Media previews 231 259 */ … … 341 369 } 342 370 343 371 /* Audio player is short; therefore let's put the toolbar above */ 344 .wpview- type-audio.toolbar {372 .wpview-wrap[data-wpview-type="audio"] .toolbar { 345 373 top: auto; 346 374 bottom: -34px; 347 375 } 348 376 349 .wpview- type-audio.toolbar div {377 .wpview-wrap[data-wpview-type="audio"] .toolbar div { 350 378 margin-top: 0; 351 379 } 352 380 353 .wpview- type-audio.toolbar div:first-child {381 .wpview-wrap[data-wpview-type="audio"] .toolbar div:first-child { 354 382 margin-left: 0; 355 383 } 356 384