Ticket #28595: 28595.5.patch
File 28595.5.patch, 26.0 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>' : '' ) + 45 52 '</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>', 53 '<p class="wpview-selection-after">\u00a0</p>', 53 54 function( self, editor, node ) { 54 55 $( self ).trigger( 'ready', [ editor, node ] ); 55 } 56 }, 57 'wrap' 56 58 ); 57 59 }, 58 60 unbind: function() {}, 59 setContent: function( html, callback, replace) {61 setContent: function( html, callback, option ) { 60 62 _.each( tinymce.editors, function( editor ) { 61 63 var self = this; 62 64 if ( editor.plugins.wpview ) { 63 65 $( editor.getBody() ) 64 66 .find( '[data-wpview-text="' + this.encodedText + '"]' ) 65 67 .each( function ( i, element ) { 66 var contentWrap = $( element ). children( '.wpview-content' ),68 var contentWrap = $( element ).find( '.wpview-content' ), 67 69 wrap = element; 68 70 69 if ( contentWrap.length ) {71 if ( contentWrap.length && option !== 'wrap' ) { 70 72 element = contentWrap = contentWrap[0]; 71 73 } 72 74 73 75 if ( _.isString( html ) ) { 74 if ( replace) {76 if ( option === 'replace' ) { 75 77 element = editor.dom.replace( editor.dom.createFragment( html ), wrap ); 76 78 } else { 77 79 editor.dom.setHTML( element, html ); 78 80 } 79 81 } else { 80 if ( replace) {82 if ( option === 'replace' ) { 81 83 element = editor.dom.replace( html, wrap ); 82 84 } else { 83 85 element.appendChild( html ); … … 85 87 } 86 88 87 89 if ( _.isFunction( callback ) ) { 88 callback( self, editor, $( element ). children( '.wpview-content' )[0] );90 callback( self, editor, $( element ).find( '.wpview-content' )[0] ); 89 91 } 90 92 } ); 91 93 } … … 267 269 tag: 'div', 268 270 269 271 attrs: { 270 'class': 'wpview-wrap wpview-type-' + viewType,272 'class': 'wpview-wrap', 271 273 'data-wpview-text': encodedText, 272 'data-wpview-type': viewType, 273 'contenteditable': 'false' 274 'data-wpview-type': viewType 274 275 }, 275 276 276 277 content: '\u00a0' … … 742 743 743 744 self.setError( response.message, 'admin-media' ); 744 745 } else { 745 self.setContent( '<p>' + self.original + '</p>', null, true);746 self.setContent( '<p>' + self.original + '</p>', null, 'replace' ); 746 747 } 747 748 } else if ( response && response.statusText ) { 748 749 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; … … 144 146 145 147 // Remove the content of view wrappers from HTML string 146 148 function emptyViews( content ) { 147 return content.replace(/ (<div[^>]+wpview-wrap[^>]+>)[\s\S]+?data-wpview-end[^>]*><\/ins><\/div>/g, '$1</div>' );149 return content.replace(/<div[^>]+data-wpview-text=\"([^"]+)"[^>]*>[\s\S]+?wpview-selection-after[^>]+>(?: |\u00a0)*<\/p><\/div>/g, '$1' ); 148 150 } 151 152 window.emptyViews = function() { 153 return emptyViews( editor.getContent({format : 'raw'}) ); 154 }; 149 155 150 156 // Prevent adding undo levels on changes inside a view wrapper 151 157 editor.on( 'BeforeAddUndo', function( event ) { … … 181 187 182 188 // When the editor's content has been updated and the DOM has been 183 189 // processed, render the views in the document. 184 editor.on( 'SetContent', function( event ) { 185 var body, padNode; 186 190 editor.on( 'SetContent', function() { 187 191 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 192 }); 203 193 204 // Detect mouse down events that are adjacent to a view when a view is the first view or the last view194 // Set the cursor before or after a view when clicking next to it. 205 195 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 } 196 var x = event.clientX, 197 y = event.clientY, 198 body = editor.getBody(), 199 bodyRect = body.getBoundingClientRect(), 200 first = body.firstChild, 201 firstRect = first.getBoundingClientRect(), 202 last = body.lastChild, 203 lastRect = last.getBoundingClientRect(), 204 view; 205 206 if ( y < firstRect.top && ( view = isView( first ) ) ) { 207 setViewCursor( true, view ); 208 event.preventDefault(); 209 } else if ( y > lastRect.bottom && ( view = isView( last ) ) ) { 210 setViewCursor( false, view ); 211 event.preventDefault(); 212 } else { 213 tinymce.each( editor.dom.select( '.wpview-wrap' ), function( view ) { 214 var rect = view.getBoundingClientRect(); 215 216 if ( y >= rect.top && y <= rect.bottom ) { 217 if ( x < bodyRect.left ) { 218 setViewCursor( true, view ); 219 event.preventDefault(); 220 } else if ( x > bodyRect.right ) { 221 setViewCursor( false, view ); 222 event.preventDefault(); 223 } 224 return; 225 } 226 }); 238 227 } 239 228 }); 240 229 241 230 editor.on( 'init', function() { 242 231 var selection = editor.selection; 232 243 233 // When a view is selected, ensure content that is being pasted 244 234 // or inserted is added to a text node (instead of the view). 245 235 editor.on( 'BeforeSetContent', function() { 246 236 var walker, target, 247 view = getParentView( selection.getNode() );237 view = isView( selection.getNode() ); 248 238 249 239 // If the selection is not within a view, bail. 250 240 if ( ! view ) { … … 267 257 selection.collapse( true ); 268 258 }); 269 259 270 // When the selection's content changes, scan any new content 271 // for matching views. 272 // 260 // When the selection's content changes, scan any new content for matching views. 273 261 // Runs on paste and on inserting nodes/html. 274 262 editor.on( 'SetContent', function( e ) { 275 263 if ( ! e.context ) { … … 286 274 }); 287 275 288 276 editor.dom.bind( editor.getBody().parentNode, 'mousedown mouseup click', function( event ) { 289 var view = getParentView( event.target ),277 var view = isView( event.target ), 290 278 deselectEventType; 291 279 292 280 // Contain clicks inside the view wrapper … … 294 282 event.stopPropagation(); 295 283 296 284 // 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 ) {285 if ( Env.ie <= 10 ) { 298 286 deselect(); 299 287 } 300 288 … … 307 295 editor.dom.remove( view ); 308 296 } 309 297 } 298 310 299 // Returning false stops the ugly bars from appearing in IE11 and stops the view being selected as a range in FF. 311 300 // Unfortunately, it also inhibits the dragging of views to a new location. 312 301 return false; 313 302 } else { 314 315 303 // Fix issue with deselecting a view in IE8. Without this hack, clicking content above the view wouldn't actually deselect it 316 304 // and the caret wouldn't be placed at the mouse location 317 if ( tinymce.Env.ie && tinymce.Env.ie <= 8 ) {305 if ( Env.ie && Env.ie <= 8 ) { 318 306 deselectEventType = 'mouseup'; 319 307 } else { 320 308 deselectEventType = 'mousedown'; … … 328 316 }); 329 317 330 318 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 319 // Replace the wpview node with the wpview string/shortcode? 343 tinymce.each( dom.select( 'div[data-wpview-text]', event.node ), function( node ) {320 tinymce.each( editor.dom.select( 'div[data-wpview-text]', event.node ), function( node ) { 344 321 // Empty the wrap node 345 322 if ( 'textContent' in node ) { 346 323 node.textContent = '\u00a0'; … … 361 338 } 362 339 }); 363 340 341 // (De)select views when arrow keys are used to navigate the content of the editor. 364 342 editor.on( 'keydown', function( event ) { 343 if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) { 344 return; 345 } 346 347 if ( selected ) { 348 return; 349 } 350 365 351 var keyCode = event.keyCode, 366 body = editor.getBody(), 367 view, padNode; 352 dom = editor.dom, 353 selection = editor.selection, 354 node = selection.getNode(), 355 view = isView( node ), 356 cursorBefore, cursorAfter; 357 358 lastKeyDownNode = node; 359 360 if ( ! view ) { 361 return; 362 } 363 364 if ( ! ( ( cursorBefore = dom.hasClass( view, 'wpview-selection-before' ) ) || 365 ( cursorAfter = dom.hasClass( view, 'wpview-selection-after' ) ) ) ) { 366 return; 367 } 368 369 if ( ( cursorAfter && keyCode === VK.UP ) || ( cursorBefore && keyCode === VK.BACKSPACE ) ) { 370 if ( view.previousSibling ) { 371 if ( isView( view.previousSibling ) ) { 372 setViewCursor( false, view.previousSibling ); 373 } else { 374 if ( dom.isEmpty( view.previousSibling ) && keyCode === VK.BACKSPACE ) { 375 dom.remove( view.previousSibling ); 376 } else { 377 selection.select( view.previousSibling, true ); 378 selection.collapse(); 379 } 380 } 381 } else { 382 handleEnter( view, true ); 383 } 384 event.preventDefault(); 385 } else if ( cursorAfter && ( keyCode === VK.DOWN || keyCode === VK.RIGHT ) ) { 386 if ( view.nextSibling ) { 387 if ( isView( view.nextSibling ) ) { 388 setViewCursor( false, view.nextSibling ); 389 } else { 390 selection.setCursorLocation( view.nextSibling, 0 ); 391 } 392 } else { 393 handleEnter( view ); 394 } 395 event.preventDefault(); 396 } else if ( cursorBefore && ( keyCode === VK.UP || keyCode === VK.LEFT ) ) { 397 if ( view.previousSibling ) { 398 if ( isView( view.previousSibling ) ) { 399 setViewCursor( true, view.previousSibling ); 400 } else { 401 selection.select( view.previousSibling, true ); 402 selection.collapse(); 403 } 404 } else { 405 handleEnter( view, true ); 406 } 407 event.preventDefault(); 408 } else if ( cursorBefore && keyCode === VK.DOWN ) { 409 if ( view.nextSibling ) { 410 if ( isView( view.nextSibling ) ) { 411 setViewCursor( true, view.nextSibling ); 412 } else { 413 selection.setCursorLocation( view.nextSibling, 0 ); 414 } 415 } else { 416 handleEnter( view ); 417 } 418 event.preventDefault(); 419 } else if ( ( cursorAfter && keyCode === VK.LEFT ) || ( cursorBefore && keyCode === VK.RIGHT ) ) { 420 select( view ); 421 event.preventDefault(); 422 event.stopImmediatePropagation(); 423 } else if ( cursorAfter && keyCode === VK.BACKSPACE ) { 424 dom.remove( view ); 425 event.preventDefault(); 426 } else if ( cursorAfter ) { 427 handleEnter( view ); 428 } else if ( cursorBefore ) { 429 handleEnter( view , true); 430 } 431 432 if ( keyCode === VK.ENTER ) { 433 event.preventDefault(); 434 } 435 }); 436 437 // Handle key presses for selected views. 438 editor.on( 'keydown', function( event ) { 439 var dom = editor.dom, 440 keyCode = event.keyCode, 441 selection = editor.selection, 442 view; 368 443 369 444 // If a view isn't selected, let the event go on its merry way. 370 445 if ( ! selected ) { 371 446 return; 372 447 } 373 448 374 // Let key presses that involve the command or control keys through.449 // Let key presses that involve the command or control keys through. 375 450 // Also, let any of the F# keys through. 376 451 if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) { 377 if ( ( event.metaKey || event.ctrlKey ) && keyCode === 88 ) { 378 toRemove = selected; 452 // But remove the view when cmd/ctrl + x/backspace are pressed. 453 if ( ( event.metaKey || event.ctrlKey ) && ( keyCode === 88 || keyCode === VK.BACKSPACE ) ) { 454 // We'll remove a cut view on keyup, otherwise the browser can't copy the content. 455 if ( keyCode === 88 ) { 456 toRemove = selected; 457 } else { 458 editor.dom.remove( selected ); 459 } 379 460 } 380 461 return; 381 462 } 382 463 383 view = getParentView( editor.selection.getNode() );464 view = isView( selection.getNode() ); 384 465 385 // If the caret is not within the selected view, deselect the 386 // view and bail. 466 // If the caret is not within the selected view, deselect the view and bail. 387 467 if ( view !== selected ) { 388 468 deselect(); 389 469 return; 390 470 } 391 471 392 // Deselect views with the arrow keys393 472 if ( keyCode === VK.LEFT || keyCode === VK.UP ) { 473 setViewCursor( true, view ); 394 474 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 475 } else if ( keyCode === VK.RIGHT || keyCode === VK.DOWN ) { 476 setViewCursor( false, view ); 409 477 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 } 478 } else if ( keyCode === VK.ENTER ) { 479 handleEnter( view ); 422 480 } else if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { 423 // If delete or backspace is pressed, delete the view. 424 editor.dom.remove( selected ); 481 dom.remove( selected ); 425 482 } 426 483 427 484 event.preventDefault(); 428 485 }); 429 486 430 // Select views when arrow keys are used to navigate the content of the editor.487 // Make sure we don't eat any content. 431 488 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 489 var selection = editor.selection, 490 node, range, view; 467 491 468 return;469 }492 if ( event.keyCode === VK.BACKSPACE ) { 493 node = selection.getNode(); 470 494 471 // In a child element 472 while ( container && container !== node && container !== body ) { 473 if ( container.nextSibling ) { 474 return; 475 } 476 container = container.parentNode; 495 if ( editor.dom.isEmpty( node ) ) { 496 if ( view = isView( node.previousSibling ) ) { 497 setViewCursor( false, view ); 498 editor.dom.remove( node ); 499 event.preventDefault(); 477 500 } 478 } 479 480 if ( selectSiblingView( node, 'next' ) ) { 501 } else if ( ( range = selection.getRng() ) && 502 range.startOffset === 0 && 503 range.endOffset === 0 && 504 ( view = isView( node.previousSibling ) ) ) { 505 setViewCursor( false, view ); 481 506 event.preventDefault(); 482 507 } 483 508 } 484 509 }); 485 510 486 editor.on( 'keyup', function( event ) { 487 var padNode, 488 keyCode = event.keyCode, 489 body = editor.getBody(), 490 range; 491 511 editor.on( 'keyup', function() { 492 512 if ( toRemove ) { 493 513 editor.dom.remove( toRemove ); 494 514 toRemove = false; 495 515 } 516 }); 496 517 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 ); 518 editor.on( 'nodechange', function( event ) { 519 var dom = editor.dom, 520 views = editor.dom.select( '.wpview-wrap' ), 521 className = event.element.className, 522 view = isView( event.element ), 523 lKDN = lastKeyDownNode; 524 525 lastKeyDownNode = false; 526 527 clearInterval( cursorInterval ); 528 529 dom.removeClass( views, 'wpview-selection-before' ); 530 dom.removeClass( views, 'wpview-selection-after' ); 531 dom.removeClass( views, 'wpview-cursor-hide' ); 502 532 503 if ( body.childNodes.length === 2 ) { 504 editor.selection.setCursorLocation( padNode, 0 ); 505 } 506 } 533 if ( view ) { 534 if ( className === 'wpview-selection-before' || className === 'wpview-selection-after' ) { 535 setViewCursorTries = 0; 507 536 508 range = editor.selection.getRng(); 537 // Make sure the cursor arrived in the right node. 538 // This is necessary for Firefox. 539 if ( lKDN === view.previousSibling ) { 540 setViewCursor( true, view ); 541 return; 542 } else if ( lKDN === view.nextSibling ) { 543 setViewCursor( false, view ); 544 return; 545 } 509 546 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 ) { 547 dom.addClass( view, className ); 513 548 514 editor.dom.remove( range.startContainer ); 549 cursorInterval = setInterval( function() { 550 if ( dom.hasClass( view, 'wpview-cursor-hide' ) ) { 551 dom.removeClass( view, 'wpview-cursor-hide' ); 552 } else { 553 dom.addClass( view, 'wpview-cursor-hide' ); 554 } 555 }, 500 ); 556 // If the cursor happens to be anywhere around the view, then set the cursor properly. 557 // Only try this once to prevent a loop. (You never know.) 558 } else if ( ! selected && ! setViewCursorTries ) { 559 setViewCursorTries++; 560 setViewCursor( true, view ); 515 561 } 516 562 } 517 563 }); -
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