Ticket #5309: 5309.patch

File 5309.patch, 15.4 KB (added by Garry Yao, 14 years ago)
  • _source/core/dom/range.js

     
    317317                return !whitespaceEval( node ) && !bookmarkEval( node );
    318318        }
    319319
     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
    320341        CKEDITOR.dom.range.prototype =
    321342        {
    322343                clone : function()
     
    666687                },
    667688
    668689                /**
     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                        // Scroll the last inserted node into viewport.
     792                        anchorNode.scrollIntoView();
     793                },
     794
     795                /**
    669796                 * Transforms the startContainer and endContainer properties from text
    670797                 * nodes to element nodes, whenever possible. This is actually possible
    671798                 * if either of the boundary containers point to a text node, and its
     
    866993                                                {
    867994                                                        // If we reached the common ancestor, mark the flag
    868995                                                        // for it.
    869                                                         if ( !commonReached && enlargeable.equals( commonAncestor ) )
     996                                                        if ( !commonReached && ( enlargeable.contains( commonAncestor ) || enlargeable.equals( commonAncestor ) ) )
    870997                                                                commonReached = true;
    871998
    872999                                                        if ( !body.contains( enlargeable ) )
     
    10361163                                        {
    10371164                                                if ( enlargeable && !sibling )
    10381165                                                {
    1039                                                         if ( !commonReached && enlargeable.equals( commonAncestor ) )
     1166                                                        if ( !commonReached && ( enlargeable.contains( commonAncestor ) || enlargeable.equals( commonAncestor ) ) )
    10401167                                                                commonReached = true;
    10411168
    10421169                                                        if ( !body.contains( enlargeable ) )
     
    13411468                        var startContainer = this.startContainer;
    13421469                        var startOffset = this.startOffset;
    13431470
     1471                        var count = node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ?
     1472                                node.getChildren().count() : 1;
     1473
     1474                        var anchorNode = node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ?
     1475                                node.getChildren().getItem( 0 ) : node;
     1476
    13441477                        var nextNode = startContainer.getChild( startOffset );
    13451478
    13461479                        if ( nextNode )
     
    13491482                                startContainer.append( node );
    13501483
    13511484                        // Check if we need to update the end boundary.
    1352                         if ( node.getParent().equals( this.endContainer ) )
    1353                                 this.endOffset++;
     1485                        if ( startContainer.equals( this.endContainer ) )
     1486                                this.endOffset += count;
    13541487
    13551488                        // Expand the range to embrace the new node.
    1356                         this.setStartBefore( node );
     1489                        this.setStartBefore( anchorNode );
    13571490                },
    13581491
    13591492                moveToPosition : function( node, position )
     
    15981731                        if ( !this.collapsed )
    15991732                                return null;
    16001733
     1734                        var testRange = this.clone(),
     1735                                        walker = new CKEDITOR.dom.walker( testRange );
     1736
     1737                        walker.evaluator = function( node )
     1738                        {
     1739                                return !!( emptyspaces( node ) || bookmarks( node ) );
     1740                        };
     1741
     1742                        testRange.setEndAfter( toSplit );
     1743                        if ( walker.checkForward() )
     1744                        {
     1745                                this.moveToPosition( toSplit, CKEDITOR.POSITION_AFTER_END );
     1746                                return null;
     1747                        }
     1748                        else
     1749                        {
     1750                                testRange = this.clone();
     1751                                testRange.setStartBefore( toSplit );
     1752                                walker.reset();
     1753                                walker.range = testRange;
     1754                                if ( walker.checkBackward() )
     1755                                {
     1756                                        this.moveToPosition( toSplit, CKEDITOR.POSITION_BEFORE_START );
     1757                                        return null;
     1758                                }
     1759                        }
     1760                               
    16011761                        // Extract the contents of the block from the selection point to the end
    16021762                        // of its contents.
    16031763                        this.setEndAt( toSplit, CKEDITOR.POSITION_BEFORE_END );
     
    16961856                        // Creates a range starting at the block start until the range start.
    16971857                        var walkerRange = this.clone();
    16981858                        walkerRange.collapse( false );
    1699                         walkerRange.setEndAt( path.block || path.blockLimit, CKEDITOR.POSITION_BEFORE_END );
     1859                        var pathBlock = path.block || path.blockLimit;
     1860                        walkerRange.setEndAt( pathBlock, CKEDITOR.POSITION_BEFORE_END );
    17001861
    17011862                        var walker = new CKEDITOR.dom.walker( walkerRange );
    17021863                        walker.evaluator = getCheckStartEndBlockEvalFunction( false );
    17031864
    1704                         return walker.checkForward();
     1865                        return walker.checkForward() && pathBlock;
    17051866                },
    17061867
    17071868                /**
  • _source/plugins/htmldataprocessor/plugin.js

     
    240240                };
    241241        }
    242242
     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
    243251        var protectAttributeRegex = /<(?:a|area|img|input)[\s\S]*?\s((?:href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+)))/gi;
    244252
    245253        var protectElementsRegex = /(?:<style(?=[ >])[^>]*>[\s\S]*<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi,
     
    366374
    367375                        dataProcessor.dataFilter.addRules( defaultDataFilterRules );
    368376                        dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules );
     377                        dataProcessor.dataFilter.addRules( removeIfEmptyFilterRules );
    369378                        dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules );
    370379                        dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules );
     380                        dataProcessor.htmlFilter.addRules( removeIfEmptyFilterRules );
    371381                }
    372382        });
    373383
  • _source/core/htmlparser/cdata.js

     
    3838                writeHtml : function( writer )
    3939                {
    4040                        writer.write( this.value );
     41                },
     42
     43                toDom : function( doc )
     44                {
     45                        return new CKEDITOR.dom.text( this.value, doc );
    4146                }
    4247        };
    4348})();
  • _source/core/htmlparser/element.js

     
    235235                        // Send children.
    236236                        CKEDITOR.htmlParser.fragment.prototype.writeChildrenHtml.apply( this, arguments );
    237237
     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;
    238249                }
    239250        };
    240251})();
  • _source/core/htmlparser/text.js

     
    5050                                return;
    5151
    5252                        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 );
    5359                }
    5460        };
    5561})();
  • _source/plugins/wysiwygarea/plugin.js

     
    2424                        this.fire( 'saveSnapshot' );
    2525
    2626                        var selection = this.getSelection(),
     27                                ranges = selection.getRanges(),
    2728                                data = evt.data;
    2829
    2930                        if ( this.dataProcessor )
    3031                                data = this.dataProcessor.toHtml( data );
    3132
    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-- )
    3335                        {
    34                                 var selIsLocked = selection.isLocked;
     36                                var range = ranges[ i ],
     37                                        previousRange = ranges[ i - 1 ];
    3538
    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                        }
    4964
     65                        selection.selectRanges( ranges );
     66
    5067                        CKEDITOR.tools.setTimeout( function()
    5168                                {
    5269                                        this.fire( 'saveSnapshot' );
  • _source/core/dom/element.js

     
    254254                },
    255255
    256256                /**
     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( /^(?:&nbsp;|\xa0)$/ )
     272                                ||      CKEDITOR.env.gecko && CKEDITOR.env.webkit && lastChild.is( 'br' ) ) )
     273                        {
     274                                return lastChild;
     275                        }
     276                },
     277
     278                /**
    257279                 * Breaks one of the ancestor element in the element position, moving
    258280                 * this element between the broken parts.
    259281                 * @param {CKEDITOR.dom.element} parent The anscestor element to get broken.
  • _source/core/htmlparser/fragment.js

     
    491491                {
    492492                        for ( var i = 0 ; i < this.children.length ; i++ )
    493493                                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;
    494503                }
    495504        };
    496505})();
  • _source/core/htmlparser/comment.js

     
    5656                }
    5757
    5858                writer.comment( comment );
     59        },
     60
     61        toDom : function( doc )
     62        {
     63                return new CKEDITOR.dom.comment( this.value, doc );
    5964        }
     65
    6066};
© 2003 – 2022, CKSource sp. z o.o. sp.k. All rights reserved. | Terms of use | Privacy policy