Ticket #5309: 5309_6.patch
File 5309_6.patch, 20.4 KB (added by , 14 years ago) |
---|
-
_source/plugins/htmldataprocessor/plugin.js
272 272 defaultHtmlFilterRules.elements[ i ] = unprotectReadyOnly; 273 273 } 274 274 275 // Disallow structural blocks without sub nodes. 276 var removeIfEmptyBlocks = { table:1, thead:1,tfoot:1,tbody:1, tr:1 }, 277 removeIfEmptyFilterRules = { elements : {} }, 278 removeIfEmtpyFilter = function( element ){ if ( !element.children.length ) return false; }; 279 280 for ( i in removeIfEmptyBlocks ) 281 removeIfEmptyFilterRules.elements[ i ] = removeIfEmtpyFilter; 282 275 283 var protectAttributeRegex = /<((?:a|area|img|input)[\s\S]*?\s)((href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+)))([^>]*)>/gi, 276 284 findSavedSrcRegex = /\s_cke_saved_src\s*=/; 277 285 … … 411 419 412 420 dataProcessor.dataFilter.addRules( defaultDataFilterRules ); 413 421 dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules ); 422 dataProcessor.dataFilter.addRules( removeIfEmptyFilterRules ); 414 423 dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules ); 415 424 dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules ); 425 dataProcessor.htmlFilter.addRules( removeIfEmptyFilterRules ); 416 426 } 417 427 }); 418 428 … … 427 437 428 438 CKEDITOR.htmlDataProcessor.prototype = 429 439 { 430 toHtml : function( data, fixForBody )440 toHtml : function( data, fixForBody, clipboard ) 431 441 { 432 442 // The source data is already HTML, but we need to clean 433 443 // it up and apply the filter. … … 473 483 474 484 // Now use our parser to make further fixes to the structure, as 475 485 // well as apply the filter. 476 var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody ),486 var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody, clipboard ), 477 487 writer = new CKEDITOR.htmlParser.basicWriter(); 478 488 479 489 fragment.writeHtml( writer, this.dataFilter ); -
_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/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. … … 725 747 { 726 748 var attribute = thisAttribs[ i ]; 727 749 750 if ( attribute.nodeName == '_moz_dirty' ) 751 continue; 752 728 753 if ( ( !CKEDITOR.env.ie || ( attribute.specified && attribute.nodeName != '_cke_expando' ) ) && attribute.nodeValue != otherElement.getAttribute( attribute.nodeName ) ) 729 754 return false; 730 755 } -
_source/core/htmlparser/fragment.js
8 8 * @constructor 9 9 * @example 10 10 */ 11 CKEDITOR.htmlParser.fragment = function( )11 CKEDITOR.htmlParser.fragment = function( clipboard ) 12 12 { 13 13 /** 14 14 * The nodes contained in the root of this fragment. … … 30 30 /** @private */ 31 31 this._ = 32 32 { 33 isBlockLike : true,34 hasInlineStarted : false33 isBlockLike : !clipboard, 34 hasInlineStarted : clipboard 35 35 }; 36 36 }; 37 37 … … 58 58 * alert( fragment.children[0].name ); "b" 59 59 * alert( fragment.children[1].value ); " Text" 60 60 */ 61 CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, fixForBody )61 CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, fixForBody, clipboard ) 62 62 { 63 63 var parser = new CKEDITOR.htmlParser(), 64 64 html = [], 65 fragment = new CKEDITOR.htmlParser.fragment( ),65 fragment = new CKEDITOR.htmlParser.fragment( clipboard ), 66 66 pendingInline = [], 67 67 pendingBRs = [], 68 68 currentNode = fragment, … … 205 205 var currentName = currentNode.name; 206 206 207 207 var currentDtd = currentName 208 && (CKEDITOR.dtd[ currentName ]209 || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ));208 && CKEDITOR.dtd[ currentName ] 209 || ( currentNode._.isBlockLike || currentNode instanceof CKEDITOR.htmlParser.fragment ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ); 210 210 211 211 // If the element cannot be child of the current element. 212 212 if ( currentDtd // Fragment could receive any elements. … … 379 379 380 380 // Shrinking consequential spaces into one single for all elements 381 381 // text contents. 382 if ( ! inPre)382 if ( !( inPre || currentNode instanceof CKEDITOR.htmlParser.fragment && clipboard ) ) 383 383 text = text.replace( /[\t\r\n ]{2,}|[\t\r\n]/g, ' ' ); 384 384 385 385 currentNode.add( new CKEDITOR.htmlParser.text( text ) ); … … 485 485 var writer = new CKEDITOR.htmlParser.basicWriter(); 486 486 this.writeChildrenHtml.call( this, writer, filter, true ); 487 487 var html = writer.getHtml(); 488 this.children = new CKEDITOR.htmlParser.fragment.fromHtml( html ).children;488 this.children = new CKEDITOR.htmlParser.fragment.fromHtml( html, false, true ).children; 489 489 isChildrenFiltered = 1; 490 490 }; 491 491 … … 499 499 { 500 500 for ( var i = 0 ; i < this.children.length ; i++ ) 501 501 this.children[i].writeHtml( writer, filter ); 502 }, 503 504 toDom : function( doc ) 505 { 506 var fragment = new CKEDITOR.dom.documentFragment( doc ); 507 for ( var i = 0, count = this.children.length; i < count; i++ ) 508 fragment.append( this.children[ i ].toDom( doc ) ); 509 510 return fragment; 502 511 } 503 512 }; 504 513 })(); -
_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/pastefromword/filter/default.js
1112 1112 1113 1113 try 1114 1114 { 1115 data = dataProcessor.toHtml( data, false );1115 data = dataProcessor.toHtml( data, false, true ); 1116 1116 } 1117 1117 catch ( e ) 1118 1118 { -
_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/htmlparser/text.js
50 50 return; 51 51 52 52 writer.text( text ); 53 }, 54 55 toDom : function( doc ) 56 { 57 if ( !CKEDITOR.tools.trim( this.value ) ) 58 return new CKEDITOR.dom.text( this.value ); 59 60 // DON'T use CKEDITOR.dom.text because of entities. 61 return CKEDITOR.dom.element.createFromHtml( this.value ); 53 62 } 54 63 }; 55 64 })(); -
_source/plugins/wysiwygarea/plugin.js
32 32 { 33 33 this.focus(); 34 34 35 var selection = this.getSelection(); 35 var selection = this.getSelection(), 36 start = selection.getStartElement(), 37 data = evt.data; 38 36 39 if ( checkReadOnly( selection ) ) 37 40 return; 38 41 39 var data = evt.data;40 42 this.fire( 'saveSnapshot' ); 41 43 42 if ( this.dataProcessor ) 43 data = this.dataProcessor.toHtml( data ); 44 44 // Make use of the browser to perform "extractContent". 45 45 if ( CKEDITOR.env.ie ) 46 { 47 var selIsLocked = selection.isLocked; 46 selection.getNative().createRange().pasteHTML( '' ); 47 else 48 this.document.$.execCommand( 'insertHtml', false, CKEDITOR.env.gecko? '<span _cke_bookmark=1></span>' : '' ); 48 49 49 if ( selIsLocked ) 50 selection.unlock(); 51 52 var $sel = selection.getNative(); 53 54 // Delete control selections to avoid IE bugs on pasteHTML. 55 if ( $sel.type == 'Control' ) 56 $sel.clear(); 57 else if ( selection.getType() == CKEDITOR.SELECTION_TEXT ) 58 { 59 // Due to IE bugs on handling contenteditable=false blocks 60 // (#6005), we need to make some checks and eventually 61 // delete the selection first. 62 63 var range = selection.getRanges()[0], 64 endContainer = range && range.endContainer; 50 // Paste will happen on the collapsed result range. 51 selection = this.getSelection(); 52 var range = selection.getRanges()[ 0 ], 53 data = evt.data; 65 54 66 if ( endContainer && 67 endContainer.type == CKEDITOR.NODE_ELEMENT && 68 endContainer.getAttribute( 'contenteditable' ) == 'false' && 69 range.checkBoundaryOfElement( endContainer, CKEDITOR.END ) ) 70 { 71 range.setEndAfter( range.endContainer ); 72 range.deleteContents(); 73 } 74 } 55 if ( this.dataProcessor ) 56 data = this.dataProcessor.toHtml( data, false, true ); 75 57 76 try 77 { 78 $sel.createRange().pasteHTML( data ); 79 } 80 catch (e) {} 58 // Merge the fragile inline elements splited by Gecko. 59 if ( CKEDITOR.env.gecko 60 && range.startContainer.type == CKEDITOR.NODE_ELEMENT 61 && range.startContainer.hasAttribute( '_cke_bookmark' ) ) 62 { 63 var bookmark = range.startContainer, 64 previous = bookmark.getPrevious(); 81 65 82 if ( selIsLocked ) 83 this.getSelection().lock(); 84 } 85 else 86 this.document.$.execCommand( 'inserthtml', false, data ); 66 if ( previous && previous.type == CKEDITOR.NODE_ELEMENT && previous.getName() in CKEDITOR.dtd.$inline ) 67 previous.mergeSiblings(); 87 68 88 // Webkit does not scroll to the cursor position after pasting (#5558) 89 if ( CKEDITOR.env.webkit ) 90 { 91 this.document.$.execCommand( 'inserthtml', false, '<span id="cke_paste_marker" cke_temp="1"></span>' ); 92 var marker = this.document.getById( 'cke_paste_marker' ); 93 marker.scrollIntoView(); 94 marker.remove(); 95 marker = null; 69 range.moveToBookmark( { startNode: bookmark } ); 96 70 } 97 71 72 // Paste and scroll to the end of content. 73 range.pasteHtml( data ); 74 range.select( 1 ); 75 !CKEDITOR.env.ie && selection.scrollIntoView(); 76 98 77 CKEDITOR.tools.setTimeout( function() 99 78 { 100 79 this.fire( 'saveSnapshot' ); -
_source/core/dom/range.js
320 320 return !whitespaceEval( node ) && !bookmarkEval( node ); 321 321 } 322 322 323 324 function collectValidChildren( element, parentName ) 325 { 326 var next = element, isValid, 327 candidates = []; 328 329 while ( ( next = next.getNextSourceNode( isValid, CKEDITOR.NODE_ELEMENT ) ) ) 330 { 331 if ( isValid = next.getName() in CKEDITOR.dtd[ parentName ] ) 332 candidates.push( next ); 333 } 334 335 var fragment = new CKEDITOR.dom.documentFragment(); 336 for ( var i = 0, count = candidates.length; i < count; i++ ) 337 fragment.append( candidates[ i ] ); 338 return fragment; 339 } 340 341 var emptyspaces = CKEDITOR.dom.walker.whitespaces(), 342 bookmarks = CKEDITOR.dom.walker.bookmark(); 343 323 344 CKEDITOR.dom.range.prototype = 324 345 { 325 346 clone : function() … … 674 695 }, 675 696 676 697 /** 698 * Paste the give HTML at the start of the range, and replacing any previously selected nodes within the range. 699 * Note: This method might alter the specified HTML to make it fit the given range context. For example, when pasting a table 700 * when actually a table row is selected (a DTD violation), instead of inserting the table, it results in only the cells of the table 701 * getting pasted into the row. 702 * For predictable results, paste only well-formed HTML that fits at the given range context. 703 * @param {String} html Valid HTML string to paste into the range. 704 */ 705 pasteHtml : function ( html ) 706 { 707 // Create a fragment of nodes without context that represents the structure to paste. (W3C Spec) 708 var fragment = CKEDITOR.htmlParser.fragment.fromHtml( html, false, true ), 709 nodeList = fragment.children; 710 711 if ( !this.collapsed ) 712 { 713 // Avoid resulting in dummy element after the deletion. 714 this.enlarge( CKEDITOR.NODE_ELEMENT ); 715 this.extractContents(); 716 } 717 718 // Split up text node at position. 719 if ( this.startContainer.type == CKEDITOR.NODE_TEXT ) 720 this.trim(); 721 722 // If we're at end of block, remove any bogus node, because: 723 // 1. It's not needed any more due to the incomming content. 724 // 2. It will get doubled (visible) if the to be inserted node is a bogus too. 725 var block; 726 if ( block = this.checkEndOfBlock() ) 727 { 728 var bogus = block.getBogus(); 729 bogus && bogus.remove(); 730 } 731 732 var targetNode, targetNodeOffset, targetName, path, 733 updateTarget = CKEDITOR.tools.bind( function () 734 { 735 targetNode = this.startContainer, 736 targetNodeOffset = this.startOffset, 737 targetName = targetNode.getName(); 738 path = new CKEDITOR.dom.elementPath( targetNode ); 739 }, this ); 740 741 updateTarget(); 742 for ( var i = 0, count = nodeList.length; i < count; i++ ) 743 { 744 var node = nodeList[ i ], 745 nodeName = node.type == CKEDITOR.NODE_ELEMENT ? node.name : '#'; 746 747 // If we have a list to insert, and we're actually standing inside a list item, 748 // insert the appropriate children instead, in short, merge the lists 749 // instead of pasting in a sublist. 750 if ( nodeName in CKEDITOR.dtd.$list 751 && path.block && path.block.getName() in CKEDITOR.dtd.$listItem ) 752 { 753 this.splitElement( path.block ); 754 updateTarget(); 755 } 756 757 // The to be inserted node fails DTD examination. 758 while ( !CKEDITOR.dtd[ targetName ][ nodeName ] ) 759 { 760 // If the failure is caused by pasting table/list inside a table/list 761 // structure, instead of splitting up the element, just insert the 762 // appropriate children. 763 if ( nodeName == 'table' && targetName in { table:1, tfoot:1,thead:1,tbody:1,tr:1 } 764 || nodeName in { ul:1, ol:1, dl:1 } && targetName in { ul:1, ol:1, dl:1 } ) 765 { 766 node = collectValidChildren( node.toDom(), targetName ); 767 break; 768 } 769 770 // Users are more likely want to paste content intead of the element, e.g. pre, headings. 771 if ( ( targetName == nodeName ) 772 && ( nodeName == 'pre' || nodeName.match( /^h[1-6]$/ ) ) ) 773 { 774 Array.prototype.splice.apply( nodeList, [ i, 1 ].concat( node.children ) ); 775 node = nodeList[ i ]; 776 break; 777 } 778 779 // Ok, it's our last option, split up or move out of current element. 780 this.splitElement( targetNode ); 781 updateTarget(); 782 } 783 784 var domNode = node.$ ? node : node.toDom(); 785 // Where to put the cursor? 786 var anchorNode = domNode.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ? 787 domNode.getLast() : domNode; 788 789 this.insertNode( domNode ); 790 791 // Try to place cursor at the end of the last pasted node, 792 // with the only exception that we should discontinue any pasted link, 793 // If that fails, simply collapse by the end of last node. 794 if ( !( i == count - 1 && anchorNode.type == CKEDITOR.NODE_ELEMENT 795 && !anchorNode.is( 'a' ) && this.moveToElementEditEnd( anchorNode ) ) ) 796 this.collapse(); 797 } 798 }, 799 800 /** 677 801 * Transforms the startContainer and endContainer properties from text 678 802 * nodes to element nodes, whenever possible. This is actually possible 679 803 * if either of the boundary containers point to a text node, and its … … 874 998 { 875 999 // If we reached the common ancestor, mark the flag 876 1000 // for it. 877 if ( !commonReached && enlargeable.equals( commonAncestor) )1001 if ( !commonReached && ( enlargeable.contains( commonAncestor ) || enlargeable.equals( commonAncestor ) ) ) 878 1002 commonReached = true; 879 1003 880 1004 if ( !body.contains( enlargeable ) ) … … 1044 1168 { 1045 1169 if ( enlargeable && !sibling ) 1046 1170 { 1047 if ( !commonReached && enlargeable.equals( commonAncestor) )1171 if ( !commonReached && ( enlargeable.contains( commonAncestor ) || enlargeable.equals( commonAncestor ) ) ) 1048 1172 commonReached = true; 1049 1173 1050 1174 if ( !body.contains( enlargeable ) ) … … 1361 1485 var startContainer = this.startContainer; 1362 1486 var startOffset = this.startOffset; 1363 1487 1488 var count = node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ? 1489 node.getChildren().count() : 1; 1490 1491 var anchorNode = node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ? 1492 node.getChildren().getItem( 0 ) : node; 1493 1364 1494 var nextNode = startContainer.getChild( startOffset ); 1365 1495 1366 1496 if ( nextNode ) … … 1369 1499 startContainer.append( node ); 1370 1500 1371 1501 // Check if we need to update the end boundary. 1372 if ( node.getParent().equals( this.endContainer ) )1373 this.endOffset ++;1502 if ( startContainer.equals( this.endContainer ) ) 1503 this.endOffset += count; 1374 1504 1375 1505 // Expand the range to embrace the new node. 1376 this.setStartBefore( node );1506 this.setStartBefore( anchorNode ); 1377 1507 }, 1378 1508 1379 1509 moveToPosition : function( node, position ) … … 1628 1758 if ( !this.collapsed ) 1629 1759 return null; 1630 1760 1761 var testRange = this.clone(), 1762 walker = new CKEDITOR.dom.walker( testRange ); 1763 1764 walker.evaluator = function( node ) 1765 { 1766 return !!( emptyspaces( node ) || bookmarks( node ) ); 1767 }; 1768 1769 testRange.setEndAfter( toSplit ); 1770 if ( walker.checkForward() ) 1771 { 1772 this.moveToPosition( toSplit, CKEDITOR.POSITION_AFTER_END ); 1773 return null; 1774 } 1775 else 1776 { 1777 testRange = this.clone(); 1778 testRange.setStartBefore( toSplit ); 1779 walker.reset(); 1780 walker.range = testRange; 1781 if ( walker.checkBackward() ) 1782 { 1783 this.moveToPosition( toSplit, CKEDITOR.POSITION_BEFORE_START ); 1784 return null; 1785 } 1786 } 1787 1631 1788 // Extract the contents of the block from the selection point to the end 1632 1789 // of its contents. 1633 1790 this.setEndAt( toSplit, CKEDITOR.POSITION_BEFORE_END ); … … 1736 1893 // Creates a range starting at the block start until the range start. 1737 1894 var walkerRange = this.clone(); 1738 1895 walkerRange.collapse( false ); 1739 walkerRange.setEndAt( path.block || path.blockLimit, CKEDITOR.POSITION_BEFORE_END ); 1896 var pathBlock = path.block || path.blockLimit; 1897 walkerRange.setEndAt( pathBlock, CKEDITOR.POSITION_BEFORE_END ); 1740 1898 1741 1899 var walker = new CKEDITOR.dom.walker( walkerRange ); 1742 1900 walker.evaluator = getCheckStartEndBlockEvalFunction( false ); 1743 1901 1744 return walker.checkForward() ;1902 return walker.checkForward() && pathBlock; 1745 1903 }, 1746 1904 1747 1905 /**