Ticket #2905: 2905_5.patch

File 2905_5.patch, 37.4 KB (added by Martin Kou, 14 years ago)
  • _source/lang/en.js

     
    176176                matchCase                       : 'Match case',
    177177                matchWord                       : 'Match whole word',
    178178                matchCyclic                     : 'Match cyclic',
    179                 replaceAll                      : 'Replace All'
     179                replaceAll                      : 'Replace All',
     180                replaceSuccessMsg       : 'Successfully replaced %1 finds.'
    180181        },
    181182
    182183        // Table Dialog
  • _source/plugins/styles/plugin.js

     
    9898                };
    9999        };
    100100
     101        var applyStyle = function( document, remove )
     102        {
     103                // Get all ranges from the selection.
     104                var selection = document.getSelection();
     105                var ranges = selection.getRanges();
     106                var func = remove ? this.removeFromRange : this.applyToRange;
     107
     108                // Apply the style to the ranges.
     109                for ( var i = 0 ; i < ranges.length ; i++ )
     110                        func.call( this, ranges[ i ] );
     111
     112                // Select the ranges again.
     113                selection.selectRanges( ranges );
     114        };
     115
    101116        CKEDITOR.style.prototype =
    102117        {
    103118                apply : function( document )
    104119                {
    105                         // Get all ranges from the selection.
    106                         var selection = document.getSelection();
    107                         var ranges = selection.getRanges();
     120                        applyStyle.call( this, document, false );
     121                },
    108122
    109                         // Apply the style to the ranges.
    110                         for ( var i = 0 ; i < ranges.length ; i++ )
    111                                 this.applyToRange( ranges[ i ] );
    112 
    113                         // Select the ranges again.
    114                         selection.selectRanges( ranges );
     123                remove : function( document )
     124                {
     125                        applyStyle.call( this, document, true );
    115126                },
    116127
    117128                applyToRange : function( range )
     
    124135                                                : null ).call( this, range );
    125136                },
    126137
     138                removeFromRange : function( range )
     139                {
     140                        return ( this.removeFromRange =
     141                                                this.type == CKEDITOR.STYLE_INLINE ?
     142                                                        removeInlineStyle
     143                                                : null ).call( this, range );
     144                },
     145
    127146                /**
    128147                 * Get the style state inside an element path. Returns "true" if the
    129148                 * element is active in the path.
     
    359378
    360379                                        // Here we do some cleanup, removing all duplicated
    361380                                        // elements from the style element.
    362                                         removeFromElement( this, styleNode );
     381                                        removeFromInsideElement( this, styleNode );
    363382
    364383                                        // Insert it into the range position (it is collapsed after
    365384                                        // extractContents.
     
    389408                range.moveToBookmark( bookmark );
    390409        };
    391410
    392         var applyBlockStyle = function( range )
     411        var removeInlineStyle = function( range )
    393412        {
    394         };
     413                /*
     414                 * Make sure our range has included all "collpased" parent inline nodes so
     415                 * that our operation logic can be simpler.
     416                 */
     417                range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
    395418
    396         // Removes a style from inside an element.
    397         var removeFromElement = function( style, element )
    398         {
    399                 var def = style._.definition;
    400                 var attribs = def.attributes;
    401                 var styles = def.styles;
     419                var bookmark = range.createBookmark( true ),
     420                        startNode = range.document.getById( bookmark.startNode ),
     421                        startPath = new CKEDITOR.dom.elementPath( startNode.getParent() );
    402422
    403                 var innerElements = element.getElementsByTag( style.element );
     423                if ( range.collapsed )
     424                {
     425                        /*
     426                         * If the range is collapsed, try to remove the style from all ancestor
     427                         * elements, until either a block boundary is reached, or the style is
     428                         * removed.
     429                         */
     430                        for ( var i = 0, element ; i < startPath.elements.length && ( element = startPath.elements[i] )  ; i++ )
     431                        {
     432                                if ( element == startPath.block || element == startPath.blockLimit )
     433                                        break;
    404434
    405                 for ( var i = innerElements.count() ; --i >= 0 ; )
     435                                if ( this.checkElementRemovable( element ) )
     436                                {
     437                                        removeFromElement( this, element );
     438                                        break;
     439                                }
     440                        }
     441                }
     442                else
    406443                {
    407                         var innerElement = innerElements.getItem( i );
     444                        /*
     445                         * Now our range isn't collapsed. Lets walk from the start node to the end
     446                         * node via DFS and remove the styles one-by-one.
     447                         */
     448                        var endNode = range.document.getById( bookmark.endNode ),
     449                                endPath = new CKEDITOR.dom.elementPath( endNode.getParent() );
     450                                currentNode = startNode;
    408451
    409                         for ( var attName in attribs )
     452                        // Find out the ancestor that needs to be broken down at startNode and endNode.
     453                        var breakStart = null, breakEnd = null;
     454                        for ( var i = 0 ; i < startPath.elements.length ; i++ )
    410455                        {
    411                                 // The 'class' element value must match (#1318).
    412                                 if ( attName == 'class' && innerElement.getAttribute( 'class' ) != attribs[ attName ] )
    413                                         continue;
    414 
    415                                 innerElement.removeAttribute( attName );
     456                                if ( this.checkElementRemovable( startPath.elements[ i ] ) )
     457                                {
     458                                        breakStart = startPath.elements[ i ];
     459                                        break;
     460                                }
    416461                        }
     462                        for ( var i = 0 ; i < endPath.elements.length ; i++ )
     463                        {
     464                                if ( this.checkElementRemovable( endPath.elements[ i ] ) )
     465                                {
     466                                        breakEnd = endPath.elements[ i ];
     467                                        break;
     468                                }
     469                        }
    417470
    418                         for ( var styleName in styles )
     471                        if ( breakEnd )
     472                                endNode.breakParent( breakEnd );
     473                        if ( breakStart )
     474                                startNode.breakParent( breakStart );
     475
     476                        // Now, do the DFS walk.
     477                        while ( ( currentNode = currentNode.getNextSourceNode() ) && !currentNode.equals( endNode ) )
    419478                        {
    420                                 innerElement.removeStyle( styleName );
     479                                if ( currentNode.type == CKEDITOR.NODE_ELEMENT )
     480                                        removeFromElement( this, currentNode );
    421481                        }
     482                }
     483               
     484                range.moveToBookmark( bookmark );
     485        };
    422486
    423                         removeNoAttribsElement( innerElement );
     487        var applyBlockStyle = function( range )
     488        {
     489        };
     490
     491        // Removes a style from an element itself, don't care about its subtree.
     492        var removeFromElement = function( style, element )
     493        {
     494                var def = style._.definition,
     495                        attributes = def.attributes,
     496                        styles = def.styles;
     497
     498                for ( var attName in attributes )
     499                {
     500                        // The 'class' element value must match (#1318).
     501                        if ( attName == 'class' && element.getAttribute( attName ) != attributes[ attName ] )
     502                                continue;
     503                        element.removeAttribute( attName );
    424504                }
     505
     506                for ( var styleName in styles )
     507                        element.removeStyle( styleName );
     508
     509                removeNoAttribsElement( element );
    425510        };
    426511
     512        // Removes a style from inside an element.
     513        var removeFromInsideElement = function( style, element )
     514        {
     515                var def = style._.definition;
     516                var attribs = def.attributes;
     517                var styles = def.styles;
     518
     519                var innerElements = element.getElementsByTag( style.element );
     520
     521                for ( var i = innerElements.count() ; --i >= 0 ; )
     522                        removeFromElement( style, innerElements.getItem( i ) );
     523        };
     524
    427525        // If the element has no more attributes, remove it.
    428526        var removeNoAttribsElement = function( element )
    429527        {
     
    596694        var doc = editor.document;
    597695
    598696        if ( doc )
    599                 this.style.apply( doc );
     697        {
     698                if ( this.state == CKEDITOR.TRISTATE_OFF )
     699                        this.style.apply( doc );
     700                else if ( this.state == CKEDITOR.TRISTATE_ON )
     701                        this.style.remove( doc );
     702        }
    600703
    601704        return !!doc;
    602705};
  • _source/plugins/find/dialogs/find.js

     
     1/*
     2Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
     3For licensing, see LICENSE.html or http://ckeditor.com/license
     4*/
     5
     6(function()
     7{
     8        // Element tag names which prevent characters counting.
     9        var characterBoundaryElementsEnum =
     10        {
     11                address :1, blockquote :1, dl :1, h1 :1, h2 :1, h3 :1,
     12                h4 :1, h5 :1, h6 :1, p :1, pre :1, li :1, dt :1, de :1, div :1, td:1, th:1
     13        };
     14       
     15        var guardDomWalkerNonEmptyTextNode = function( evt )
     16        {
     17                if ( evt.data.to && evt.data.to.type == CKEDITOR.NODE_TEXT
     18                        && evt.data.to.$.length > 0 )
     19                        this.stop();
     20                CKEDITOR.dom.domWalker.blockBoundary( { br : 1 } ).call( this, evt );
     21        };
     22       
     23       
     24        /**
     25         * Get the cursor object which represent both current character and it's dom
     26         * position thing.
     27         */
     28        var cursorStep = function()
     29        {
     30                var obj = {
     31                        textNode : this.textNode,
     32                        offset : this.offset,
     33                        character : this.textNode ? this.textNode.getText().charAt( this.offset ) : null,
     34                        hitMatchBoundary : this._.matchBoundary
     35                };
     36                return obj;
     37        };
     38       
     39        var pages = [ 'find', 'replace' ],
     40                fieldsMapping = [
     41                [ 'txtFindFind', 'txtFindReplace' ],
     42                [ 'txtFindCaseChk', 'txtReplaceCaseChk' ],
     43                [ 'txtFindWordChk', 'txtReplaceWordChk' ],
     44                [ 'txtFindCyclic', 'txtReplaceCyclic' ] ];
     45               
     46        /**
     47         * Synchronize corresponding filed values between 'replace' and 'find' pages.
     48         * @param {String} currentPageId        The page id which receive values.
     49         */
     50        function syncFieldsBetweenTabs( currentPageId )
     51        {
     52                var sourceIndex, targetIndex,
     53                        sourceField, targetField;
     54
     55                sourceIndex = currentPageId === 'find' ? 1 : 0;
     56                targetIndex = 1 - sourceIndex;
     57                var i, l = fieldsMapping.length;
     58                for ( i = 0 ; i < l ; i++ )
     59                {
     60                        var sourceField = this.getContentElement( pages[ sourceIndex ],
     61                                        fieldsMapping[ i ][ sourceIndex ] );
     62                        var targetField = this.getContentElement( pages[ targetIndex ],
     63                                        fieldsMapping[ i ][ targetIndex ] );
     64
     65                        targetField.setValue( sourceField.getValue() );
     66                }
     67        }
     68
     69        var findDialog = function( editor, startupPage )
     70        {
     71                // Style object for highlights.
     72                var highlightStyle = new CKEDITOR.style( editor.config.find_highlight );
     73               
     74                /**
     75                 * Iterator which walk through document char by char.
     76                 * @param {Object} start
     77                 * @param {Number} offset
     78                 */
     79                var characterWalker = function( start, offset )
     80                {
     81                        var isCursor = typeof( start.textNode ) !== 'undefined';
     82                        this.textNode = isCursor ? start.textNode : start;
     83                        this.offset = isCursor ? start.offset : offset;
     84                        this._ = {
     85                                walker : new CKEDITOR.dom.domWalker( this.textNode ),
     86                                matchBoundary : false
     87                        };
     88                };
     89
     90                characterWalker.prototype = {
     91                        next : function()
     92                        {
     93                                // Already at the end of document, no more character available.
     94                                if( this.textNode == null )
     95                                        return cursorStep.call( this );
     96                                       
     97                                this._.matchBoundary = false;
     98                               
     99                                // If there are more characters in the text node, get it and
     100                                // raise an event.
     101                                if( this.textNode.type == CKEDITOR.NODE_TEXT
     102                                        && this.offset < this.textNode.getLength() - 1 )
     103                                {
     104                                        this.offset++;
     105                                        return cursorStep.call( this );
     106                                }
     107
     108                                // If we are at the end of the text node, use dom walker to get
     109                                // the next text node.
     110                                var data = null;
     111                                while ( !data || ( data.node && data.node.type !=
     112                                        CKEDITOR.NODE_TEXT ) )
     113                                {
     114                                        data = this._.walker.forward(
     115                                                guardDomWalkerNonEmptyTextNode );
     116       
     117                                        // Block boundary? BR? Document boundary?
     118                                        if ( !data.node
     119                                                || ( data.node.type !== CKEDITOR.NODE_TEXT
     120                                                        && data.node.getName() in
     121                                                        characterBoundaryElementsEnum ) )
     122                                                this._.matchBoundary = true;
     123                                }
     124                                this.textNode = data.node;
     125                                this.offset = 0;
     126                                return cursorStep.call( this );
     127                        },
     128                       
     129                        back : function()
     130                        {
     131                                this._.matchBoundary = false;
     132
     133                                // More characters -> decrement offset and return.
     134                                if ( this.textNode.type == CKEDITOR.NODE_TEXT && this.offset > 0 )
     135                                {
     136                                        this.offset--;
     137                                        return cursorStep.call( this );
     138                                }
     139
     140                                // Start of text node -> use dom walker to get the previous text node.
     141                                var data = null;
     142                                while ( !data
     143                                || ( data.node && data.node.type != CKEDITOR.NODE_TEXT ) )
     144                                {
     145                                        data = this._.walker.reverse( guardDomWalkerNonEmptyTextNode );
     146
     147                                        // Block boundary? BR? Document boundary?
     148                                        if ( !data.node || ( data.node.type !== CKEDITOR.NODE_TEXT &&
     149                                        data.node.getName() in characterBoundaryElementsEnum ) )
     150                                                this._.matchBoundary = true;
     151                                }
     152                                this.textNode = data.node;
     153                                this.offset = data.node.length - 1;
     154                                return cursorStep.call( this );
     155                        }
     156                };
     157               
     158                /**
     159                 * A range of cursors which represent a trunk of characters which try to
     160                 * match, it has the same length as the pattern  string.
     161                 */
     162                var characterRange = function( characterWalker, rangeLength )
     163                {
     164                        this._ = {
     165                                walker : characterWalker,
     166                                cursors : [],
     167                                rangeLength : rangeLength,
     168                                highlightRange : null,
     169                                isMatched : false
     170                        };
     171                };
     172
     173                characterRange.prototype = {
     174                        /**
     175                         * Translate this range to {@link CKEDITOR.dom.range}
     176                         */
     177                        toDomRange : function()
     178                        {
     179                                var cursors = this._.cursors;
     180                                if ( cursors.length < 1 )
     181                                        return null;
     182
     183                                var first = cursors[0],
     184                                        last = cursors[ cursors.length - 1 ],
     185                                        range = new CKEDITOR.dom.range( editor.document );
     186
     187                                range.setStart( first.textNode, first.offset );
     188                                range.setEnd( last.textNode, last.offset + 1 );
     189                                return range;
     190                        },
     191
     192                        updateFromDomRange : function( domRange )
     193                        {
     194                                var startNode = domRange.startContainer,
     195                                        startIndex = domRange.startOffset,
     196                                        endNode = domRange.endContainer,
     197                                        endIndex = domRange.endOffset,
     198                                        boundaryNodes = domRange.getBoundaryNodes();
     199
     200                                if ( startNode.type != CKEDITOR.NODE_TEXT )
     201                                {
     202                                        startNode = boundaryNodes.startNode;
     203                                        while ( startNode.type != CKEDITOR.NODE_TEXT )
     204                                                startNode = startNode.getFirst();
     205                                        startIndex = 0;
     206                                }
     207
     208                                if ( endNode.type != CKEDITOR.NODE_TEXT )
     209                                {
     210                                        endNode = boundaryNodes.endNode;
     211                                        while ( endNode.type != CKEDITOR.NODE_TEXT )
     212                                                endNode = endNode.getLast();
     213                                        endIndex = endNode.getLength();
     214                                }
     215
     216                                // If the endNode is an empty text node, our walker would just walk through
     217                                // it without stopping. So need to backtrack to the nearest non-emtpy text
     218                                // node.
     219                                if ( endNode.getLength() < 1 )
     220                                {
     221                                        while ( ( endNode = endNode.getPreviousSourceNode() ) && !( endNode.type == CKEDITOR.NODE_TEXT && endNode.getLength() > 0 ) );
     222                                        endIndex = endNode.getLength();
     223                                }
     224
     225                                var cursor = new characterWalker( startNode, startIndex );
     226                                this._.cursors = [ cursor ];
     227                                if ( this._.rangeLength > 1 )
     228                                {
     229                                        do
     230                                        {
     231                                                cursor = new characterWalker( cursor );
     232                                                cursor.next();
     233                                                this._.cursors.push( cursor );
     234                                                if ( this._.cursors.length > this._.rangeLength )
     235                                                        this._.cursors.shift();
     236                                        }
     237                                        while ( !( cursor.textNode.equals( endNode ) && cursor.offset == endIndex - 1 ) );
     238                                }
     239                        },
     240                       
     241                        setMatched : function()
     242                        {
     243                                this._.isMatched = true;
     244                                this.highlight();
     245                        },
     246                       
     247                        clearMatched : function()
     248                        {
     249                                this._.isMatched = false;
     250                                this.removeHighlight();
     251                        },
     252                       
     253                        isMatched : function()
     254                        {
     255                                return this._.isMatched;
     256                        },
     257                       
     258                        /**
     259                         * Hightlight the current matched chunk of text.
     260                         */
     261                        highlight : function()
     262                        {
     263                                // Do not apply if nothing is found.
     264                                if ( this._.cursors.length < 1 )
     265                                        return;
     266
     267                                // Remove the previous highlight if there's one.
     268                                if ( this._.highlightRange )
     269                                        this.removeHighlight();
     270
     271                                // Apply the highlight.
     272                                var range = this.toDomRange();
     273                                highlightStyle.applyToRange( range );
     274                                this._.highlightRange = range;
     275
     276                                // Scroll the editor to the highlighted area.
     277                                var element = range.startContainer;
     278                                if ( element.type != CKEDITOR.NODE_ELEMENT )
     279                                        element = element.getParent();
     280                                element.scrollIntoView();
     281
     282                                // Update the character cursors.
     283                                this.updateFromDomRange( range );
     284                        },
     285                       
     286                        /**
     287                         * Remove highlighted find result.
     288                         */
     289                        removeHighlight : function()
     290                        {
     291                                if ( this._.highlightRange == null )
     292                                        return;
     293
     294                                highlightStyle.removeFromRange( this._.highlightRange );
     295                                this.updateFromDomRange( this._.highlightRange );
     296                                this._.highlightRange = null;
     297                        },
     298
     299                        moveBack : function()
     300                        {
     301                                var retval = this._.walker.back(),
     302                                        cursors = this._.cursors;
     303
     304                                if ( retval.hitMatchBoundary )
     305                                        this._.cursors = cursors = [];
     306
     307                                cursors.unshift( retval );
     308                                if ( cursors.length > this._.rangeLength )
     309                                        cursors.pop();
     310
     311                                return retval;
     312                        },
     313       
     314                        moveNext : function()
     315                        {
     316                                var retval = this._.walker.next(),
     317                                        cursors = this._.cursors;
     318
     319                                // Clear the cursors queue if we've crossed a match boundary.
     320                                if ( retval.hitMatchBoundary )
     321                                        this._.cursors = cursors = [];
     322
     323                                cursors.push( retval );
     324                                if ( cursors.length > this._.rangeLength )
     325                                        cursors.shift();
     326
     327                                return retval;
     328                        },
     329
     330                        getEndCharacter : function()
     331                        {
     332                                var cursors = this._.cursors;
     333                                if ( cursors.length < 1 )
     334                                        return null;
     335
     336                                return cursors[ cursors.length - 1 ].character;
     337                        },
     338
     339                        getNextRange : function( maxLength )
     340                        {
     341                                var cursors = this._.cursors;
     342                                if ( cursors.length < 1 )
     343                                        return null;
     344
     345                                var next = new characterWalker( cursors[ cursors.length - 1 ] );
     346                                return new characterRange( next, maxLength );
     347                        },
     348
     349                        getCursors : function()
     350                        {
     351                                return this._.cursors;
     352                        }
     353                };
     354
     355                var KMP_NOMATCH = 0,
     356                        KMP_ADVANCED = 1,
     357                        KMP_MATCHED = 2;
     358                /**
     359                 * Examination the occurrence of a word which implement KMP algorithm.
     360                 */
     361                var kmpMatcher = function( pattern, ignoreCase )
     362                {
     363                        var overlap = [ -1 ];
     364                        if ( ignoreCase )
     365                                pattern = pattern.toLowerCase();
     366                        for ( var i = 0 ; i < pattern.length ; i++ )
     367                        {
     368                                overlap.push( overlap[i] + 1 );
     369                                while ( overlap[ i + 1 ] > 0
     370                                        && pattern.charAt( i ) != pattern
     371                                                .charAt( overlap[ i + 1 ] - 1 ) )
     372                                        overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1;
     373                        }
     374
     375                        this._ = {
     376                                overlap : overlap,
     377                                state : 0,
     378                                ignoreCase : !!ignoreCase,
     379                                pattern : pattern
     380                        };
     381                };
     382
     383                kmpMatcher.prototype =
     384                {
     385                        feedCharacter : function( c )
     386                        {
     387                                if ( this._.ignoreCase )
     388                                        c = c.toLowerCase();
     389
     390                                while ( true )
     391                                {
     392                                        if ( c == this._.pattern.charAt( this._.state ) )
     393                                        {
     394                                                this._.state++;
     395                                                if ( this._.state == this._.pattern.length )
     396                                                {
     397                                                        this._.state = 0;
     398                                                        return KMP_MATCHED;
     399                                                }
     400                                                return KMP_ADVANCED;
     401                                        }
     402                                        else if ( this._.state == 0 )
     403                                                return KMP_NOMATCH;
     404                                        else
     405                                                this._.state = this._.overlap[ this._.state ];
     406                                }
     407
     408                                return null;
     409                        },
     410
     411                        reset : function()
     412                        {
     413                                this._.state = 0;
     414                        }
     415                };
     416
     417                var wordSeparatorRegex =
     418                /[.,"'?!;: \u0085\u00a0\u1680\u280e\u2028\u2029\u202f\u205f\u3000]/;
     419
     420                var isWordSeparator = function( c )
     421                {
     422                        if ( !c )
     423                                return true;
     424                        var code = c.charCodeAt( 0 );
     425                        return ( code >= 9 && code <= 0xd )
     426                                || ( code >= 0x2000 && code <= 0x200a )
     427                                || wordSeparatorRegex.test( c );
     428                };
     429
     430                var finder = {
     431                        startCursor : null,
     432                        range : null,
     433                        find : function( pattern, matchCase, matchWord, matchCyclic )
     434                        {
     435                                if( !this.range )
     436                                        this.range = new characterRange( new characterWalker( this.startCursor ), pattern.length );
     437                                else
     438                                {
     439                                        this.range.removeHighlight();
     440                                        this.range = this.range.getNextRange( pattern.length );
     441                                }
     442
     443                                var matcher = new kmpMatcher( pattern, !matchCase ),
     444                                        matchState = KMP_NOMATCH,
     445                                        character = '%';
     446
     447                                while ( character != null )
     448                                {
     449                                        this.range.moveNext();
     450                                        while ( ( character = this.range.getEndCharacter() ) )
     451                                        {
     452                                                matchState = matcher.feedCharacter( character );
     453                                                if ( matchState == KMP_MATCHED )
     454                                                        break;
     455                                                if ( this.range.moveNext().hitMatchBoundary )
     456                                                        matcher.reset();
     457                                        }
     458
     459                                        if ( matchState == KMP_MATCHED )
     460                                        {
     461                                                if ( matchWord )
     462                                                {
     463                                                        var cursors = this.range.getCursors(),
     464                                                                tail = cursors[ cursors.length - 1 ],
     465                                                                head = cursors[ 0 ],
     466                                                                headWalker = new characterWalker( head ),
     467                                                                tailWalker = new characterWalker( tail );
     468                                                               
     469                                                        if ( ! ( isWordSeparator(
     470                                                                                headWalker.back().character )
     471                                                                                && isWordSeparator(
     472                                                                                tailWalker.next().character ) ) )
     473                                                                continue;
     474                                                }
     475
     476                                                this.range.setMatched();
     477                                                return true;
     478                                        }
     479                                }
     480                               
     481                                this.range.clearMatched();
     482                               
     483                                // clear current session and restart from beginning
     484                                if ( matchCyclic )
     485                                        this.range = null;
     486                               
     487                                return false;
     488                        },
     489                       
     490                        /**
     491                         * Record how much replacement occurred toward one replacing.
     492                         */
     493                        replaceCounter : 0,
     494                       
     495                        replace : function( dialog, pattern, newString, matchCase, matchWord,
     496                                matchCyclic, matchReplaceAll )
     497                        {
     498                                var replaceResult = false;
     499                                if ( this.range && this.range.isMatched() )
     500                                {
     501                                        var domRange = this.range.toDomRange();
     502                                        domRange.extractContents();
     503                                        domRange.insertNode( editor.document.createText( newString ) );
     504                                        this.range.updateFromDomRange( domRange );
     505
     506                                        this.replaceCounter++;
     507                                        replaceResult = true;
     508                                }
     509                               
     510                                var findResult = this.find( pattern, matchCase, matchWord, matchCyclic );
     511                                if ( findResult && matchReplaceAll )
     512                                        this.replace.apply( this, Array.prototype.slice.call( arguments ) );
     513                                return matchReplaceAll ?
     514                                        this.replaceCounter : replaceResult || findResult;
     515                        }
     516                };
     517               
     518                /**
     519                 * Get the default cursor which is the start of this document.
     520                 */
     521                function getDefaultStartCursor()
     522                {
     523                        return { textNode : editor.document.getBody(), offset: 0 };
     524                }
     525               
     526                /**
     527                 * Get cursor that indicate search begin with, receive from user
     528                 * selection prior.
     529                 */
     530                function getStartCursor()
     531                {
     532                        if ( CKEDITOR.env.ie )
     533                                this.restoreSelection();
     534
     535                        var sel = editor.getSelection();
     536                        if ( sel )
     537                        {
     538                                var lastRange = sel.getRanges()[ sel.getRanges().length - 1 ];
     539                                return {
     540                                        textNode : lastRange.getBoundaryNodes().endNode,
     541                                        offset : lastRange.endContainer.type ===
     542                                                CKEDITOR.NODE_ELEMENT ?
     543                                                0       : lastRange.endOffset
     544                                };
     545                        }
     546                        else
     547                                return getDefaultStartCursor();
     548                }
     549               
     550                return {
     551                        title : editor.lang.findAndReplace.title,
     552                        resizable : CKEDITOR.DIALOG_RESIZE_NONE,
     553                        minWidth : 400,
     554                        minHeight : 255,
     555                        buttons : [ CKEDITOR.dialog.cancelButton ],             //Cancel button only.
     556                        contents : [
     557                                {
     558                                        id : 'find',
     559                                        label : editor.lang.findAndReplace.find,
     560                                        title : editor.lang.findAndReplace.find,
     561                                        accessKey : '',
     562                                        elements : [
     563                                                {
     564                                                        type : 'hbox',
     565                                                        widths : [ '230px', '90px' ],
     566                                                        children :
     567                                                        [
     568                                                                {
     569                                                                        type : 'text',
     570                                                                        id : 'txtFindFind',
     571                                                                        label : editor.lang.findAndReplace.findWhat,
     572                                                                        isChanged : false,
     573                                                                        labelLayout : 'horizontal',
     574                                                                        accessKey : 'F'
     575                                                                },
     576                                                                {
     577                                                                        type : 'button',
     578                                                                        align : 'left',
     579                                                                        style : 'width:100%',
     580                                                                        label : editor.lang.findAndReplace.find,
     581                                                                        onClick : function()
     582                                                                        {
     583                                                                                var dialog = this.getDialog();
     584                                                                                if ( !finder.find( dialog.getValueOf( 'find', 'txtFindFind' ),
     585                                                                                                        dialog.getValueOf( 'find', 'txtFindCaseChk' ),
     586                                                                                                        dialog.getValueOf( 'find', 'txtFindWordChk' ),
     587                                                                                                        dialog.getValueOf( 'find', 'txtFindCyclic' ) ) )
     588                                                                                        alert( editor.lang.findAndReplace
     589                                                                                                .notFoundMsg );
     590                                                                        }
     591                                                                }
     592                                                        ]
     593                                                },
     594                                                {
     595                                                        type : 'vbox',
     596                                                        padding : 0,
     597                                                        children :
     598                                                        [
     599                                                                {
     600                                                                        type : 'checkbox',
     601                                                                        id : 'txtFindCaseChk',
     602                                                                        isChanged : false,
     603                                                                        style : 'margin-top:28px',
     604                                                                        label : editor.lang.findAndReplace.matchCase
     605                                                                },
     606                                                                {
     607                                                                        type : 'checkbox',
     608                                                                        id : 'txtFindWordChk',
     609                                                                        isChanged : false,
     610                                                                        label : editor.lang.findAndReplace.matchWord
     611                                                                },
     612                                                                {
     613                                                                        type : 'checkbox',
     614                                                                        id : 'txtFindCyclic',
     615                                                                        isChanged : false,
     616                                                                        'default' : true,
     617                                                                        label : editor.lang.findAndReplace.matchCyclic
     618                                                                }
     619                                                        ]
     620                                                }
     621                                        ]
     622                                },
     623                                {
     624                                        id : 'replace',
     625                                        label : editor.lang.findAndReplace.replace,
     626                                        accessKey : 'M',
     627                                        elements : [
     628                                                {
     629                                                        type : 'hbox',
     630                                                        widths : [ '230px', '90px' ],
     631                                                        children :
     632                                                        [
     633                                                                {
     634                                                                        type : 'text',
     635                                                                        id : 'txtFindReplace',
     636                                                                        label : editor.lang.findAndReplace.findWhat,
     637                                                                        isChanged : false,
     638                                                                        labelLayout : 'horizontal',
     639                                                                        accessKey : 'F'
     640                                                                },
     641                                                                {
     642                                                                        type : 'button',
     643                                                                        align : 'left',
     644                                                                        style : 'width:100%',
     645                                                                        label : editor.lang.findAndReplace.replace,
     646                                                                        onClick : function()
     647                                                                        {
     648                                                                                var dialog = this.getDialog();
     649                                                                                if ( !finder.replace( dialog,
     650                                                                                                        dialog.getValueOf( 'replace', 'txtFindReplace' ),
     651                                                                                                        dialog.getValueOf( 'replace', 'txtReplace' ),
     652                                                                                                        dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ),
     653                                                                                                        dialog.getValueOf( 'replace', 'txtReplaceWordChk' ),
     654                                                                                                        dialog.getValueOf( 'replace', 'txtReplaceCyclic' ) ) )
     655                                                                                        alert( editor.lang.findAndReplace
     656                                                                                                .notFoundMsg );
     657                                                                        }
     658                                                                }
     659                                                        ]
     660                                                },
     661                                                {
     662                                                        type : 'hbox',
     663                                                        widths : [ '230px', '90px' ],
     664                                                        children :
     665                                                        [
     666                                                                {
     667                                                                        type : 'text',
     668                                                                        id : 'txtReplace',
     669                                                                        label : editor.lang.findAndReplace.replaceWith,
     670                                                                        isChanged : false,
     671                                                                        labelLayout : 'horizontal',
     672                                                                        accessKey : 'R'
     673                                                                },
     674                                                                {
     675                                                                        type : 'button',
     676                                                                        align : 'left',
     677                                                                        style : 'width:100%',
     678                                                                        label : editor.lang.findAndReplace.replaceAll,
     679                                                                        isChanged : false,
     680                                                                        onClick : function()
     681                                                                        {
     682                                                                                var dialog = this.getDialog();
     683                                                                                var replaceNums;
     684
     685                                                                                finder.replaceCounter = 0;
     686                                                                                if ( ( replaceNums = finder.replace( dialog,
     687                                                                                        dialog.getValueOf( 'replace', 'txtFindReplace' ),
     688                                                                                        dialog.getValueOf( 'replace', 'txtReplace' ),
     689                                                                                        dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ),
     690                                                                                        dialog.getValueOf( 'replace', 'txtReplaceWordChk' ),
     691                                                                                        dialog.getValueOf( 'replace', 'txtReplaceCyclic' ), true ) ) )
     692                                                                                        alert( editor.lang.findAndReplace.replaceSuccessMsg.replace( /%1/, replaceNums ) );
     693                                                                                else
     694                                                                                        alert( editor.lang.findAndReplace.notFoundMsg );
     695                                                                        }
     696                                                                }
     697                                                        ]
     698                                                },
     699                                                {
     700                                                        type : 'vbox',
     701                                                        padding : 0,
     702                                                        children :
     703                                                        [
     704                                                                {
     705                                                                        type : 'checkbox',
     706                                                                        id : 'txtReplaceCaseChk',
     707                                                                        isChanged : false,
     708                                                                        label : editor.lang.findAndReplace
     709                                                                                .matchCase
     710                                                                },
     711                                                                {
     712                                                                        type : 'checkbox',
     713                                                                        id : 'txtReplaceWordChk',
     714                                                                        isChanged : false,
     715                                                                        label : editor.lang.findAndReplace
     716                                                                                .matchWord
     717                                                                },
     718                                                                {
     719                                                                        type : 'checkbox',
     720                                                                        id : 'txtReplaceCyclic',
     721                                                                        isChanged : false,
     722                                                                        'default' : true,
     723                                                                        label : editor.lang.findAndReplace
     724                                                                                .matchCyclic
     725                                                                }
     726                                                        ]
     727                                                }
     728                                        ]
     729                                }
     730                        ],
     731                        onLoad : function()
     732                        {
     733                                var dialog = this;
     734
     735                                //keep track of the current pattern field in use.
     736                                var patternField, wholeWordChkField;
     737                               
     738                                /**
     739                                 * Check the whether the 'wholeworld' match mode could be
     740                                 * toggled on.
     741                                 */
     742                                function checkWholeWordState()
     743                                {
     744                                        var isWord = /^\w+$/.test( patternField.getValue() );
     745                                        var wholeWordChkFieldEl = wholeWordChkField.getElement();
     746                                        if ( isWord )
     747                                                wholeWordChkFieldEl.show();
     748                                        else
     749                                                wholeWordChkFieldEl.hide();
     750                                }
     751                               
     752                                //Ignore initial page select on dialog show
     753                                var isUserSelect = false;
     754                                this.on('hide', function()
     755                                                {
     756                                                        isUserSelect = false;
     757                                                } );
     758                                this.on('show', function()
     759                                                {
     760                                                        isUserSelect = true;
     761                                                } );
     762                               
     763                                this.selectPage = CKEDITOR.tools.override( this.selectPage, function( originalFunc )
     764                                        {
     765                                                return function( pageId )
     766                                                {
     767                                                        originalFunc.call( dialog, pageId );
     768                                                       
     769                                                        var currPage = dialog._.tabs[ pageId ];
     770                                                        var patternFieldInput, patternFieldId, wholeWordChkFieldId;
     771                                                        patternFieldId = pageId === 'find' ? 'txtFindFind' : 'txtFindReplace';
     772                                                        wholeWordChkFieldId = pageId === 'find' ? 'txtFindWordChk' : 'txtReplaceWordChk';
     773
     774                                                        patternField = dialog.getContentElement( pageId,
     775                                                                patternFieldId );
     776                                                        wholeWordChkField = dialog.getContentElement( pageId,
     777                                                                wholeWordChkFieldId );
     778                                                       
     779                                                        // prepare for check pattern text filed 'keyup' event
     780                                                        if ( !currPage.initialized )
     781                                                        {
     782                                                                patternFieldInput = CKEDITOR.document
     783                                                                        .getById( patternField._.inputId );
     784                                                                patternFieldInput.on( 'keyup', checkWholeWordState );
     785                                                                currPage.initialized = true;
     786                                                        }
     787                                                       
     788                                                        if( isUserSelect )
     789                                                                // synchronize fields on tab switch.
     790                                                                syncFieldsBetweenTabs.call( this, pageId );
     791                                                       
     792                                                        // Check wholeword match status on tab switch
     793                                                        checkWholeWordState();
     794                                                };
     795                                        } );
     796
     797                        },
     798                        onShow : function()
     799                        {
     800                                // Establish initial searching start position.
     801                                finder.startCursor = getStartCursor.call( this );
     802                               
     803                                if ( startupPage == 'replace' )
     804                                        this.getContentElement( 'replace', 'txtFindReplace' ).focus();
     805                                else
     806                                        this.getContentElement( 'find', 'txtFindFind' ).focus();
     807                        },
     808                        onHide : function()
     809                        {
     810                                if ( finder.range && finder.range.isMatched() )
     811                                {
     812                                        finder.range.removeHighlight();
     813                                        editor.getSelection().selectRanges(
     814                                                [ finder.range.toDomRange() ] );
     815                                }
     816
     817                                // Clear current session before dialog close
     818                                delete finder.range;
     819                        }
     820                };
     821        };
     822
     823        CKEDITOR.dialog.add( 'find', function( editor ){
     824                        return findDialog( editor, 'find' )
     825                }
     826        );
     827
     828        CKEDITOR.dialog.add( 'replace', function( editor ){
     829                        return findDialog( editor, 'replace' )
     830                }
     831        );
     832})();
  • _source/plugins/find/plugin.js

    Property changes on: _source/plugins/find/dialogs/find.js
    ___________________________________________________________________
    Added: svn:executable
       + *
    
     
     1/*
     2Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
     3For licensing, see LICENSE.html or http://ckeditor.com/license
     4*/
     5
     6CKEDITOR.plugins.add( 'find',
     7{
     8        init : function( editor )
     9        {
     10                var forms = CKEDITOR.plugins.find;
     11                editor.ui.addButton( 'Find',
     12                        {
     13                                label : editor.lang.findAndReplace.find,
     14                                command : 'find'
     15                        });
     16                editor.addCommand( 'find', new CKEDITOR.dialogCommand( 'find' ) );
     17
     18                editor.ui.addButton( 'Replace',
     19                        {
     20                                label : editor.lang.findAndReplace.replace,
     21                                command : 'replace'
     22                        });
     23                editor.addCommand( 'replace', new CKEDITOR.dialogCommand( 'replace' ) );
     24
     25                CKEDITOR.dialog.add( 'find',    this.path + 'dialogs/find.js' );
     26                CKEDITOR.dialog.add( 'replace', this.path + 'dialogs/find.js' );
     27        },
     28
     29        requires : [ 'styles' ]
     30} );
     31
     32// Styles for highlighting search results.
     33CKEDITOR.config.find_highlight = { element : 'span', styles : { 'background-color' : '#004', 'color' : '#fff' } };
  • _source/plugins/toolbar/plugin.js

    Property changes on: _source/plugins/find/plugin.js
    ___________________________________________________________________
    Added: svn:executable
       + *
    
     
    210210                'NewPage', 'Preview', 'Templates', 'Print', '-',
    211211                'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-',
    212212                'Undo', 'Redo', '-',
     213                'Find', 'Replace', '-',
    213214                'Bold', 'Italic', 'Underline', 'Strike', '-',
    214215                'NumberedList', 'BulletedList', '-',
    215216                'Outdent', 'Indent', 'Blockquote', '-',
  • _source/core/config.js

     
    147147         * config.plugins = 'basicstyles,button,htmldataprocessor,toolbar,wysiwygarea';
    148148         */
    149149
    150         plugins : 'basicstyles,blockquote,button,clipboard,elementspath,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,newpage,pagebreak,pastefromword,pastetext,preview,print,removeformat,smiley,sourcearea,table,specialchar,tab,templates,toolbar,undo,wysiwygarea',
     150        plugins : 'basicstyles,blockquote,button,clipboard,elementspath,find,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,newpage,pagebreak,pastefromword,pastetext,preview,print,removeformat,smiley,sourcearea,table,specialchar,tab,templates,toolbar,undo,wysiwygarea',
    151151
    152152        /**
    153153         * The theme to be used to build the UI.
  • _source/core/dom/element.js

     
    973973                                                        }
    974974                                                }
    975975                                        }
     976                                },
     977
     978                getPositionedAncestor : function()
     979                {
     980                        var current = this;
     981                        while ( current.getName() != 'html' )
     982                        {
     983                                if ( current.getComputedStyle( 'position' ) != 'static' )
     984                                        return current;
     985
     986                                current = current.getParent();
     987                        }
     988                        return null;
     989                },
     990
     991                getDocumentPosition : function()
     992                {
     993                        var x = 0, y = 0, current = this, previous = null;
     994                        while ( current && !( current.getName() == 'body' || current.getName() == 'html' ) )
     995                        {
     996                                x += current.$.offsetLeft - current.$.scrollLeft;
     997                                y += current.$.offsetTop - current.$.scrollTop;
     998
     999                                if ( !CKEDITOR.env.opera )
     1000                                {
     1001                                        var scrollElement = previous;
     1002                                        while ( scrollElement && !scrollElement.equals( current ) )
     1003                                        {
     1004                                                x -= scrollElement.$.scrollLeft;
     1005                                                y -= scrollElement.$.scrollTop;
     1006                                                scrollElement = scrollElement.getParent();
     1007                                        }
    9761008                                }
     1009
     1010                                previous = current;
     1011                                current = new CKEDITOR.dom.element( current.$.offsetParent );
     1012                        }
     1013
     1014                        // document.body is a special case when it comes to offsetTop and offsetLeft
     1015                        // values.
     1016                        // 1. It matters if document.body itself is a positioned element;
     1017                        // 2. It matters when we're in IE and the element has no positioned ancestor.
     1018                        // Otherwise the values should be ignored.
     1019                        if ( this.getComputedStyle( 'position' ) != 'static' || ( CKEDITOR.env.ie && this.getPositionedAncestor() == null ) )
     1020                        {
     1021                                x += this.getDocument().getBody().$.offsetLeft;
     1022                                y += this.getDocument().getBody().$.offsetTop;
     1023                        }
     1024
     1025                        return { x : x, y : y };
     1026                },
     1027
     1028                scrollIntoView : function( alignTop )
     1029                {
     1030                        // Get the element window.
     1031                        var win = this.getWindow(),
     1032                                winHeight = win.getViewPaneSize().height;
     1033
     1034                        // Starts from the offset that will be scrolled with the negative value of
     1035                        // the visible window height.
     1036                        var offset = winHeight * -1;
     1037
     1038                        // Append the height if we are about the align the bottom.
     1039                        if ( !alignTop )
     1040                        {
     1041                                offset += this.$.offsetHeight || 0;
     1042
     1043                                // Consider the margin in the scroll, which is ok for our current needs, but
     1044                                // needs investigation if we will be using this function in other places.
     1045                                offset += parseInt( this.getComputedStyle( 'marginBottom' ) || 0, 10 ) || 0;
     1046                        }
     1047
     1048                        // Append the offsets for the entire element hierarchy.
     1049                        var elementPosition = this.getDocumentPosition();
     1050                        offset += elementPosition.y;
     1051
     1052                        // Scroll the window to the desired position, if not already visible.
     1053                        var currentScroll = win.getScrollPosition().y;
     1054                        if ( offset > 0 && ( offset > currentScroll || offset < currentScroll - winHeight ) )
     1055                                win.$.scrollTo( 0, offset );
     1056                }
    9771057        });
  • _source/core/dom/range.js

     
    466466
    467467                                        // In this case, move the start information to that text
    468468                                        // node.
    469                                         if ( child && child.type == CKEDITOR.NODE_TEXT && child.getPrevious().type == CKEDITOR.NODE_TEXT )
     469                                        if ( child && child.type == CKEDITOR.NODE_TEXT
     470                                                        && startOffset > 0 && child.getPrevious().type == CKEDITOR.NODE_TEXT )
    470471                                        {
    471472                                                startContainer = child;
    472473                                                startOffset = 0;
     
    493494
    494495                                                // In this case, move the start information to that
    495496                                                // text node.
    496                                                 if ( child && child.type == CKEDITOR.NODE_TEXT && child.getPrevious().type == CKEDITOR.NODE_TEXT )
     497                                                if ( child && child.type == CKEDITOR.NODE_TEXT
     498                                                                && endOffset > 0 && child.getPrevious().type == CKEDITOR.NODE_TEXT )
    497499                                                {
    498500                                                        endContainer = child;
    499501                                                        endOffset = 0;
  • _source/core/dom/document.js

     
    7070
    7171                createText : function( text )
    7272                {
    73                         return new CKEDITOR.dom.text( '', this );
     73                        return new CKEDITOR.dom.text( text, this );
    7474                },
    7575
    7676                /**
© 2003 – 2022, CKSource sp. z o.o. sp.k. All rights reserved. | Terms of use | Privacy policy