Ticket #3309: 3309_3.patch
File 3309_3.patch, 9.3 KB (added by , 15 years ago) |
---|
-
_source/plugins/styles/plugin.js
510 510 */ 511 511 range.enlarge( CKEDITOR.ENLARGE_ELEMENT ); 512 512 513 var bookmark = range.createBookmark( true),514 startNode = range.document.getById( bookmark.startNode );513 var bookmark = range.createBookmark(), 514 startNode = bookmark.startNode; 515 515 516 516 if ( range.collapsed ) 517 517 { 518 /* 519 * If the range is collapsed, try to remove the style from all ancestor 520 * elements, until a block boundary is reached. 521 */ 522 var startPath = new CKEDITOR.dom.elementPath( startNode.getParent() ); 523 for ( var i = 0, element ; i < startPath.elements.length && ( element = startPath.elements[i] ) ; i++ ) 524 { 518 519 var startPath = new CKEDITOR.dom.elementPath( startNode.getParent() ), 520 // The topmost element in elementspatch which we should jump out of. 521 boundaryElement; 522 523 524 for ( var i = 0, element ; i < startPath.elements.length 525 && ( element = startPath.elements[i] ) ; i++ ) 526 { 527 /* 528 * 1. If it's collaped inside text nodes, try to remove the style from the whole element. 529 * 530 * 2. Otherwise if it's collapsed on element boundaries, moving the selection 531 * outside the styles instead of removing the whole tag, 532 * also make sure other inner styles were well preserverd.(#3309) 533 */ 525 534 if ( element == startPath.block || element == startPath.blockLimit ) 526 535 break; 527 536 528 537 if ( this.checkElementRemovable( element ) ) 529 538 { 530 /* 531 * Before removing the style node, there may be a sibling to the style node 532 * that's exactly the same to the one to be removed. To the user, it makes 533 * no difference that they're separate entities in the DOM tree. So, merge 534 * them before removal. 535 */ 536 mergeSiblings( element ); 537 removeFromElement( this, element ); 538 } 539 } 540 } 541 else 539 var endOfElement = range.checkBoundaryOfElement( element, CKEDITOR.END ), 540 startOfElement = !endOfElement && range.checkBoundaryOfElement( element, CKEDITOR.START ); 541 if ( startOfElement || endOfElement ) 542 { 543 boundaryElement = element; 544 boundaryElement.match = startOfElement ? 'start' : 'end'; 545 } 546 else 547 { 548 /* 549 * Before removing the style node, there may be a sibling to the style node 550 * that's exactly the same to the one to be removed. To the user, it makes 551 * no difference that they're separate entities in the DOM tree. So, merge 552 * them before removal. 553 */ 554 mergeSiblings( element ); 555 removeFromElement( this, element ); 556 557 } 558 } 559 } 560 561 // Re-create the style tree after/before the boundary element, 562 // the replication start from bookmark start node to define the 563 // new range. 564 if ( boundaryElement ) 565 { 566 var clonedElement = startNode; 567 for ( var i = 0 ;; i++ ) 568 { 569 var newElement = startPath.elements[ i ]; 570 if ( newElement.equals( boundaryElement ) ) 571 break; 572 // Avoid copying any removable style element. 573 else if( newElement.match ) 574 continue; 575 else 576 newElement = newElement.clone(); 577 newElement.append( clonedElement ); 578 clonedElement = newElement; 579 } 580 clonedElement[ boundaryElement.match == 'start' ? 581 'insertBefore' : 'insertAfter' ]( boundaryElement ); 582 } 583 } 584 else 542 585 { 543 586 /* 544 587 * Now our range isn't collapsed. Lets walk from the start node to the end -
_source/core/dom/range.js
296 296 }; 297 297 } 298 298 299 // Evaluator for CKEDITOR.dom.element::checkBoundaryOfElement, reject any 300 // text node and non-empty elements unless it's being bookmark text. 301 function elementBoundaryEval( node ) 302 { 303 // Reject any text node unless it's being bookmark. 304 return node.type != CKEDITOR.NODE_TEXT 305 && node.getName() in CKEDITOR.dtd.$removeEmpty 306 || node.getParent().hasAttribute( '_fck_bookmark' ); 307 }; 308 299 309 CKEDITOR.dom.range.prototype = 300 310 { 301 311 clone : function() … … 1467 1477 }; 1468 1478 }, 1469 1479 1480 /** 1481 * Check whether current range is on the inner edge of the specified element. 1482 * @param {Number} checkType ( CKEDITOR.START | CKEDITOR.END ) The checking side. 1483 * @param {CKEDITOR.dom.element} element The target element to check. 1484 */ 1485 checkBoundaryOfElement : function( element, checkType ) 1486 { 1487 var walkerRange = this.clone(); 1488 // Expand the range to element boundary. 1489 walkerRange[ checkType == CKEDITOR.START ? 1490 'setStartAt' : 'setEndAt' ] 1491 ( element, checkType == CKEDITOR.START ? 1492 CKEDITOR.POSITION_AFTER_START 1493 : CKEDITOR.POSITION_BEFORE_END ); 1494 1495 var walker = new CKEDITOR.dom.walker( walkerRange ), 1496 retval = false; 1497 walker.evaluator = elementBoundaryEval; 1498 return walker[ checkType == CKEDITOR.START ? 1499 'checkBackward' : 'checkForward' ](); 1500 }, 1470 1501 // Calls to this function may produce changes to the DOM. The range may 1471 1502 // be updated to reflect such changes. 1472 1503 checkStartOfBlock : function() … … 1593 1624 CKEDITOR.ENLARGE_ELEMENT = 1; 1594 1625 CKEDITOR.ENLARGE_BLOCK_CONTENTS = 2; 1595 1626 CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS = 3; 1627 1628 /** 1629 * Check boundary types. 1630 * @see CKEDITOR.dom.range::checkBoundaryOfElement 1631 */ 1632 CKEDITOR.START = 1; 1633 CKEDITOR.END = 2; 1634 CKEDITOR.STARTEND = 3; -
_source/tests/plugins/styles/styles.html
336 336 337 337 test_ticket_2040 : function() 338 338 { 339 doc.getById( '_P1' ).setHtml( 'This is some <strong>sample text<\/strong>. You are using <a href="http://www.fckeditor.net/"> CKEditor<\/a>.' );339 doc.getById( '_P1' ).setHtml( 'This is some <strong>sample text<\/strong>. You are using <a href="http://www.fckeditor.net/">ckeditor<\/a>.' ); 340 340 341 341 var range = new CKEDITOR.dom.range( doc ); 342 342 range.setStart( doc.getById( '_P1' ), 1 ); … … 345 345 var style = new CKEDITOR.style( { element : 'i' } ); 346 346 style.applyToRange( range ); 347 347 348 assert.areSame( 'this is some <strong><i>sample</i> text<\/strong>. you are using <a href="http://www.fckeditor.net/"> CKEditor<\/a>.', getInnerHtml( '_P1' ) );348 assert.areSame( 'this is some <strong><i>sample</i> text<\/strong>. you are using <a href="http://www.fckeditor.net/">ckeditor<\/a>.', getInnerHtml( '_P1' ) ); 349 349 }, 350 350 351 351 test_checkElementRemovable1 : function() … … 486 486 assert.areSame( '<p><i title="z">text</i></p><i title="z">outter</i>', getInnerHtml( element ) ); 487 487 }, 488 488 489 // Remove inline style when range collapsed at element boundaries, 490 // move out of the removing-style element, with inner style copied. 491 test_ticket_3309 : function() 492 { 493 var element = doc.getById( '_P1' ); 494 element.setHtml( 'this is some <b><i id="_i1">styles</i></b> text' ); 495 496 // This is some <b><i>styles^</i></b> text 497 var range = new CKEDITOR.dom.range( doc ); 498 range.setStartAt( doc.getById( '_i1' ), CKEDITOR.POSITION_BEFORE_END ); 499 500 var style = new CKEDITOR.style( { element : 'b' } ); 501 style.removeFromRange( range ); 502 503 assert.areSame( 'this is some <b><i id="_i1">styles</i></b><i></i> text', getInnerHtml( element ) ); 504 }, 505 506 // No inner style preserved, simply move out of the removing-style element. 507 test_ticket_3309_2 : function() 508 { 509 var element = doc.getById( '_P1' ); 510 element.setHtml( 'this is some <b id="_b1">styles</b> text' ); 511 512 // This is some <b>styles^</b> text 513 var range = new CKEDITOR.dom.range( doc ); 514 range.setStartAt( doc.getById( '_b1' ), CKEDITOR.POSITION_BEFORE_END ); 515 516 var style = new CKEDITOR.style( { element : 'b' } ); 517 style.removeFromRange( range ); 518 // This is some <b>styles</b>^ text 519 assert.areSame( doc.getById( '_b1' ).getParent().$, range.startContainer.$ ); 520 assert.areSame( 2, range.startOffset ); 521 assert.areSame( 'this is some <b id="_b1">styles</b> text', getInnerHtml( element ) ); 522 }, 523 524 // With style overrides. 525 test_ticket_3309_3 : function() 526 { 527 var element = doc.getById( '_P1' ); 528 element.setHtml( 'text <strong><bold><span><b><i id="_i1">styles</i></b></span></bold></strong>' ); 529 530 // text <strong><bold><span><b><i id="_i1">^styles</i></b></span></bold></strong> 531 var range = new CKEDITOR.dom.range( doc ); 532 range.setStartAt( doc.getById( '_i1' ), CKEDITOR.POSITION_AFTER_START ); 533 534 var style = new CKEDITOR.style( { element : 'b' , overrides : [ 'strong', 'bold' ] } ); 535 style.removeFromRange( range ); 536 537 // text <span><i>^</i></span><bold><span><b><i>styles</i></b></span></bold> 538 assert.areSame( 'text <span><i></i></span><strong><bold><span><b><i id="_i1">styles</i></b></span></bold></strong>', getInnerHtml( element ) ); 539 }, 489 540 name : document.title 490 541 }; 491 542 })() ); 492 543 //window.onload = testCase.test_ticket_3309_3; 493 544 //]]> 494 545 </script> 495 546 </head>