Ticket #28595: 28595.4.patch
File 28595.4.patch, 25.7 KB (added by , 10 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' … … 742 746 743 747 self.setError( response.message, 'admin-media' ); 744 748 } else { 745 self.setContent( '<p>' + self.original + '</p>', null, true);749 self.setContent( '<p>' + self.original + '</p>', null, 'replace' ); 746 750 } 747 751 } else if ( response && response.statusText ) { 748 752 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, lastKeyDownNode, setViewCursorTries; 20 12 21 13 function isView( node ) { 22 return node && /\bwpview-wrap\b/.test( node.className ); 23 } 24 25 function createPadNode() { 26 return editor.dom.create( 'p', { 'data-wpview-pad': 1 }, 27 ( tinymce.Env.ie && tinymce.Env.ie < 11 ) ? '' : '<br data-mce-bogus="1" />' ); 14 return editor.dom.getParent( node, function( node ) { 15 return editor.dom.hasClass( node, 'wpview-wrap' ); 16 }); 28 17 } 29 18 30 19 /** 31 20 * Get the text/shortcode string for a view. 32 21 * 33 * @param view The view wrapper's HTML id ornode22 * @param view The view wrapper's node 34 23 * @returns string The text/shoercode string of the view 35 24 */ 36 25 function getViewText( view ) { 37 view = getParentView( typeof view === 'string' ? editor.dom.get( view ) : view ); 38 39 if ( view ) { 26 if ( view = isView( view ) ) { 40 27 return window.decodeURIComponent( editor.dom.getAttrib( view, 'data-wpview-text' ) || '' ); 41 28 } 29 42 30 return ''; 43 31 } 44 32 … … 49 37 * @param text The text string to be set 50 38 */ 51 39 function setViewText( view, text ) { 52 view = getParentView( typeof view === 'string' ? editor.dom.get( view ) :view );40 view = isView( view ); 53 41 54 42 if ( view ) { 55 43 editor.dom.setAttrib( view, 'data-wpview-text', window.encodeURIComponent( text || '' ) ); 56 44 return true; 57 45 } 46 58 47 return false; 59 48 } 60 49 … … 62 51 event.stopPropagation(); 63 52 } 64 53 54 function setViewCursor( before, view ) { 55 var location = before ? 'before' : 'after', 56 offset = before ? 0 : 1; 57 editor.selection.setCursorLocation( editor.dom.select( '.wpview-selection-' + location, view )[0], offset ); 58 editor.nodeChanged(); 59 } 60 61 function handleEnter( view, before ) { 62 var dom = editor.dom, 63 padNode; 64 65 if ( ! before && view.nextSibling && dom.isEmpty( view.nextSibling ) && view.nextSibling.nodeName === 'P' ) { 66 padNode = view.nextSibling; 67 } else if ( before && view.previousSibling && dom.isEmpty( view.previousSibling ) && view.previousSibling.nodeName === 'P' ) { 68 padNode = view.previousSibling; 69 } else { 70 padNode = dom.create( 'p' ); 71 72 if ( ! ( Env.ie && Env.ie < 11 ) ) { 73 padNode.innerHTML = '<br data-mce-bogus="1">'; 74 } 75 76 if ( before ) { 77 view.parentNode.insertBefore( padNode, view ); 78 } else { 79 dom.insertAfter( padNode, view ); 80 } 81 } 82 83 deselect(); 84 editor.getBody().focus(); 85 editor.selection.setCursorLocation( padNode, 0 ); 86 editor.nodeChanged(); 87 } 88 65 89 function select( viewNode ) { 66 90 var clipboard, 67 91 dom = editor.dom; … … 80 104 'contenteditable': 'true' 81 105 }, getViewText( viewNode ) ); 82 106 83 // Prepend inside the wrapper 84 viewNode.insertBefore( clipboard, viewNode.firstChild ); 107 editor.dom.select( '.wpview-body', viewNode )[0].appendChild( clipboard ); 85 108 86 109 // Both of the following are necessary to prevent manipulating the selection/focus 87 110 dom.bind( clipboard, 'beforedeactivate focusin focusout', _stop ); … … 116 139 selected = null; 117 140 } 118 141 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 142 // Check if the `wp.mce` API exists. 141 143 if ( typeof wp === 'undefined' || ! wp.mce ) { 142 144 return; … … 181 183 182 184 // When the editor's content has been updated and the DOM has been 183 185 // processed, render the views in the document. 184 editor.on( 'SetContent', function( event ) { 185 var body, padNode; 186 186 editor.on( 'SetContent', function() { 187 187 wp.mce.views.render(); 188 189 // Add padding <p> if the noneditable node is last190 if ( event.load || ! event.set ) {191 body = editor.getBody();192 193 if ( isView( body.lastChild ) ) {194 padNode = createPadNode();195 body.appendChild( padNode );196 197 if ( ! event.initial ) {198 editor.selection.setCursorLocation( padNode, 0 );199 }200 }201 }202 188 }); 203 189 204 // Detect mouse down events that are adjacent to a view when a view is the first view or the last view190 // Set the cursor before or after a view when clicking next to it. 205 191 editor.on( 'click', function( event ) { 206 var body = editor.getBody(), 207 doc = editor.getDoc(), 208 scrollTop = doc.documentElement.scrollTop || body.scrollTop || 0, 209 x, y, firstNode, lastNode, padNode; 210 211 if ( event.target.nodeName === 'HTML' && ! event.metaKey && ! event.ctrlKey ) { 212 firstNode = body.firstChild; 213 lastNode = body.lastChild; 214 x = event.clientX; 215 y = event.clientY; 216 217 // Detect clicks above or to the left if the first node is a wpview 218 if ( isView( firstNode ) && ( ( x < firstNode.offsetLeft && y < ( firstNode.offsetHeight - scrollTop ) ) || 219 y < firstNode.offsetTop ) ) { 220 221 padNode = createPadNode(); 222 body.insertBefore( padNode, firstNode ); 223 224 // Detect clicks to the right and below the last view 225 } else if ( isView( lastNode ) && ( x > ( lastNode.offsetLeft + lastNode.offsetWidth ) || 226 ( ( scrollTop + y ) - ( lastNode.offsetTop + lastNode.offsetHeight ) ) > 0 ) ) { 227 228 padNode = createPadNode(); 229 body.appendChild( padNode ); 230 } 231 232 if ( padNode ) { 233 // Make sure that a selected view is deselected so that focus and selection are handled properly 234 deselect(); 235 editor.getBody().focus(); 236 editor.selection.setCursorLocation( padNode, 0 ); 237 } 192 var x = event.clientX, 193 y = event.clientY, 194 body = editor.getBody(), 195 bodyRect = body.getBoundingClientRect(), 196 first = body.firstChild, 197 firstRect = first.getBoundingClientRect(), 198 last = body.lastChild, 199 lastRect = last.getBoundingClientRect(), 200 view; 201 202 if ( y < firstRect.top && ( view = isView( first ) ) ) { 203 setViewCursor( true, view ); 204 event.preventDefault(); 205 } else if ( y > lastRect.bottom && ( view = isView( last ) ) ) { 206 setViewCursor( false, view ); 207 event.preventDefault(); 208 } else { 209 tinymce.each( editor.dom.select( '.wpview-wrap' ), function( view ) { 210 var rect = view.getBoundingClientRect(); 211 212 if ( y >= rect.top && y <= rect.bottom ) { 213 if ( x < bodyRect.left ) { 214 setViewCursor( true, view ); 215 event.preventDefault(); 216 } else if ( x > bodyRect.right ) { 217 setViewCursor( false, view ); 218 event.preventDefault(); 219 } 220 return; 221 } 222 }); 238 223 } 239 224 }); 240 225 241 226 editor.on( 'init', function() { 242 227 var selection = editor.selection; 228 243 229 // When a view is selected, ensure content that is being pasted 244 230 // or inserted is added to a text node (instead of the view). 245 231 editor.on( 'BeforeSetContent', function() { 246 232 var walker, target, 247 view = getParentView( selection.getNode() );233 view = isView( selection.getNode() ); 248 234 249 235 // If the selection is not within a view, bail. 250 236 if ( ! view ) { … … 267 253 selection.collapse( true ); 268 254 }); 269 255 270 // When the selection's content changes, scan any new content 271 // for matching views. 272 // 256 // When the selection's content changes, scan any new content for matching views. 273 257 // Runs on paste and on inserting nodes/html. 274 258 editor.on( 'SetContent', function( e ) { 275 259 if ( ! e.context ) { … … 286 270 }); 287 271 288 272 editor.dom.bind( editor.getBody().parentNode, 'mousedown mouseup click', function( event ) { 289 var view = getParentView( event.target ),273 var view = isView( event.target ), 290 274 deselectEventType; 291 275 292 276 // Contain clicks inside the view wrapper … … 294 278 event.stopPropagation(); 295 279 296 280 // 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 ) {281 if ( Env.ie <= 10 ) { 298 282 deselect(); 299 283 } 300 284 … … 307 291 editor.dom.remove( view ); 308 292 } 309 293 } 294 310 295 // Returning false stops the ugly bars from appearing in IE11 and stops the view being selected as a range in FF. 311 296 // Unfortunately, it also inhibits the dragging of views to a new location. 312 297 return false; 313 298 } else { 314 315 299 // Fix issue with deselecting a view in IE8. Without this hack, clicking content above the view wouldn't actually deselect it 316 300 // and the caret wouldn't be placed at the mouse location 317 if ( tinymce.Env.ie && tinymce.Env.ie <= 8 ) {301 if ( Env.ie && Env.ie <= 8 ) { 318 302 deselectEventType = 'mouseup'; 319 303 } else { 320 304 deselectEventType = 'mousedown'; … … 328 312 }); 329 313 330 314 editor.on( 'PreProcess', function( event ) { 331 var dom = editor.dom;332 333 // Remove empty padding nodes334 tinymce.each( dom.select( 'p[data-wpview-pad]', event.node ), function( node ) {335 if ( dom.isEmpty( node ) ) {336 dom.remove( node );337 } else {338 dom.setAttrib( node, 'data-wpview-pad', null );339 }340 });341 342 315 // Replace the wpview node with the wpview string/shortcode? 343 tinymce.each( dom.select( 'div[data-wpview-text]', event.node ), function( node ) {316 tinymce.each( editor.dom.select( 'div[data-wpview-text]', event.node ), function( node ) { 344 317 // Empty the wrap node 345 318 if ( 'textContent' in node ) { 346 319 node.textContent = '\u00a0'; … … 361 334 } 362 335 }); 363 336 337 // (De)select views when arrow keys are used to navigate the content of the editor. 364 338 editor.on( 'keydown', function( event ) { 339 if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) { 340 return; 341 } 342 343 if ( selected ) { 344 return; 345 } 346 365 347 var keyCode = event.keyCode, 366 body = editor.getBody(), 367 view, padNode; 348 dom = editor.dom, 349 selection = editor.selection, 350 node = selection.getNode(), 351 view = isView( node ), 352 cursorBefore, cursorAfter; 353 354 lastKeyDownNode = node; 355 356 if ( ! view ) { 357 return; 358 } 359 360 if ( ! ( ( cursorBefore = dom.hasClass( view, 'wpview-selection-before' ) ) || 361 ( cursorAfter = dom.hasClass( view, 'wpview-selection-after' ) ) ) ) { 362 return; 363 } 364 365 if ( ( cursorAfter && keyCode === VK.UP ) || ( cursorBefore && keyCode === VK.BACKSPACE ) ) { 366 if ( view.previousSibling ) { 367 if ( isView( view.previousSibling ) ) { 368 setViewCursor( false, view.previousSibling ); 369 } else { 370 if ( dom.isEmpty( view.previousSibling ) && keyCode === VK.BACKSPACE ) { 371 dom.remove( view.previousSibling ); 372 } else { 373 selection.select( view.previousSibling, true ); 374 selection.collapse(); 375 } 376 } 377 } else { 378 handleEnter( view, true ); 379 } 380 event.preventDefault(); 381 } else if ( cursorAfter && ( keyCode === VK.DOWN || keyCode === VK.RIGHT ) ) { 382 if ( view.nextSibling ) { 383 if ( isView( view.nextSibling ) ) { 384 setViewCursor( false, view.nextSibling ); 385 } else { 386 selection.setCursorLocation( view.nextSibling, 0 ); 387 } 388 } else { 389 handleEnter( view ); 390 } 391 event.preventDefault(); 392 } else if ( cursorBefore && ( keyCode === VK.UP || keyCode === VK.LEFT ) ) { 393 if ( view.previousSibling ) { 394 if ( isView( view.previousSibling ) ) { 395 setViewCursor( true, view.previousSibling ); 396 } else { 397 selection.select( view.previousSibling, true ); 398 selection.collapse(); 399 } 400 } else { 401 handleEnter( view, true ); 402 } 403 event.preventDefault(); 404 } else if ( cursorBefore && keyCode === VK.DOWN ) { 405 if ( view.nextSibling ) { 406 if ( isView( view.nextSibling ) ) { 407 setViewCursor( true, view.nextSibling ); 408 } else { 409 selection.setCursorLocation( view.nextSibling, 0 ); 410 } 411 } else { 412 handleEnter( view ); 413 } 414 event.preventDefault(); 415 } else if ( ( cursorAfter && keyCode === VK.LEFT ) || ( cursorBefore && keyCode === VK.RIGHT ) ) { 416 select( view ); 417 event.preventDefault(); 418 event.stopImmediatePropagation(); 419 } else if ( cursorAfter && keyCode === VK.BACKSPACE ) { 420 dom.remove( view ); 421 event.preventDefault(); 422 } else if ( cursorAfter ) { 423 handleEnter( view ); 424 } else if ( cursorBefore ) { 425 handleEnter( view , true); 426 } 427 428 if ( keyCode === VK.ENTER ) { 429 event.preventDefault(); 430 } 431 }); 432 433 // Handle key presses for selected views. 434 editor.on( 'keydown', function( event ) { 435 var dom = editor.dom, 436 keyCode = event.keyCode, 437 selection = editor.selection, 438 view; 368 439 369 440 // If a view isn't selected, let the event go on its merry way. 370 441 if ( ! selected ) { 371 442 return; 372 443 } 373 444 374 // Let key presses that involve the command or control keys through.445 // Let key presses that involve the command or control keys through. 375 446 // Also, let any of the F# keys through. 376 447 if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) { 377 if ( ( event.metaKey || event.ctrlKey ) && keyCode === 88 ) { 378 toRemove = selected; 448 // But remove the view when cmd/ctrl + x/backspace are pressed. 449 if ( ( event.metaKey || event.ctrlKey ) && ( keyCode === 88 || keyCode === VK.BACKSPACE ) ) { 450 // We'll remove a cut view on keyup, otherwise the browser can't copy the content. 451 if ( keyCode === 88 ) { 452 toRemove = selected; 453 } else { 454 editor.dom.remove( selected ); 455 } 379 456 } 380 457 return; 381 458 } 382 459 383 view = getParentView( editor.selection.getNode() );460 view = isView( selection.getNode() ); 384 461 385 // If the caret is not within the selected view, deselect the 386 // view and bail. 462 // If the caret is not within the selected view, deselect the view and bail. 387 463 if ( view !== selected ) { 388 464 deselect(); 389 465 return; 390 466 } 391 467 392 // Deselect views with the arrow keys393 468 if ( keyCode === VK.LEFT || keyCode === VK.UP ) { 469 setViewCursor( true, view ); 394 470 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 471 } else if ( keyCode === VK.RIGHT || keyCode === VK.DOWN ) { 472 setViewCursor( false, view ); 409 473 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 } 474 } else if ( keyCode === VK.ENTER ) { 475 handleEnter( view ); 422 476 } else if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { 423 // If delete or backspace is pressed, delete the view. 424 editor.dom.remove( selected ); 477 dom.remove( selected ); 425 478 } 426 479 427 480 event.preventDefault(); 428 481 }); 429 482 430 // Select views when arrow keys are used to navigate the content of the editor.483 // Make sure we don't eat any content. 431 484 editor.on( 'keydown', function( event ) { 432 var keyCode = event.keyCode, 433 dom = editor.dom, 434 range = editor.selection.getRng(), 435 startNode = range.startContainer, 436 body = editor.getBody(), 437 node, container; 438 439 if ( ! startNode || startNode === body || event.metaKey || event.ctrlKey ) { 440 return; 441 } 442 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 } 448 449 if ( ! ( node = dom.getParent( startNode, dom.isBlock ) ) ) { 450 return; 451 } 452 453 if ( selectSiblingView( node, 'previous' ) ) { 454 event.preventDefault(); 455 } 456 } else if ( keyCode === VK.DOWN || keyCode === VK.RIGHT ) { 457 if ( ! ( node = dom.getParent( startNode, dom.isBlock ) ) ) { 458 return; 459 } 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 485 var selection = editor.selection, 486 node, range, view; 467 487 468 return;469 }488 if ( event.keyCode === VK.BACKSPACE ) { 489 node = selection.getNode(); 470 490 471 // In a child element 472 while ( container && container !== node && container !== body ) { 473 if ( container.nextSibling ) { 474 return; 475 } 476 container = container.parentNode; 491 if ( editor.dom.isEmpty( node ) ) { 492 if ( view = isView( node.previousSibling ) ) { 493 setViewCursor( false, view ); 494 editor.dom.remove( node ); 495 event.preventDefault(); 477 496 } 478 } 479 480 if ( selectSiblingView( node, 'next' ) ) { 497 } else if ( ( range = selection.getRng() ) && 498 range.startOffset === 0 && 499 range.endOffset === 0 && 500 ( view = isView( node.previousSibling ) ) ) { 501 setViewCursor( false, view ); 481 502 event.preventDefault(); 482 503 } 483 504 } 484 505 }); 485 506 486 editor.on( 'keyup', function( event ) { 487 var padNode, 488 keyCode = event.keyCode, 489 body = editor.getBody(), 490 range; 491 507 editor.on( 'keyup', function() { 492 508 if ( toRemove ) { 493 509 editor.dom.remove( toRemove ); 494 510 toRemove = false; 495 511 } 512 }); 496 513 497 if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { 498 // Make sure there is padding if the last element is a view 499 if ( isView( body.lastChild ) ) { 500 padNode = createPadNode(); 501 body.appendChild( padNode ); 514 editor.on( 'nodechange', function( event ) { 515 var dom = editor.dom, 516 views = editor.dom.select( '.wpview-wrap' ), 517 className = event.element.className, 518 view = isView( event.element ), 519 lKDN = lastKeyDownNode; 520 521 lastKeyDownNode = false; 522 523 clearInterval( cursorInterval ); 524 525 dom.removeClass( views, 'wpview-selection-before' ); 526 dom.removeClass( views, 'wpview-selection-after' ); 527 dom.removeClass( views, 'wpview-cursor-hide' ); 502 528 503 if ( body.childNodes.length === 2 ) { 504 editor.selection.setCursorLocation( padNode, 0 ); 505 } 506 } 529 if ( view ) { 530 if ( className === 'wpview-selection-before' || className === 'wpview-selection-after' ) { 531 setViewCursorTries = 0; 507 532 508 range = editor.selection.getRng(); 533 // Make sure the cursor arrived in the right node. 534 // This is necessary for Firefox. 535 if ( lKDN === view.previousSibling ) { 536 setViewCursor( true, view ); 537 return; 538 } else if ( lKDN === view.nextSibling ) { 539 setViewCursor( false, view ); 540 return; 541 } 509 542 510 // Allow an initial element in the document to be removed when it is before a view 511 if ( body.firstChild === range.startContainer && range.collapsed === true && 512 isView( range.startContainer.nextSibling ) && range.startOffset === 0 ) { 543 dom.addClass( view, className ); 513 544 514 editor.dom.remove( range.startContainer ); 545 cursorInterval = setInterval( function() { 546 if ( dom.hasClass( view, 'wpview-cursor-hide' ) ) { 547 dom.removeClass( view, 'wpview-cursor-hide' ); 548 } else { 549 dom.addClass( view, 'wpview-cursor-hide' ); 550 } 551 }, 500 ); 552 // If the cursor happens to be anywhere around the view, then set the cursor properly. 553 // Only try this once to prevent a loop. (You never know.) 554 } else if ( ! selected && ! setViewCursorTries ) { 555 setViewCursorTries++; 556 setViewCursor( true, view ); 515 557 } 516 558 } 517 559 }); -
src/wp-includes/js/tinymce/skins/wordpress/wp-content.css
210 210 } 211 211 212 212 /* hide the shortcode content, but allow the content to still be selected */ 213 .wpview-wrap .wpview-clipboard { 213 .wpview-wrap .wpview-clipboard, 214 .wpview-wrap > p { 214 215 position: absolute; 215 216 top: 0; 216 217 left: 0; … … 225 226 height: 1px; 226 227 } 227 228 229 /* An ugly box will appear when this is focussed in IE, so we'll move it outside the window. */ 230 .wpview-wrap.wpview-selection-before > p, 231 .wpview-wrap.wpview-selection-after > p { 232 left: -10000px; 233 } 234 228 235 .wpview-wrap .wpview-clipboard, 229 .wpview-wrap .wpview-clipboard * { 236 .wpview-wrap .wpview-clipboard *, 237 .wpview-wrap > p { 230 238 -moz-user-select: text; 231 239 -webkit-user-select: text; 232 240 -ms-user-select: text; 233 241 user-select: text; 234 242 } 235 243 244 .wpview-wrap.wpview-selection-before:before, 245 .wpview-wrap.wpview-selection-after:before { 246 content: ''; 247 margin: 0; 248 padding: 0; 249 position: absolute; 250 top: -2px; 251 left: -3px; 252 bottom: -2px; 253 width: 1px; 254 background-color: black; 255 -webkit-transition: opacity 0.15s ease-out; 256 -moz-transition: opacity 0.15s ease-out; 257 transition: opacity 0.15s ease-out; 258 opacity: 1; 259 } 260 261 .wpview-wrap.wpview-selection-after:before { 262 left: auto; 263 right: -3px; 264 } 265 266 .wpview-wrap.wpview-cursor-hide:before { 267 opacity: 0; 268 } 269 236 270 /** 237 271 * Media previews 238 272 */ … … 348 382 } 349 383 350 384 /* Audio player is short; therefore let's put the toolbar above */ 351 .wpview- type-audio.toolbar {385 .wpview-wrap[data-wpview-type="audio"] .toolbar { 352 386 top: auto; 353 387 bottom: -34px; 354 388 } 355 389 356 .wpview- type-audio.toolbar div {390 .wpview-wrap[data-wpview-type="audio"] .toolbar div { 357 391 margin-top: 0; 358 392 } 359 393 360 .wpview- type-audio.toolbar div:first-child {394 .wpview-wrap[data-wpview-type="audio"] .toolbar div:first-child { 361 395 margin-left: 0; 362 396 } 363 397