Ticket #5309: 5309_6.patch

File 5309_6.patch, 20.4 KB (added by Garry Yao, 10 years ago)
  • _source/plugins/htmldataprocessor/plugin.js

     
    272272                defaultHtmlFilterRules.elements[ i ] = unprotectReadyOnly;
    273273        }
    274274
     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
    275283        var protectAttributeRegex = /<((?:a|area|img|input)[\s\S]*?\s)((href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+)))([^>]*)>/gi,
    276284                findSavedSrcRegex = /\s_cke_saved_src\s*=/;
    277285
     
    411419
    412420                        dataProcessor.dataFilter.addRules( defaultDataFilterRules );
    413421                        dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules );
     422                        dataProcessor.dataFilter.addRules( removeIfEmptyFilterRules );
    414423                        dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules );
    415424                        dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules );
     425                        dataProcessor.htmlFilter.addRules( removeIfEmptyFilterRules );
    416426                }
    417427        });
    418428
     
    427437
    428438        CKEDITOR.htmlDataProcessor.prototype =
    429439        {
    430                 toHtml : function( data, fixForBody )
     440                toHtml : function( data, fixForBody, clipboard )
    431441                {
    432442                        // The source data is already HTML, but we need to clean
    433443                        // it up and apply the filter.
     
    473483
    474484                        // Now use our parser to make further fixes to the structure, as
    475485                        // well as apply the filter.
    476                         var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody ),
     486                        var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody, clipboard ),
    477487                                writer = new CKEDITOR.htmlParser.basicWriter();
    478488
    479489                        fragment.writeHtml( writer, this.dataFilter );
  • _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/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.
     
    725747                        {
    726748                                var attribute = thisAttribs[ i ];
    727749
     750                                if ( attribute.nodeName == '_moz_dirty' )
     751                                        continue;
     752
    728753                                if ( ( !CKEDITOR.env.ie || ( attribute.specified && attribute.nodeName != '_cke_expando' ) ) && attribute.nodeValue != otherElement.getAttribute( attribute.nodeName ) )
    729754                                        return false;
    730755                        }
  • _source/core/htmlparser/fragment.js

     
    88 * @constructor
    99 * @example
    1010 */
    11 CKEDITOR.htmlParser.fragment = function()
     11CKEDITOR.htmlParser.fragment = function( clipboard )
    1212{
    1313        /**
    1414         * The nodes contained in the root of this fragment.
     
    3030        /** @private */
    3131        this._ =
    3232        {
    33                 isBlockLike : true,
    34                 hasInlineStarted : false
     33                isBlockLike : !clipboard,
     34                hasInlineStarted : clipboard
    3535        };
    3636};
    3737
     
    5858         * alert( fragment.children[0].name );  "b"
    5959         * alert( fragment.children[1].value );  " Text"
    6060         */
    61         CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, fixForBody )
     61        CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, fixForBody, clipboard )
    6262        {
    6363                var parser = new CKEDITOR.htmlParser(),
    6464                        html = [],
    65                         fragment = new CKEDITOR.htmlParser.fragment(),
     65                        fragment = new CKEDITOR.htmlParser.fragment( clipboard ),
    6666                        pendingInline = [],
    6767                        pendingBRs = [],
    6868                        currentNode = fragment,
     
    205205                        var currentName = currentNode.name;
    206206
    207207                        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 );
    210210
    211211                        // If the element cannot be child of the current element.
    212212                        if ( currentDtd   // Fragment could receive any elements.
     
    379379
    380380                        // Shrinking consequential spaces into one single for all elements
    381381                        // text contents.
    382                         if ( !inPre )
     382                        if ( !( inPre || currentNode instanceof CKEDITOR.htmlParser.fragment && clipboard ) )
    383383                                text = text.replace( /[\t\r\n ]{2,}|[\t\r\n]/g, ' ' );
    384384
    385385                        currentNode.add( new CKEDITOR.htmlParser.text( text ) );
     
    485485                                var writer = new CKEDITOR.htmlParser.basicWriter();
    486486                                this.writeChildrenHtml.call( this, writer, filter, true );
    487487                                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;
    489489                                isChildrenFiltered = 1;
    490490                        };
    491491
     
    499499                {
    500500                        for ( var i = 0 ; i < this.children.length ; i++ )
    501501                                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;
    502511                }
    503512        };
    504513})();
  • _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/plugins/pastefromword/filter/default.js

     
    11121112
    11131113                try
    11141114                {
    1115                         data = dataProcessor.toHtml( data, false );
     1115                        data = dataProcessor.toHtml( data, false, true );
    11161116                }
    11171117                catch ( e )
    11181118                {
  • _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};
  • _source/core/htmlparser/text.js

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

     
    3232                {
    3333                        this.focus();
    3434
    35                         var selection = this.getSelection();
     35                        var selection = this.getSelection(),
     36                                start = selection.getStartElement(),
     37                                data = evt.data;
     38
    3639                        if ( checkReadOnly( selection ) )
    3740                                return;
    3841
    39                         var data = evt.data;
    4042                        this.fire( 'saveSnapshot' );
    4143
    42                         if ( this.dataProcessor )
    43                                 data = this.dataProcessor.toHtml( data );
    44 
     44                        // Make use of the browser to perform "extractContent".
    4545                        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>' : '' );
    4849
    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;
    6554
    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 );
    7557
    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();
    8165
    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();
    8768
    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 } );
    9670                        }
    9771
     72                        // Paste and scroll to the end of content.
     73                        range.pasteHtml( data );
     74                        range.select( 1 );
     75                        !CKEDITOR.env.ie && selection.scrollIntoView();
     76
    9877                        CKEDITOR.tools.setTimeout( function()
    9978                                {
    10079                                        this.fire( 'saveSnapshot' );
  • _source/core/dom/range.js

     
    320320                return !whitespaceEval( node ) && !bookmarkEval( node );
    321321        }
    322322
     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
    323344        CKEDITOR.dom.range.prototype =
    324345        {
    325346                clone : function()
     
    674695                },
    675696
    676697                /**
     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                /**
    677801                 * Transforms the startContainer and endContainer properties from text
    678802                 * nodes to element nodes, whenever possible. This is actually possible
    679803                 * if either of the boundary containers point to a text node, and its
     
    874998                                                {
    875999                                                        // If we reached the common ancestor, mark the flag
    8761000                                                        // for it.
    877                                                         if ( !commonReached && enlargeable.equals( commonAncestor ) )
     1001                                                        if ( !commonReached && ( enlargeable.contains( commonAncestor ) || enlargeable.equals( commonAncestor ) ) )
    8781002                                                                commonReached = true;
    8791003
    8801004                                                        if ( !body.contains( enlargeable ) )
     
    10441168                                        {
    10451169                                                if ( enlargeable && !sibling )
    10461170                                                {
    1047                                                         if ( !commonReached && enlargeable.equals( commonAncestor ) )
     1171                                                        if ( !commonReached && ( enlargeable.contains( commonAncestor ) || enlargeable.equals( commonAncestor ) ) )
    10481172                                                                commonReached = true;
    10491173
    10501174                                                        if ( !body.contains( enlargeable ) )
     
    13611485                        var startContainer = this.startContainer;
    13621486                        var startOffset = this.startOffset;
    13631487
     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
    13641494                        var nextNode = startContainer.getChild( startOffset );
    13651495
    13661496                        if ( nextNode )
     
    13691499                                startContainer.append( node );
    13701500
    13711501                        // 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;
    13741504
    13751505                        // Expand the range to embrace the new node.
    1376                         this.setStartBefore( node );
     1506                        this.setStartBefore( anchorNode );
    13771507                },
    13781508
    13791509                moveToPosition : function( node, position )
     
    16281758                        if ( !this.collapsed )
    16291759                                return null;
    16301760
     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                               
    16311788                        // Extract the contents of the block from the selection point to the end
    16321789                        // of its contents.
    16331790                        this.setEndAt( toSplit, CKEDITOR.POSITION_BEFORE_END );
     
    17361893                        // Creates a range starting at the block start until the range start.
    17371894                        var walkerRange = this.clone();
    17381895                        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 );
    17401898
    17411899                        var walker = new CKEDITOR.dom.walker( walkerRange );
    17421900                        walker.evaluator = getCheckStartEndBlockEvalFunction( false );
    17431901
    1744                         return walker.checkForward();
     1902                        return walker.checkForward() && pathBlock;
    17451903                },
    17461904
    17471905                /**
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy