Ticket #5309: 5309_2.patch
File 5309_2.patch, 15.9 KB (added by , 14 years ago) |
---|
-
_source/core/htmlparser/element.js
235 235 // Send children. 236 236 CKEDITOR.htmlParser.fragment.prototype.writeChildrenHtml.apply( this, arguments ); 237 237 238 }, 239 240 toDom : function( doc ) 241 { 242 var element = new CKEDITOR.dom.element( this.name, doc ); 243 element.setAttributes( this.attributes ); 244 245 for ( var i = 0, count = this.children.length; i < count; i++ ) 246 element.append( this.children[ i ].toDom() ); 247 248 return element; 238 249 } 239 250 }; 240 251 })(); -
_source/plugins/wysiwygarea/plugin.js
24 24 this.fire( 'saveSnapshot' ); 25 25 26 26 var selection = this.getSelection(), 27 ranges = selection.getRanges(), 27 28 data = evt.data; 28 29 29 30 if ( this.dataProcessor ) 30 31 data = this.dataProcessor.toHtml( data ); 31 32 32 if ( CKEDITOR.env.ie ) 33 // Process the ranges counter-clockwise to avoid any impacts among them. 34 for ( var i = ranges.length - 1 ; i >= 0 ; i-- ) 33 35 { 34 var selIsLocked = selection.isLocked; 36 var range = ranges[ i ], 37 previousRange = ranges[ i - 1 ]; 35 38 36 if ( selIsLocked ) 37 selection.unlock(); 38 39 var $sel = selection.getNative(); 40 if ( $sel.type == 'Control' ) 41 $sel.clear(); 42 $sel.createRange().pasteHTML( data ); 43 44 if ( selIsLocked ) 45 this.getSelection().lock(); 46 } 47 else 48 this.document.$.execCommand( 'inserthtml', false, data ); 39 // Merge next sibling range when possible, it's not a common case, 40 // but necessary for situation like multiple table cell selection in Firefox. 41 if ( previousRange ) 42 { 43 if ( previousRange.endContainer.equals( range.startContainer ) 44 && previousRange.endOffset == range.startOffset ) 45 { 46 previousRange.endContainer = range.endContainer; 47 previousRange.endOffset = range.endOffset; 48 ranges.splice( i, 1 ); 49 continue; 50 } 51 } 52 53 // Pasting should only goes into the first range. 54 if ( i == 0 ) 55 range.pasteHtml( data ); 56 else 57 { 58 range.enlarge( CKEDITOR.NODE_ELEMENT ); 59 // Drop the range after selected content is deleted. 60 range.extractContents(); 61 ranges.length--; 62 } 63 } 49 64 65 selection.selectRanges( ranges ); 66 !CKEDITOR.env.ie && selection.scrollIntoView(); 67 50 68 CKEDITOR.tools.setTimeout( function() 51 69 { 52 70 this.fire( 'saveSnapshot' ); … … 696 714 docType = fullPage && editor.docType, 697 715 doc = iframe.getFrameDocument(); 698 716 717 // BR at the end of document is mozilla editor bogus node (#5293). 718 if ( CKEDITOR.env.gecko ) 719 { 720 var last = doc.getBody().getLast(); 721 if ( last.type == CKEDITOR.NODE_ELEMENT && last.is( 'br' ) ) 722 last.remove(); 723 } 724 699 725 var data = fullPage 700 726 ? doc.getDocumentElement().getOuterHtml() 701 727 : doc.getBody().getHtml(); -
_source/core/htmlparser/comment.js
56 56 } 57 57 58 58 writer.comment( comment ); 59 }, 60 61 toDom : function( doc ) 62 { 63 return new CKEDITOR.dom.comment( this.value, doc ); 59 64 } 65 60 66 }; -
_source/core/dom/element.js
254 254 }, 255 255 256 256 /** 257 * Retrieve block element's filler node if existed. 258 */ 259 getBogus : function() 260 { 261 if ( !this.isBlockBoundary() ) 262 return; 263 264 var lastChild = this.getLast() ; 265 266 // Ignore empty/spaces text. 267 while ( lastChild && lastChild.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.rtrim( lastChild.getText() ) ) 268 lastChild = lastChild.getPrevious(); 269 270 if ( lastChild && 271 ( CKEDITOR.env.ie && lastChild.type == CKEDITOR.NODE_TEXT && CKEDITOR.tools.trim( lastChild.getText() ).match( /^(?: |\xa0)$/ ) 272 || CKEDITOR.env.gecko && CKEDITOR.env.webkit && lastChild.is( 'br' ) ) ) 273 { 274 return lastChild; 275 } 276 }, 277 278 /** 257 279 * Breaks one of the ancestor element in the element position, moving 258 280 * this element between the broken parts. 259 281 * @param {CKEDITOR.dom.element} parent The anscestor element to get broken. -
_source/core/htmlparser/text.js
50 50 return; 51 51 52 52 writer.text( text ); 53 }, 54 55 toDom : function( doc ) 56 { 57 // DON'T use CKEDITOR.dom.text, it's not handling entities.. 58 return CKEDITOR.dom.element.createFromHtml( this.value ); 53 59 } 54 60 }; 55 61 })(); -
_source/core/htmlparser/cdata.js
38 38 writeHtml : function( writer ) 39 39 { 40 40 writer.write( this.value ); 41 }, 42 43 toDom : function( doc ) 44 { 45 return new CKEDITOR.dom.text( this.value, doc ); 41 46 } 42 47 }; 43 48 })(); -
_source/core/dom/range.js
317 317 return !whitespaceEval( node ) && !bookmarkEval( node ); 318 318 } 319 319 320 321 function collectValidChildren( element, parentName ) 322 { 323 var next = element, isValid, 324 candidates = []; 325 326 while ( ( next = next.getNextSourceNode( isValid, CKEDITOR.NODE_ELEMENT ) ) ) 327 { 328 if ( isValid = next.getName() in CKEDITOR.dtd[ parentName ] ) 329 candidates.push( next ); 330 } 331 332 var fragment = new CKEDITOR.dom.documentFragment(); 333 for ( var i = 0, count = candidates.length; i < count; i++ ) 334 fragment.append( candidates[ i ] ); 335 return fragment; 336 } 337 338 var emptyspaces = CKEDITOR.dom.walker.whitespaces(), 339 bookmarks = CKEDITOR.dom.walker.bookmark(); 340 320 341 CKEDITOR.dom.range.prototype = 321 342 { 322 343 clone : function() … … 666 687 }, 667 688 668 689 /** 690 * Paste the give HTML at the start of the range, and replacing any previously selected nodes within the range. 691 * Note: This method might alter the specified HTML to make it fit the given range context. For example, when pasting a table 692 * when actually a table row is selected (a DTD violation), instead of inserting the table, it results in only the cells of the table 693 * getting pasted into the row. 694 * For predictable results, paste only well-formed HTML that fits at the given range context. 695 * @param {String} html Valid HTML string to paste into the range. 696 */ 697 pasteHtml : function ( html ) 698 { 699 // Create a fragment of nodes without context that represents the structure to paste. (W3C Spec) 700 var fragment = CKEDITOR.htmlParser.fragment.fromHtml( html, false ), 701 nodeList = fragment.children; 702 703 if ( !this.collapsed ) 704 { 705 // Avoid resulting in dummy element after the deletion. 706 this.enlarge( CKEDITOR.NODE_ELEMENT ); 707 this.extractContents(); 708 } 709 710 // Split up text node at position. 711 if ( this.startContainer.type == CKEDITOR.NODE_TEXT ) 712 this.trim(); 713 714 // If we're at end of block, remove any bogus node, because: 715 // 1. It's not needed any more due to the incomming content. 716 // 2. It will get doubled (visible) if the to be inserted node is a bogus too. 717 var block; 718 if ( block = this.checkEndOfBlock() ) 719 { 720 var bogus = block.getBogus(); 721 bogus && bogus.remove(); 722 } 723 724 var targetNode, targetNodeOffset, targetName, path, 725 updateTarget = CKEDITOR.tools.bind( function () 726 { 727 targetNode = this.startContainer, 728 targetNodeOffset = this.startOffset, 729 targetName = targetNode.getName(); 730 path = new CKEDITOR.dom.elementPath( targetNode ); 731 }, this ); 732 733 updateTarget(); 734 for ( var i = 0, count = nodeList.length; i < count; i++ ) 735 { 736 var node = nodeList[ i ], 737 nodeName = node.type == CKEDITOR.NODE_ELEMENT ? node.name : '#'; 738 739 // If we have a list to insert, and we're actually standing inside a list item, 740 // insert the appropriate children instead, in short, merge the lists 741 // instead of pasting in a sublist. 742 if ( nodeName in CKEDITOR.dtd.$list 743 && path.block && path.block.getName() in CKEDITOR.dtd.$listItem ) 744 { 745 this.splitElement( path.block ); 746 updateTarget(); 747 } 748 749 // The to be inserted node fails DTD examination. 750 while ( !CKEDITOR.dtd[ targetName ][ nodeName ] ) 751 { 752 // If the failure is caused by pasting table/list inside a table/list 753 // structure, instead of splitting up the element, just insert the 754 // appropriate children. 755 if ( nodeName == 'table' && targetName in { table:1, tfoot:1,thead:1,tbody:1,tr:1 } 756 || nodeName in { ul:1, ol:1, dl:1 } && targetName in { ul:1, ol:1, dl:1 } ) 757 { 758 node = collectValidChildren( node.toDom(), targetName ); 759 break; 760 } 761 762 // Users are more likely want to paste content intead of the element, e.g. pre, headings. 763 if ( ( targetName == nodeName ) 764 && ( nodeName == 'pre' || nodeName.match( /^h[1-6]$/ ) ) ) 765 { 766 Array.prototype.splice.apply( nodeList, [ i, 1 ].concat( node.children ) ); 767 node = nodeList[ i ]; 768 break; 769 } 770 771 // Ok, it's our last option, split up or move out of current element. 772 this.splitElement( targetNode ); 773 updateTarget(); 774 } 775 776 var domNode = node.$ ? node : node.toDom(); 777 // Where to put the cursor? 778 var anchorNode = domNode.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ? 779 domNode.getLast() : domNode; 780 781 this.insertNode( domNode ); 782 783 // Try to place cursor at the end of the last pasted node, 784 // with the only exception that we should discontinue any pasted link, 785 // If that fails, simply collapse by the end of last node. 786 if ( !( i == count - 1 && anchorNode.type == CKEDITOR.NODE_ELEMENT 787 && !anchorNode.is( 'a' ) && this.moveToElementEditEnd( anchorNode ) ) ) 788 this.collapse(); 789 } 790 }, 791 792 /** 669 793 * Transforms the startContainer and endContainer properties from text 670 794 * nodes to element nodes, whenever possible. This is actually possible 671 795 * if either of the boundary containers point to a text node, and its … … 866 990 { 867 991 // If we reached the common ancestor, mark the flag 868 992 // for it. 869 if ( !commonReached && enlargeable.equals( commonAncestor) )993 if ( !commonReached && ( enlargeable.contains( commonAncestor ) || enlargeable.equals( commonAncestor ) ) ) 870 994 commonReached = true; 871 995 872 996 if ( !body.contains( enlargeable ) ) … … 1036 1160 { 1037 1161 if ( enlargeable && !sibling ) 1038 1162 { 1039 if ( !commonReached && enlargeable.equals( commonAncestor) )1163 if ( !commonReached && ( enlargeable.contains( commonAncestor ) || enlargeable.equals( commonAncestor ) ) ) 1040 1164 commonReached = true; 1041 1165 1042 1166 if ( !body.contains( enlargeable ) ) … … 1341 1465 var startContainer = this.startContainer; 1342 1466 var startOffset = this.startOffset; 1343 1467 1468 var count = node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ? 1469 node.getChildren().count() : 1; 1470 1471 var anchorNode = node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ? 1472 node.getChildren().getItem( 0 ) : node; 1473 1344 1474 var nextNode = startContainer.getChild( startOffset ); 1345 1475 1346 1476 if ( nextNode ) … … 1349 1479 startContainer.append( node ); 1350 1480 1351 1481 // Check if we need to update the end boundary. 1352 if ( node.getParent().equals( this.endContainer ) )1353 this.endOffset ++;1482 if ( startContainer.equals( this.endContainer ) ) 1483 this.endOffset += count; 1354 1484 1355 1485 // Expand the range to embrace the new node. 1356 this.setStartBefore( node );1486 this.setStartBefore( anchorNode ); 1357 1487 }, 1358 1488 1359 1489 moveToPosition : function( node, position ) … … 1608 1738 if ( !this.collapsed ) 1609 1739 return null; 1610 1740 1741 var testRange = this.clone(), 1742 walker = new CKEDITOR.dom.walker( testRange ); 1743 1744 walker.evaluator = function( node ) 1745 { 1746 return !!( emptyspaces( node ) || bookmarks( node ) ); 1747 }; 1748 1749 testRange.setEndAfter( toSplit ); 1750 if ( walker.checkForward() ) 1751 { 1752 this.moveToPosition( toSplit, CKEDITOR.POSITION_AFTER_END ); 1753 return null; 1754 } 1755 else 1756 { 1757 testRange = this.clone(); 1758 testRange.setStartBefore( toSplit ); 1759 walker.reset(); 1760 walker.range = testRange; 1761 if ( walker.checkBackward() ) 1762 { 1763 this.moveToPosition( toSplit, CKEDITOR.POSITION_BEFORE_START ); 1764 return null; 1765 } 1766 } 1767 1611 1768 // Extract the contents of the block from the selection point to the end 1612 1769 // of its contents. 1613 1770 this.setEndAt( toSplit, CKEDITOR.POSITION_BEFORE_END ); … … 1706 1863 // Creates a range starting at the block start until the range start. 1707 1864 var walkerRange = this.clone(); 1708 1865 walkerRange.collapse( false ); 1709 walkerRange.setEndAt( path.block || path.blockLimit, CKEDITOR.POSITION_BEFORE_END ); 1866 var pathBlock = path.block || path.blockLimit; 1867 walkerRange.setEndAt( pathBlock, CKEDITOR.POSITION_BEFORE_END ); 1710 1868 1711 1869 var walker = new CKEDITOR.dom.walker( walkerRange ); 1712 1870 walker.evaluator = getCheckStartEndBlockEvalFunction( false ); 1713 1871 1714 return walker.checkForward() ;1872 return walker.checkForward() && pathBlock; 1715 1873 }, 1716 1874 1717 1875 /** -
_source/core/htmlparser/fragment.js
491 491 { 492 492 for ( var i = 0 ; i < this.children.length ; i++ ) 493 493 this.children[i].writeHtml( writer, filter ); 494 }, 495 496 toDom : function( doc ) 497 { 498 var fragment = new CKEDITOR.dom.documentFragment( doc ); 499 for ( var i = 0, count = this.children.length; i < count; i++ ) 500 fragment.append( this.children[ i ].toDom( doc ) ); 501 502 return fragment; 494 503 } 495 504 }; 496 505 })(); -
_source/plugins/htmldataprocessor/plugin.js
240 240 }; 241 241 } 242 242 243 // Disallow structural blocks without sub nodes. 244 var removeIfEmptyBlocks = { table:1, thead:1,tfoot:1,tbody:1, tr:1 }, 245 removeIfEmptyFilterRules = { elements : {} }, 246 removeIfEmtpyFilter = function( element ){ if ( !element.children.length ) return false; }; 247 248 for ( i in removeIfEmptyBlocks ) 249 removeIfEmptyFilterRules.elements[ i ] = removeIfEmtpyFilter; 250 243 251 var protectAttributeRegex = /<(?:a|area|img|input)[\s\S]*?\s((?:href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+)))/gi; 244 252 245 253 var protectElementsRegex = /(?:<style(?=[ >])[^>]*>[\s\S]*<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi, … … 366 374 367 375 dataProcessor.dataFilter.addRules( defaultDataFilterRules ); 368 376 dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules ); 377 dataProcessor.dataFilter.addRules( removeIfEmptyFilterRules ); 369 378 dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules ); 370 379 dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules ); 380 dataProcessor.htmlFilter.addRules( removeIfEmptyFilterRules ); 371 381 } 372 382 }); 373 383